C# DataGridView:将多行合并为一但保留原件 [英] C# DataGridView: combine multiple rows to one but keep originals

查看:21
本文介绍了C# DataGridView:将多行合并为一但保留原件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先:我将在完成后立即回答这个问题.
但是你可以在我实现它的过程中帮助我,我会感谢你的所有建议.

我有一个 DataGridView,其中不同的行属于一起.我的问题是找到一种合适的方式来显示和处理这些连接的行.

我的第一个想法是保持每一行独立,但这有一些缺点:

  • 如何清楚地表明行属于一起?
    我可以添加另一列,其单元格显示的连接行数相同,但这不容易看到,需要另一列.
    我的解决方案是所有连接的行都具有相同的背景颜色,并且颜色会发生变化,例如每组连接行的白色和浅灰色之间.

  • 如何处理连接的行?一旦我选择了一组中的一行,我就必须通过提取信息(保存在单元格的标签或隐藏单元格中)来分析这一行,哪些行属于一起并选择它们.在 DataGridView 中向上/向下移动行的更大工作:我还必须分析相邻的行集以查看我必须移动多远.

因此我决定创建一个DataGridViewMultiRow.
完成后,我将在此处发布该课程的完整代码作为答案.

它将继承DataGridViewRow(DGVR")并存储单个DGVR或其他多行的列表,并通过自己的代码绘制行的单元格将它们显示为一个.但是,我仍然需要找出为此目的使用哪些事件.

我仍然需要修复细节,例如移动滚动条时正确放置文本和其他小东西.

我决定不覆盖 DataGridViewRow.Paint() 因为它有太多的内部连接.所以我首先尝试使用 DGV 的 CellPainting 事件,这是一个好的开始.但是我需要同时获得该行所有单元格的信息,所以我继续按照 MSDN 的建议覆盖 DataGridView.RowPrePaint(),请参见上面的链接.这真的很好用.

待续.

解决方案

经过各种弊端,我终于创建了一个解决方案.
它是用 C++/CLI 编写的,所以你们中的大多数人都必须将它改编为在 C# 中使用.
此解决方案包含一些用户功能,这些功能不属于解决方案的一部分,但其用途应该很容易通过名称猜出.

预览:

#pragma once使用命名空间系统;使用命名空间 System::Collections::Generic;使用命名空间 System::Drawing;使用命名空间 System::Windows::Forms;使用命名空间 System::ComponentModel;公共引用类 CDataGridViewMultiRow : DataGridViewRow{上市://-----------------------------------------------------------------------------//构造函数//-----------------------------------------------------------------------------CDataGridViewMultiRow();CDataGridViewMultiRow (bool i_bHideRows);CDataGridViewMultiRow (bool i_bHideRows, ::DataGridView^ i_dgv);//-----------------------------------------------------------------------------//克隆//-----------------------------------------------------------------------------虚拟对象^克隆()覆盖;//-----------------------------------------------------------------------------//清除//-----------------------------------------------------------------------------空清除();//-----------------------------------------------------------------------------//添加,插入//-----------------------------------------------------------------------------布尔添加(DataGridViewRow^ i_dgvr);布尔插入(int i_ixRow,DataGridViewRow^ i_dgvr);//-----------------------------------------------------------------------------//消除//-----------------------------------------------------------------------------布尔删除(int i_ixRow);布尔删除(DataGridViewRow^ i_dgvr);//-----------------------------------------------------------------------------//更新//-----------------------------------------------------------------------------无效更新();//-----------------------------------------------------------------------------//画线////描述:手动绘制行.////!!!重要通知: !!!//此方法必须附加到 DataGridView 的 RowPrePaint 事件.//-----------------------------------------------------------------------------static void PaintRow (Object^ sender, DataGridViewRowPrePaintEventArgs^ e);//-----------------------------------------------------------------------------//特性//-----------------------------------------------------------------------------属性 DataGridViewRow^ Rows[int] { DataGridViewRow^ get (int i_ixRow);无效集(int i_ixRow,DataGridViewRow^ i_dgvr);}属性 int RowCount { int get() { return m_listdgvr->Count;} }属性 bool HideRows { bool get() { return m_bHideRows;}无效集(布尔 i_bHideRows);}上市:受保护:List^ m_listdgvr;bool m_bHideRows;私人的:受保护:virtual void OnDataGridViewChanged() 覆盖;私人的:void CommonConstructor (bool i_bHideRows,::DataGridView^ i_dgv);};#include "CDataGridViewMultiRow.h"使用命名空间 Schmoll_SwCore;//-----------------------------------------------------------------------------//构造函数//-----------------------------------------------------------------------------CDataGridViewMultiRow::CDataGridViewMultiRow() : DataGridViewRow(){CommonConstructor (false, nullptr);}//-----------------------------------------------------------------------------CDataGridViewMultiRow::CDataGridViewMultiRow (bool i_bHideRows) : DataGridViewRow(){CommonConstructor (i_bHideRows, nullptr);}//-----------------------------------------------------------------------------CDataGridViewMultiRow::CDataGridViewMultiRow (bool i_bHideRows, ::DataGridView^ i_dgv) : DataGridViewRow(){CommonConstructor (i_bHideRows, i_dgv);}//-----------------------------------------------------------------------------//属性:行//-----------------------------------------------------------------------------DataGridViewRow^ CDataGridViewMultiRow::Rows::get (int i_ixRow){if (i_ixRow <0 || i_ixRow >= m_listdgvr->Count)返回 nullptr;返回 m_listdgvr[i_ixRow];}//-----------------------------------------------------------------------------void CDataGridViewMultiRow::Rows::set (int i_ixRow, DataGridViewRow^ i_dgvr){如果(!i_dgvr)返回;if (i_ixRow <0 || i_ixRow >= m_listdgvr->Count)返回;int ixDgvr = -1;DataGridViewRow^ dgvr = m_listdgvr[i_ixRow];如果 (dgvr->DataGridView&&dgvr->DataGridView == this->DataGridView){ixDgvr = dgvr->索引;dgvr->DataGridView->Rows->Remove (dgvr);}m_listdgvr[i_ixRow] = i_dgvr;if (this->DataGridView){如果 (ixDgvr <0)ixDgvr = this->DataGridView->Rows->IndexOf(this) + 1 + i_ixRow;this->DataGridView->Rows->Insert (ixDgvr, i_dgvr);i_dgvr-> 可见 = !m_bHideRows;}更新();}//-----------------------------------------------------------------------------//属性:HideRows//-----------------------------------------------------------------------------void CDataGridViewMultiRow::HideRows::set (bool i_bHideRows){m_bHideRows = i_bHideRows;for (int ixRow = 0; ixRow Count; ixRow++)m_listdgvr[ixRow]->Visible = !m_bHideRows;}//-----------------------------------------------------------------------------//克隆//-----------------------------------------------------------------------------对象^ CDataGridViewMultiRow::Clone(){CDataGridViewMultiRow^ dgvr = (CDataGridViewMultiRow^)DataGridViewRow::Clone();如果 (dgvr){dgvr->m_bHideRows = this->m_bHideRows;dgvr->m_listdgvr->Clear();dgvr->m_listdgvr->AddRange(this->m_listdgvr);}返回 dgvr;}//-----------------------------------------------------------------------------//清除//-----------------------------------------------------------------------------void CDataGridViewMultiRow::Clear (){for (int ixRow = 0; ixRow Count; ixRow++){if (m_listdgvr[ixRow]->DataGridView&&m_listdgvr[ixRow]->DataGridView == this->DataGridView)m_listdgvr[ixRow]->DataGridView->Rows->Remove (m_listdgvr[ixRow]);m_listdgvr[ixRow]->Visible = true;}m_listdgvr->清除();更新();}//-----------------------------------------------------------------------------//添加//-----------------------------------------------------------------------------bool CDataGridViewMultiRow::Add (DataGridViewRow^ i_dgvr){返回插入(m_listdgvr->计数,i_dgvr);}//-----------------------------------------------------------------------------//插入//-----------------------------------------------------------------------------bool CDataGridViewMultiRow::Insert (int i_ixRow, DataGridViewRow^ i_dgvr){如果(!i_dgvr)返回假;如果(i_dgvr->索引<0)返回假;//阻止共享行和不属于 DGV 的行如果 (i_ixRow <0)返回假;else if (i_ixRow > m_listdgvr->Count)i_ixRow = m_listdgvr->Count;m_listdgvr->插入(i_ixRow, i_dgvr);如果 (i_dgvr->DataGridView&&i_dgvr->DataGridView != this->DataGridView)i_dgvr->DataGridView->Rows->Remove (i_dgvr);if (this->DataGridView){int ixDgvr = this->DataGridView->Rows->IndexOf(this) + 1 + i_ixRow;if (i_dgvr->DataGridView == this->DataGridView&&i_dgvr-> 索引 != ixDgvr)i_dgvr->DataGridView->Rows->Remove (i_dgvr);ixDgvr = this->DataGridView->Rows->IndexOf(this) + 1 + i_ixRow;if (i_dgvr->DataGridView != this->DataGridView)this->DataGridView->Rows->Insert (ixDgvr, i_dgvr);}i_dgvr-> 可见 = !m_bHideRows;更新();返回真;}//-----------------------------------------------------------------------------//消除//-----------------------------------------------------------------------------bool CDataGridViewMultiRow::Remove (int i_ixRow){返回删除(行[i_ixRow]);}//-----------------------------------------------------------------------------//消除//-----------------------------------------------------------------------------bool CDataGridViewMultiRow::Remove (DataGridViewRow^ i_dgvr){bool bResult = m_listdgvr->Remove (i_dgvr);如果(i_dgvr){如果 (i_dgvr->DataGridView&&i_dgvr->DataGridView == this->DataGridView)i_dgvr->DataGridView->Rows->Remove (i_dgvr);i_dgvr->Visible = true;}更新();返回 bResult;}//-----------------------------------------------------------------------------//更新//-----------------------------------------------------------------------------void CDataGridViewMultiRow::Update(){if (!this->DataGridView)返回;if (this->Index <0)throw gcnew InvalidOperationException ("Index is <0.如果该行是由 CreateCells() 创建,然后添加到 DGV,这使得以前共享的行变为非共享,然后被同一无效对象访问,则可能会发生这种情况.获取来自 DGV 的更新行对象.");array^ aiNewLines = gcnew array(m_listdgvr->Count);array^ a2sValue = gcnew array(this->Cells->Count, m_listdgvr->Count);for (int ixCell = 0; ixCell Count; ixCell++){for (int ixRow = 0; ixRow Count; ixRow++){如果(m_listdgvr[ixRow]->索引<0)继续;对象^ oValue = m_listdgvr[ixRow]->Cells[ixCell]->Value;如果 (oValue){a2sValue[ixCell, ixRow] = oValue->ToString();int iNewLines = CString::Count (a2sValue[ixCell, ixRow], CONSTS::CRLF, StringComparison::InvariantCultureIgnoreCase);aiNewLines[ixRow] = Math::Max (aiNewLines[ixRow], iNewLines);}}}for (int ixCell = 0; ixCell Count; ixCell++){字符串^ sText = nullptr;for (int ixRow = 0; ixRow Count; ixRow++){如果 (ixRow > 0)sText += CONSTS::CRLF;sText += a2sValue[ixCell, ixRow];int iNewLines = CString::Count (a2sValue[ixCell, ixRow], CONSTS::CRLF, StringComparison::InvariantCultureIgnoreCase);sText += CString::Repeat (CONSTS::CRLF, aiNewLines[ixRow] - iNewLines);}this->Cells[ixCell]->Value = sText;}}//-----------------------------------------------------------------------------//OnDataGridViewChanged//-----------------------------------------------------------------------------void CDataGridViewMultiRow::OnDataGridViewChanged (){尝试{if (this->DataGridView){int ixDgvr = this->DataGridView->Rows->IndexOf(this) + 1;for (int ixCnt = 0; ixCnt Count; ixCnt++)DataGridView->行->插入(ixDgvr + ixCnt, m_listdgvr[ixCnt]);}别的{for (int ixCnt = 0; ixCnt Count; ixCnt++)m_listdgvr[ixCnt]->DataGridView->Rows->Remove (m_listdgvr[ixCnt]);}}最后{DataGridViewRow::OnDataGridViewChanged();}}//-----------------------------------------------------------------------------//画线//-----------------------------------------------------------------------------void CDataGridViewMultiRow::PaintRow (Object^ sender, DataGridViewRowPrePaintEventArgs^ e){::DataGridView^ dgv = dynamic_cast<::DataGridView^>(sender);如果(!dgv)返回;if (e->RowIndex < 0 || e->RowIndex >= dgv->RowCount)返回;CDataGridViewMultiRow^ dgvmr = dynamic_cast(dgv->Rows->SharedRow(e->RowIndex));如果(!dgvmr)返回;如果 (dgvmr->DataGridView != dgv)返回;bool bAutoHeight = dgv->AutoSizeRowsMo​​de == DataGridViewAutoSizeRowsMo​​de::AllCells||dgv->AutoSizeRowsMo​​de == DataGridViewAutoSizeRowsMo​​de::AllCellsExceptHeaders||dgv->AutoSizeRowsMo​​de == DataGridViewAutoSizeRowsMo​​de::DisplayedCells||dgv->AutoSizeRowsMo​​de == DataGridViewAutoSizeRowsMo​​de::DisplayedCellsExceptHeaders;图形^ g = e->图形;StringFormatFlags enFlags = (StringFormatFlags)0;if (dgvmr->InheritedStyle->WrapMode != DataGridViewTriState::True)enFlags = enFlags |StringFormatFlags::NoWrap;StringFormat^ oStringFormat = gcnew StringFormat(enFlags);array^ afRowHeight = gcnew array(dgvmr->RowCount);array^ aiLines = gcnew array(dgvmr->RowCount);array^ a2iLines = gcnew array(dgvmr->Cells->Count, dgvmr->RowCount);array^ a2sValue = gcnew array(dgvmr->Cells->Count, dgvmr->RowCount);for (int ixRow = 0; ixRow RowCount; ixRow++){DataGridViewRow^ dgvr = dgvmr->Rows[ixRow];for (int ixCell = 0; ixCell Cells->Count; ixCell++){如果 (dgvr-> 索引 <0)继续;Object^oValue = dgvr->Cells[ixCell]->Value;如果 (!oValue)继续;a2sValue[ixCell, ixRow] = oValue->ToString();int iCharacters = 0, iLines = 0;SizeF oLayoutArea ((float)dgvmr->Cells[ixCell]->Size.Width, 0);SizeF oTextSize = g->MeasureString (a2sValue[ixCell, ixRow],dgvmr->Cells[ixCell]->InheritedStyle->字体,o布局区域,oStringFormat,iCharacters,线);浮动 fHeight = oTextSize.Height;如果 (!bAutoHeight)f 高度 += 4;afRowHeight[ixRow] = Math::Max (afRowHeight[ixRow], fHeight);a2iLines[ixCell, ixRow] = iLines;aiLines[ixRow] = Math::Max (aiLines[ixRow], iLines);}}int iLength = dgv->Columns->GetColumnsWidth(DataGridViewElementStates::Visible);int iHeight = (int)Math::Ceiling(CMath::Sum (afRowHeight));dgvmr->高度 = iHeight;e->PaintCellsBackground(e->ClipBounds,真);int iPositionX = e->RowBounds.X + dgvmr->HeaderCell->Size.Width - dgv->Horizo​​ntalScrollingOffset;int iPositionY = 0;for (int ixCell = 0; ixCell Cells->Count; ixCell++){字符串^ sText = nullptr;DataGridViewCell^oCell = dgvmr->Cells[ixCell];颜色 oTextColor = oCell->Selected ?oCell->InheritedStyle->SelectionForeColor:oCell->InheritedStyle->ForeColor;绘图::画笔^ oBrush = gcnew 绘图::SolidBrush (oTextColor);iPositionY = e->RowBounds.Y;如果 (!bAutoHeight)iPositionY += 2;for (int ixRow = 0; ixRow RowCount; ixRow++){如果 (ixRow > 0)sText += CONSTS::CRLF;sText += a2sValue[ixCell, ixRow];sText += CString::Repeat (CONSTS::CRLF, aiLines[ixRow] - a2iLines[ixCell, ixRow]);矩形 oRectText (iPositionX, iPositionY, oCell->Size.Width, oCell->Size.Height);g->DrawString (a2sValue[ixCell, ixRow], oCell->InheritedStyle->Font, oBrush, oRectText, oStringFormat);iPositionY += (int)afRowHeight[ixRow];}dgvmr->Cells[ixCell]->Value = sText;iPositionX += oCell->Size.Width;}颜色 oLineColor = dgvmr->Selected ?dgvmr->InheritedStyle->SelectionForeColor : dgvmr->InheritedStyle->ForeColor;Pen^ oPen = gcnew Pen(oLineColor, 1);oPen->DashPattern = gcnew array{5, 15};iPositionX = e->RowBounds.X + dgvmr->HeaderCell->Size.Width - dgv->Horizo​​ntalScrollingOffset;iPositionY = e->RowBounds.Y;for (int ixRow = 0; ixRow RowCount - 1; ixRow++){iPositionY += (int)afRowHeight[ixRow];g->DrawLine (oPen, iPositionX, iPositionY,iPositionX + iLength, iPositionY);}e-> PaintHeader (true);e->已处理=真;}//-----------------------------------------------------------------------------//通用构造函数//-----------------------------------------------------------------------------void CDataGridViewMultiRow::CommonConstructor ( bool i_bHideRows,::DataGridView^ i_dgv){m_bHideRows = i_bHideRows;如果(i_dgv)this->CreateCells(i_dgv);m_listdgvr = gcnew List;this->ReadOnly = true;}

First of all: I am going to provide an answer to this question myself, as soon as I'm done.
But you may help me on my way to it, I will appreciate all of your advices.

I have a DataGridView with different rows that belong together. My problem is to find a suitable way for displaying and working with these connected rows.

My first idea was keeping each row individual, but that has some disadvantages:

  • how to clearly show that rows belong together?
    I could add another column whose cells show the same number for connected rows, but that's not easily visible and requires another column.
    My solution here would have been that all connected rows have the same background colour, and the colour changes e.g. between white and light grey for each set of connected rows.

  • how to work with connected rows? As soon as I select one row of a set, I would have to analyse this row by extracting information (saved in a cell's tag or in a hidden cell) which rows belong together and select them as well. Even bigger work for moving rows up/down in a DataGridView: I would have to analyse the neighbouring row sets as well to see how far I have to move.

Therefore I decided to create a DataGridViewMultiRow.
I will post the full code of that class here as an answer when finished.

It will inherit from DataGridViewRow ("DGVR") and store a list of single DGVR or of other multi-rows and display them as one by drawing the cells of the row by own code. However, I still need to find out which events to use for that purpose. MSDN suggests to use DataGridView.RowPrePaint, but I rather want to use an event that is bound to the DGVR itself. Maybe I will analyse the source code of DataGridViewRow.Paint() and write my own method...

The single rows will be made invisible when being added to a multi-row (they could be switched to visible again by abusing the concept, but there's a lot in .net itself that is not protected against abuse; maybe I don't even switch to invisible, so it's in the user's responsibility).
Recursion in the multi-rows will simply be avoided by forcing each DGVR to be part of the same DGV as the multi-row, and because each row can be added to only one DGV and only once, I don't have to check for recursion any more.

Now I am struggling how to implement the internal list of rows. I was thinking about using the .net DataGridViewRowCollection, but I found that it's operation is tightly bound to the DataGridView itself: a DGV can have only one DGVRColl, but each DGVRColl refers to a DGV. So there would be half-connected DGVRColl in each of my DGVMultiRow.
I was going to ask whether this will this cause issues, but I already found that I must provide a DGV when instantiating the DGVRColl, which I don't have at that moment when the DGVMultiRow ctor is called. Furthermore, when using a DGVRColl and providing a public get property to it, I can only hook to the 'CollectionChanged' event and have no control over the individual operations like Add() and Remove(). So I will use a simple private list.

Continuation #1
I got the main work done, it looks pretty good already:

I still need to get details fixed, like placing the text correctly when the scrollbar is moved and other small things.

I decided not to override the DataGridViewRow.Paint() because that one has too many internal connections. So I first played with using the CellPainting event of the DGV, which was a good start. But I needed to have infos of all cells of the row at the same time, so I went forward to overriding the DataGridView.RowPrePaint() as suggested by MSDN, see link above. This works really well.

To be continued.

解决方案

After various drawbacks, I have finally created a solution.
It is written in C++/CLI, so most of you will have to adapt it for usage in C#.
This solution contains some user functions that are not part of the solution, but whose purpose should be easy to guess by their names.

Here's a preview:

#pragma once

using namespace System;
using namespace System::Collections::Generic;
using namespace System::Drawing;
using namespace System::Windows::Forms;
using namespace System::ComponentModel;

public ref class CDataGridViewMultiRow : DataGridViewRow
{
public:
  //----------------------------------------------------------------------------
  // constructor
  //----------------------------------------------------------------------------
  CDataGridViewMultiRow ();
  CDataGridViewMultiRow (bool i_bHideRows);
  CDataGridViewMultiRow (bool i_bHideRows, ::DataGridView^ i_dgv);

  //----------------------------------------------------------------------------
  // Clone
  //----------------------------------------------------------------------------
  virtual Object^ Clone () override;

  //----------------------------------------------------------------------------
  // Clear
  //----------------------------------------------------------------------------
  void Clear ();

  //----------------------------------------------------------------------------
  // Add, Insert
  //----------------------------------------------------------------------------
  bool Add (DataGridViewRow^ i_dgvr);
  bool Insert (int i_ixRow, DataGridViewRow^ i_dgvr);

  //----------------------------------------------------------------------------
  // Remove
  //----------------------------------------------------------------------------
  bool Remove (int i_ixRow);
  bool Remove (DataGridViewRow^ i_dgvr);

  //----------------------------------------------------------------------------
  // Update
  //----------------------------------------------------------------------------
  void Update ();

  //----------------------------------------------------------------------------
  // PaintRow
  //
  // description: manually paints the row.
  //
  // !!! IMPORTANT NOTICE: !!!
  // This method must be attached to the DataGridView's RowPrePaint event.
  //----------------------------------------------------------------------------
  static void PaintRow (Object^ sender, DataGridViewRowPrePaintEventArgs^ e);

  //----------------------------------------------------------------------------
  // properties
  //----------------------------------------------------------------------------
  property DataGridViewRow^   Rows[int] { DataGridViewRow^  get (int i_ixRow);
                                          void              set (int i_ixRow, DataGridViewRow^ i_dgvr); }
  property int                RowCount  { int   get() { return m_listdgvr->Count;} }
  property bool               HideRows  { bool  get() { return m_bHideRows;}
                                          void  set(bool i_bHideRows); }

public:

protected:
  List<DataGridViewRow^>^     m_listdgvr;
  bool                        m_bHideRows;

private:

protected:
  virtual void OnDataGridViewChanged () override;

private:
  void CommonConstructor (bool            i_bHideRows,
                          ::DataGridView^ i_dgv);
};

#include "CDataGridViewMultiRow.h"

using namespace Schmoll_SwCore;

//----------------------------------------------------------------------------
// constructor
//----------------------------------------------------------------------------
CDataGridViewMultiRow::CDataGridViewMultiRow () : DataGridViewRow ()
{
  CommonConstructor (false, nullptr);
}

//----------------------------------------------------------------------------
CDataGridViewMultiRow::CDataGridViewMultiRow (bool i_bHideRows) : DataGridViewRow ()
{
  CommonConstructor (i_bHideRows, nullptr);
}

//----------------------------------------------------------------------------
CDataGridViewMultiRow::CDataGridViewMultiRow (bool i_bHideRows, ::DataGridView^ i_dgv) : DataGridViewRow ()
{
  CommonConstructor (i_bHideRows, i_dgv);
}

//----------------------------------------------------------------------------
// property: Rows
//----------------------------------------------------------------------------
DataGridViewRow^ CDataGridViewMultiRow::Rows::get (int i_ixRow)
{
  if (i_ixRow < 0 || i_ixRow >= m_listdgvr->Count)
    return nullptr;

  return m_listdgvr[i_ixRow];
}

//----------------------------------------------------------------------------
void CDataGridViewMultiRow::Rows::set (int i_ixRow, DataGridViewRow^ i_dgvr)
{
  if (!i_dgvr)
    return;
  if (i_ixRow < 0 || i_ixRow >= m_listdgvr->Count)
    return;

  int ixDgvr = -1;
  DataGridViewRow^ dgvr = m_listdgvr[i_ixRow];
  if (dgvr->DataGridView
  &&  dgvr->DataGridView == this->DataGridView)
  {
    ixDgvr = dgvr->Index;
    dgvr->DataGridView->Rows->Remove (dgvr);
  }

  m_listdgvr[i_ixRow] = i_dgvr;
  if (this->DataGridView)
  {
    if (ixDgvr < 0)
      ixDgvr = this->DataGridView->Rows->IndexOf (this) + 1 + i_ixRow;
    this->DataGridView->Rows->Insert (ixDgvr, i_dgvr);
    i_dgvr->Visible = !m_bHideRows;
  }

  Update();
}

//----------------------------------------------------------------------------
// property: HideRows
//----------------------------------------------------------------------------
void CDataGridViewMultiRow::HideRows::set (bool i_bHideRows)
{
  m_bHideRows = i_bHideRows;
  for (int ixRow = 0; ixRow < m_listdgvr->Count; ixRow++)
    m_listdgvr[ixRow]->Visible = !m_bHideRows;
}

//----------------------------------------------------------------------------
// Clone
//----------------------------------------------------------------------------
Object^ CDataGridViewMultiRow::Clone ()
{
  CDataGridViewMultiRow^ dgvr = (CDataGridViewMultiRow^)DataGridViewRow::Clone();
  if (dgvr)
  {
    dgvr->m_bHideRows = this->m_bHideRows;
    dgvr->m_listdgvr->Clear();
    dgvr->m_listdgvr->AddRange (this->m_listdgvr);
  }
  return dgvr;
}

//----------------------------------------------------------------------------
// Clear
//----------------------------------------------------------------------------
void CDataGridViewMultiRow::Clear ()
{
  for (int ixRow = 0; ixRow < m_listdgvr->Count; ixRow++)
  {
    if (m_listdgvr[ixRow]->DataGridView
    &&  m_listdgvr[ixRow]->DataGridView == this->DataGridView)
      m_listdgvr[ixRow]->DataGridView->Rows->Remove (m_listdgvr[ixRow]);
    m_listdgvr[ixRow]->Visible = true;
  }
  m_listdgvr->Clear();

  Update();
}

//----------------------------------------------------------------------------
// Add
//----------------------------------------------------------------------------
bool CDataGridViewMultiRow::Add (DataGridViewRow^ i_dgvr)
{
  return Insert (m_listdgvr->Count, i_dgvr);
}

//----------------------------------------------------------------------------
// Insert
//----------------------------------------------------------------------------
bool CDataGridViewMultiRow::Insert (int i_ixRow, DataGridViewRow^ i_dgvr)
{
  if (!i_dgvr)
    return false;
  if (i_dgvr->Index < 0)
    return false;  // block shared rows and rows that are not part of a DGV
  if (i_ixRow < 0)
    return false;
  else if (i_ixRow > m_listdgvr->Count)
    i_ixRow = m_listdgvr->Count;

  m_listdgvr->Insert (i_ixRow, i_dgvr);

  if (i_dgvr->DataGridView
  &&  i_dgvr->DataGridView != this->DataGridView)
    i_dgvr->DataGridView->Rows->Remove (i_dgvr);
  if (this->DataGridView)
  {
    int ixDgvr = this->DataGridView->Rows->IndexOf (this) + 1 + i_ixRow;
    if (i_dgvr->DataGridView == this->DataGridView
    &&  i_dgvr->Index != ixDgvr)
      i_dgvr->DataGridView->Rows->Remove (i_dgvr);

    ixDgvr = this->DataGridView->Rows->IndexOf (this) + 1 + i_ixRow;
    if (i_dgvr->DataGridView != this->DataGridView)
      this->DataGridView->Rows->Insert (ixDgvr, i_dgvr);
  }
  i_dgvr->Visible = !m_bHideRows;

  Update();

  return true;
}

//----------------------------------------------------------------------------
// Remove
//----------------------------------------------------------------------------
bool CDataGridViewMultiRow::Remove (int i_ixRow)
{
  return Remove (Rows[i_ixRow]);
}

//----------------------------------------------------------------------------
// Remove
//----------------------------------------------------------------------------
bool CDataGridViewMultiRow::Remove (DataGridViewRow^ i_dgvr)
{
  bool bResult = m_listdgvr->Remove (i_dgvr);

  if (i_dgvr)
  {
    if (i_dgvr->DataGridView
    &&  i_dgvr->DataGridView == this->DataGridView)
      i_dgvr->DataGridView->Rows->Remove (i_dgvr);
    i_dgvr->Visible = true;
  }

  Update();

  return bResult;
}

//----------------------------------------------------------------------------
// Update
//----------------------------------------------------------------------------
void CDataGridViewMultiRow::Update ()
{
  if (!this->DataGridView)
    return;
  if (this->Index < 0)
    throw gcnew InvalidOperationException ("Index is < 0. This may happen if the row was created by CreateCells(), then added to a DGV, which made a previously shared row become unshared, and then being accessed by the same invalidated object. Get the updated row object from the DGV.");

  array<int>^ aiNewLines = gcnew array<int>(m_listdgvr->Count);
  array<String^, 2>^ a2sValue = gcnew array<String^, 2>(this->Cells->Count, m_listdgvr->Count);

  for (int ixCell = 0; ixCell < Cells->Count; ixCell++)
  {
    for (int ixRow = 0; ixRow < m_listdgvr->Count; ixRow++)
    {
      if (m_listdgvr[ixRow]->Index < 0)
        continue;
      Object^ oValue = m_listdgvr[ixRow]->Cells[ixCell]->Value;
      if (oValue)
      {
        a2sValue[ixCell, ixRow] = oValue->ToString();
        int iNewLines = CString::Count (a2sValue[ixCell, ixRow], CONSTS::CRLF, StringComparison::InvariantCultureIgnoreCase);
        aiNewLines[ixRow] = Math::Max (aiNewLines[ixRow], iNewLines);
      }
    }
  }

  for (int ixCell = 0; ixCell < Cells->Count; ixCell++)
  {
    String^ sText = nullptr;
    for (int ixRow = 0; ixRow < m_listdgvr->Count; ixRow++)
    {
      if (ixRow > 0)
        sText += CONSTS::CRLF;
      sText += a2sValue[ixCell, ixRow];
      int iNewLines = CString::Count (a2sValue[ixCell, ixRow], CONSTS::CRLF, StringComparison::InvariantCultureIgnoreCase);
      sText += CString::Repeat (CONSTS::CRLF, aiNewLines[ixRow] - iNewLines);
    }
    this->Cells[ixCell]->Value = sText;
  }
}

//----------------------------------------------------------------------------
// OnDataGridViewChanged
//----------------------------------------------------------------------------
void CDataGridViewMultiRow::OnDataGridViewChanged ()
{
  try
  {
    if (this->DataGridView)
    {
      int ixDgvr = this->DataGridView->Rows->IndexOf (this) + 1;
      for (int ixCnt = 0; ixCnt < m_listdgvr->Count; ixCnt++)
        DataGridView->Rows->Insert (ixDgvr + ixCnt, m_listdgvr[ixCnt]);
    }
    else
    {
      for (int ixCnt = 0; ixCnt < m_listdgvr->Count; ixCnt++)
        m_listdgvr[ixCnt]->DataGridView->Rows->Remove (m_listdgvr[ixCnt]);
    }
  }
  finally
  {
    DataGridViewRow::OnDataGridViewChanged();
  }
}

//----------------------------------------------------------------------------
// PaintRow
//----------------------------------------------------------------------------
void CDataGridViewMultiRow::PaintRow (Object^ sender, DataGridViewRowPrePaintEventArgs^ e)
{
  ::DataGridView^ dgv = dynamic_cast<::DataGridView^>(sender);
  if (!dgv)
    return;
  if (e->RowIndex < 0 || e->RowIndex >= dgv->RowCount)
    return;
  CDataGridViewMultiRow^ dgvmr = dynamic_cast<CDataGridViewMultiRow^>(dgv->Rows->SharedRow(e->RowIndex));
  if (!dgvmr)
    return;
  if (dgvmr->DataGridView != dgv)
    return;

  bool bAutoHeight = dgv->AutoSizeRowsMode == DataGridViewAutoSizeRowsMode::AllCells
                  || dgv->AutoSizeRowsMode == DataGridViewAutoSizeRowsMode::AllCellsExceptHeaders
                  || dgv->AutoSizeRowsMode == DataGridViewAutoSizeRowsMode::DisplayedCells
                  || dgv->AutoSizeRowsMode == DataGridViewAutoSizeRowsMode::DisplayedCellsExceptHeaders;
  Graphics^ g = e->Graphics;
  StringFormatFlags enFlags = (StringFormatFlags)0;
  if (dgvmr->InheritedStyle->WrapMode != DataGridViewTriState::True)
    enFlags = enFlags | StringFormatFlags::NoWrap;
  StringFormat^ oStringFormat = gcnew StringFormat(enFlags);

  array<float>^       afRowHeight = gcnew array<float>(dgvmr->RowCount);
  array<int>^         aiLines     = gcnew array<int>  (dgvmr->RowCount);
  array<int,     2>^  a2iLines    = gcnew array<int,     2>(dgvmr->Cells->Count, dgvmr->RowCount);
  array<String^, 2>^  a2sValue    = gcnew array<String^, 2>(dgvmr->Cells->Count, dgvmr->RowCount);
  for (int ixRow = 0; ixRow < dgvmr->RowCount; ixRow++)
  {
    DataGridViewRow^ dgvr = dgvmr->Rows[ixRow];
    for (int ixCell = 0; ixCell < dgvmr->Cells->Count; ixCell++)
    {
      if (dgvr->Index < 0)
        continue;
      Object^ oValue = dgvr->Cells[ixCell]->Value;
      if (!oValue)
        continue;
      a2sValue[ixCell, ixRow] = oValue->ToString();
      int iCharacters = 0, iLines = 0;
      SizeF oLayoutArea ((float)dgvmr->Cells[ixCell]->Size.Width, 0);
      SizeF oTextSize = g->MeasureString (a2sValue[ixCell, ixRow],
                                          dgvmr->Cells[ixCell]->InheritedStyle->Font,
                                          oLayoutArea,
                                          oStringFormat,
                                          iCharacters,
                                          iLines);
      float fHeight = oTextSize.Height;
      if (!bAutoHeight)
        fHeight += 4;
      afRowHeight[ixRow] = Math::Max (afRowHeight[ixRow], fHeight);
      a2iLines[ixCell, ixRow] = iLines;
      aiLines[ixRow] = Math::Max (aiLines[ixRow], iLines);
    }
  }
  int iLength = dgv->Columns->GetColumnsWidth(DataGridViewElementStates::Visible);
  int iHeight = (int)Math::Ceiling(CMath::Sum (afRowHeight));
  dgvmr->Height = iHeight;

  e->PaintCellsBackground (e->ClipBounds, true);

  int iPositionX = e->RowBounds.X + dgvmr->HeaderCell->Size.Width - dgv->HorizontalScrollingOffset;
  int iPositionY = 0;
  for (int ixCell = 0; ixCell < dgvmr->Cells->Count; ixCell++)
  {
    String^ sText = nullptr;
    DataGridViewCell^ oCell = dgvmr->Cells[ixCell];
    Color oTextColor = oCell->Selected ? oCell->InheritedStyle->SelectionForeColor : oCell->InheritedStyle->ForeColor;
    Drawing::Brush^ oBrush = gcnew Drawing::SolidBrush (oTextColor);
    iPositionY = e->RowBounds.Y;
    if (!bAutoHeight)
      iPositionY += 2;
    for (int ixRow = 0; ixRow < dgvmr->RowCount; ixRow++)
    {
      if (ixRow > 0)
        sText += CONSTS::CRLF;
      sText += a2sValue[ixCell, ixRow];
      sText += CString::Repeat (CONSTS::CRLF, aiLines[ixRow] - a2iLines[ixCell, ixRow]);

      Rectangle oRectText (iPositionX, iPositionY, oCell->Size.Width, oCell->Size.Height);
      g->DrawString (a2sValue[ixCell, ixRow], oCell->InheritedStyle->Font, oBrush, oRectText, oStringFormat);
      iPositionY += (int)afRowHeight[ixRow];
    }
    dgvmr->Cells[ixCell]->Value = sText;
    iPositionX += oCell->Size.Width;
  }

  Color oLineColor = dgvmr->Selected ? dgvmr->InheritedStyle->SelectionForeColor : dgvmr->InheritedStyle->ForeColor;
  Pen^ oPen = gcnew Pen(oLineColor , 1);
  oPen->DashPattern = gcnew array<float>{5, 15};
  iPositionX = e->RowBounds.X + dgvmr->HeaderCell->Size.Width - dgv->HorizontalScrollingOffset;
  iPositionY = e->RowBounds.Y;
  for (int ixRow = 0; ixRow < dgvmr->RowCount - 1; ixRow++)
  {
    iPositionY += (int)afRowHeight[ixRow];
    g->DrawLine (oPen, iPositionX,           iPositionY,
                       iPositionX + iLength, iPositionY);
  }

  e->PaintHeader (true);

  e->Handled = true;
}

//----------------------------------------------------------------------------
// CommonConstructor
//----------------------------------------------------------------------------
void CDataGridViewMultiRow::CommonConstructor ( bool            i_bHideRows,
                                                ::DataGridView^ i_dgv)
{
  m_bHideRows = i_bHideRows;

  if (i_dgv)
    this->CreateCells(i_dgv);

  m_listdgvr = gcnew List<DataGridViewRow^>;
  this->ReadOnly = true;
}

这篇关于C# DataGridView:将多行合并为一但保留原件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆