首先:一旦我完成,我将自己提供这个问题的答案。
但是你可以在我的路上帮助我,我会很感激你的所有建议。
我有一个DataGridView,它们包含不同的行。我的问题是找到一种合适的方式来显示和使用这些连接的行。
我的第一个想法是保持每一行的个性,但这有一些缺点:
如何清楚地显示行属于一起?
我可以添加另一个列,其单元格显示连接行的相同数字,但这不容易看到,需要另一列。
我的解决方案是所有连接的行具有相同的背景颜色,并且颜色会发生变化,例如每组连接行的白色和浅灰色之间。
如何使用连接的行?一旦我选择了一组中的一行,我就必须通过提取哪些行属于一起的信息(保存在单元格的标签或隐藏的单元格中)来分析该行,并选择它们。在DataGridView中向上/向下移动行的工作量更大:我还需要分析相邻的行集以查看我必须移动的距离。
因此我决定创建一个DataGridViewMultiRow
我将在此处发布该课程的完整代码作为答案。
它将继承自DataGridViewRow
(" DGVR")并存储单个DGVR或其他多行的列表,并通过自己的代码绘制行的单元格将它们显示为一个。但是,我仍然需要找出用于此目的的事件。 MSDN suggests to use DataGridView.RowPrePaint
,但我宁愿使用绑定到DGVR本身的事件。也许我会分析DataGridViewRow.Paint()的源代码并编写我自己的方法......
当添加到多行时,单行将变为不可见(它们可以通过滥用概念再次切换到可见,但是.net本身有很多不受滥用的保护;也许我甚至不会切换到隐形,因此用户负责。)
通过强制每个DGVR成为与多行相同的DGV的一部分,可以简单地避免多行中的递归,并且因为每行只能添加到一个DGV而且只能添加一次,所以我不必再检查递归。
现在我正在努力实现内部行列表。我正在考虑使用.net DataGridViewRowCollection
,但我发现它的操作与DataGridView
本身紧密相关:DGV只能有一个DGVRColl,但每个DGVRColl都指的是DGV。所以我的每个DGVMultiRow都会有半连接的DGVRColl
我打算问这是否会引起问题,但是我已经发现在实例化DGVRColl时我必须提供一个DGV,这在我调用DGVMultiRow ctor的那一刻我没有。此外,当使用DGVRColl并为其提供公共获取属性时,我只能挂钩“收藏品”'事件并且无法控制Add()
和Remove()
等单个操作。所以我将使用一个简单的私人列表。
延续#1
我完成了主要工作,看起来已经很不错了:
我仍然需要修复细节,例如在移动滚动条和其他小东西时正确放置文本。
我决定不覆盖DataGridViewRow.Paint()
因为那个内部连接太多了。所以我首先使用了DGV的CellPainting
事件,这是一个好的开始。但是我需要同时拥有行中所有单元格的信息,因此我按照MSDN的建议继续覆盖DataGridView.RowPrePaint()
,请参阅上面的链接。这非常有效。
继续。
答案 0 :(得分:0)
经过各种弊端,我终于创造了一个解决方案 它是用C ++ / CLI编写的,因此大多数人都必须调整它以便在C#中使用 此解决方案包含一些不属于解决方案的用户功能,但其目的应该易于通过其名称进行猜测。
#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;
}