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

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

问题描述

首先:完成后,我将自己为这个问题提供答案。

但是您可能会帮助我,我会感谢您的所有建议。



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



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




  • 如何清楚地显示行属于同一行?

    我可以添加另一列,该列的单元格对于连接的行显示相同的数字,但是这不容易看到,并且需要另一列。 b $ b我的解决方案是所有连接的行都具有相同的背景色,并且颜色会发生变化,例如每组连接的行在白色和浅灰色之间。


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




因此,我决定创建一个 DataGridViewMultiRow

我将发布该类的完整代码



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



我仍然需要修正细节,例如正确放置文字



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



待续。

解决方案

在克服了种种弊端之后,我终于创建了一个解决方案。

它是用C ++ / CLI编写的,因此大多数人都必须对其进行修改以适合C#的使用。

此解决方案包含一些用户功能,这些功能不属于解决方案的一部分,但其用途应易于猜出其名称。 p>

预览:
< img src = https://i.stack.imgur.com/31f5E.png alt =在此处输入图片描述>

  #pragma使用$名称空间System一次


使用名称空间System :: Collections :: Generic;
使用名称空间System :: Drawing;
使用名称空间System :: Windows :: Forms;
使用名称空间System :: ComponentModel;

公共引用类CDataGridViewMultiRow:DataGridViewRow
{
public:
// ------------------- -------------------------------------------------- -------
//构造函数
// ------------------------------ ----------------------------------------------
CDataGridViewMultiRow();
CDataGridViewMultiRow(bool i_bHideRows);
CDataGridViewMultiRow(bool i_bHideRows,:: DataGridView ^ i_dgv);

// ---------------------------------------- ------------------------------------
//克隆
//- -------------------------------------------------- -------------------------
虚拟对象^ Clone()覆盖;

// ---------------------------------------- ------------------------------------
//清除
//- -------------------------------------------------- -------------------------
void Clear();

// ---------------------------------------- ------------------------------------
//添加,插入
/ / ------------------------------------------------- ---------------------------
bool添加(DataGridViewRow ^ i_dgvr);
bool插入(int i_ixRow,DataGridViewRow ^ i_dgvr);

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

// ---------------------------------------- ------------------------------------
//更新
//- -------------------------------------------------- -------------------------
void Update();

// ---------------------------------------- ------------------------------------
// PaintRow
//
//说明:手动绘制该行。
//
// !!!重要的提醒: !!!
//此方法必须附加到DataGridView的RowPrePaint事件。
// -------------------------------------------- --------------------------------
静态void PaintRow(Object ^发件人,DataGridViewRowPrePaintEventArgs ^ e);

// ---------------------------------------- ------------------------------------
//属性
//- -------------------------------------------------- -------------------------
属性DataGridViewRow ^行[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;}
void set(bool i_bHideRows) ; }

公共:

受保护:
List< DataGridViewRow ^> ^ m_listdgvr;
bool m_bHideRows;

私有:

保护:
虚拟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;

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)
返回;

int ixDgvr = -1;
DataGridViewRow ^ dgvr = m_listdgvr [i_ixRow];
if(dgvr-> DataGridView
& dgvr-> DataGridView == this-> DataGridView)
{
ixDgvr = dgvr->索引;
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->行->插入(ixDgvr,i_dgvr);
i_dgvr->可见=!m_bHideRows;
}

Update();
}

// ----------------------------------- -----------------------------------------
//属性: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;
}

// ----------------------------------- -----------------------------------------
//克隆
// ---------------------------------------------- ------------------------------
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;
}

// ----------------------------------- -----------------------------------------
//清除
// ---------------------------------------------- ------------------------------
void CDataGridViewMultiRow :: Clear()
{
for(int ixRow = 0; ixRow< m_listdgvr-> Count; ixRow ++)
{
if(m_listdgvr [ixRow]-> DataGridView
& m_listdgvr [ixRow]- > DataGridView ==此-> DataGridView)
m_listdgvr [ixRow]-> DataGridView->行->删除(m_listdgvr [ixRow]);
m_listdgvr [ixRow]-> Visible = true;
}
m_listdgvr-> Clear();

Update();
}

// ----------------------------------- -----------------------------------------
//添加
// ---------------------------------------------- ------------------------------
bool CDataGridViewMultiRow :: Add(DataGridViewRow ^ i_dgvr)
{
返回插入(m_listdgvr-> Count,i_dgvr);
}

// ----------------------------------- -----------------------------------------
//插入
// ---------------------------------------------- ------------------------------
bool CDataGridViewMultiRow :: Insert(int i_ixRow,DataGridViewRow ^ i_dgvr)
{
如果(!i_dgvr)
返回false;
如果(i_dgvr-> Index< 0)
返回false; //如果(i_ixRow< 0)
返回false,则阻止共享行和不属于DGV
的行。
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- >删除(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->行-> ;删除(i_dgvr);

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

Update();

返回true;
}

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

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

if(i_dgvr)
{
if(i_dgvr-> DataGridView
& i_dgvr-> DataGridView == this-> DataGridView)
i_dgvr-> DataGridView->行->删除(i_dgvr);
i_dgvr-> Visible = true;
}

Update();

返回bResult;
}

// ----------------------------------- -----------------------------------------
//更新
// ---------------------------------------------- ------------------------------
void CDataGridViewMultiRow :: Update()
{
如果(!this-> DataGridView)
返回;
if(this-> Index< 0)
throw gcnew InvalidOperationException( Index is<0。如果该行由CreateCells()创建,然后添加到DGV,则可能会发生使先前共享的行变为非共享,然后由相同的无效对象访问。从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-> Cell; ixCell ++)
{
for(int ixRow = 0; ixRow< m_listdgvr-> Count ; ixRow ++)
{
如果(m_listdgvr [ixRow]-> Index< 0)
继续;
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 ++)
{
字符串^ 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->行->插入(ixDgvr + ixCnt,m_listdgvr [ixCnt]);
}
其他
{
for(int ixCnt = 0; ixCnt< m_listdgvr-> Count; ixCnt ++)
m_listdgvr [ixCnt]-> DataGridView- >行->删除(m_listdgvr [ixCnt]);
}
}
最后
{
DataGridViewRow :: OnDataGridViewChanged();
}
}

// ------------------------------ ----------------------------------------------
// PaintRow
// ----------------------------------------- -----------------------------------
void CDataGridViewMultiRow :: PaintRow(Object ^发送者,DataGridViewRowPrePaintEventArgs ^ e)
{
:: DataGridView ^ dgv = dynamic_cast< :: DataGridView ^>(发送者);
如果(!dgv)
返回;
如果(e-> RowIndex< 0 || e-> RowIndex> = dgv-> RowCount)
返回;
CDataGridViewMultiRow ^ dgvmr = dynamic_cast< CDataGridViewMultiRow ^>(dgv-> Rows-> SharedRow(e-> RowIndex));
如果(!dgvmr)
返回;
if(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;
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)
继续;
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-> 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-> Horizo​​ntalScrollingOffset;
int iPositionY = 0;
for(int ixCell = 0; ixCell< dgvmr-> Cells-> Count; ixCell ++)
{
String ^ sText = nullptr;
DataGridViewCell ^ oCell = dgvmr-> Cells [ixCell];
颜色oTextColor = oCell->已选择? 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]);

矩形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->已选择? 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-> Horizo​​ntalScrollingOffset;
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;
}


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天全站免登陆