滚动合并 DataGridView 的 Header 个单元格时出现问题
Problem while scrolling merged Header Cells of a DataGridView
在我的 WinForm 项目中,我有一个 DataGridView 控件。
我实现了合并 2 列的 Header 单元格。它工作正常,但滚动时出现问题。下图应该可以解释。
有人可以帮忙吗?
这是我用来合并列 Header 单元格的代码。
合并列的索引是 20
和 21
.
private void loadEvents()
{
this.dgv_db_door.Paint += new PaintEventHandler(dgv_db_door_Paint);
this.dgv_db_door.Scroll += new ScrollEventHandler(dgv_db_door_Scroll);
this.dgv_db_door.ColumnWidthChanged += DataGridView1_ColumnWidthChanged;
this.dgv_db_door.Resize += DataGridView1_Resize;
}
private void dgv_db_door_Paint(object sender, PaintEventArgs e)
{
string doorCloser = "DOOR CLOSER";
//Index numbers of merged columns ara 20 and 21;
int mergedColumn1 = 20;
int mergedColumn2 = 21;
Rectangle r1 = dgv_db_door.GetCellDisplayRectangle(mergedColumn1, -1, true);
int w2 = dgv_db_door.GetCellDisplayRectangle(mergedColumn2, -1, true).Width;
r1.X += 1;
r1.Y += 1;
r1.Width = r1.Width + w2 - 2;
r1.Height = r1.Height / 2 - 2;
e.Graphics.FillRectangle(new SolidBrush(dgv_db_door.ColumnHeadersDefaultCellStyle.BackColor), r1);
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
e.Graphics.DrawString(doorCloser, dgv_db_door.ColumnHeadersDefaultCellStyle.Font,
new SolidBrush(dgv_db_door.ColumnHeadersDefaultCellStyle.ForeColor), r1, format);
}
private void dgv_db_door_Scroll(object sender, ScrollEventArgs e)
{
if (e.OldValue > e.NewValue)
{
}
else
{
this.InvalidateHeader();
}
}
private void DataGridView1_Resize(object sender, EventArgs e)
{
this.InvalidateHeader();
}
private void DataGridView1_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)
{
this.InvalidateHeader();
}
private void InvalidateHeader()
{
Rectangle rtHeader = this.dgv_db_door.DisplayRectangle;
rtHeader.Height = this.dgv_db_door.ColumnHeadersHeight / 2;
this.dgv_db_door.Invalidate(rtHeader);
}
绘图程序的一些修改:
- 自定义 Header 绘图矩形的大小计算指定范围内所有列的宽度。
- 绘图区域的位置是使用 left-most 列的位置 RowHeadersWidth and the current DataGridView.HorizontalScrollingOffset 计算的,它定义了客户端矩形的水平滚动位置。
- 要绘制自定义 Header,将创建一个剪辑区域 - 使用 Graphics.SetClip() - 从绘图区域中排除行 Header。
- 自定义 Header 仅在屏幕上可见时呈现。
- 我使用 TextRenderer.DrawText 而不是
Graphics.DrawString()
,因为这是此控件用来呈现其内容的方法。
- 因此,我使用 TextRenderer.MeasureText()
计算文本边界的高度
- TextFormatFlags.PreserveGraphicsClipping用于指示TextRenderer不要在Graphicsobject.
的裁剪区域之外绘制文本
注意 1:我已将 Double-Buffering 添加到 DataGridView,以避免在单击 Header 单元格时出现任何闪烁。当网格需要呈现大量行时,它可能会产生影响。
注意 2:您可以删除所有这些 InvalidateHeader()
调用,这里不需要。
► 此新行为允许随时重置列的范围以包含在自定义 Header 和 Header 的文本中(如可视示例所示)。
这是现在的样子:
using System.Reflection;
TextFormatFlags centerTopflags =
TextFormatFlags.HorizontalCenter | TextFormatFlags.Top | TextFormatFlags.PreserveGraphicsClipping;
string mergedHeaderText = string.Empty;
int[] mergedColumns = null;
public void SomeForm()
{
this.InitializeComponent();
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
dgvTest.GetType().GetProperty("DoubleBuffered", flags).SetValue(dgvTest, true);
mergedColumns = new int[] { 20, 21 };
mergedHeaderText = "DOOR CLOSER"
}
private void dgv_db_door_Paint(object sender, PaintEventArgs e)
{
var dgv = sender as DataGridView;
var headerStyle = dgv.ColumnHeadersDefaultCellStyle;
int colsWidth = -1;
int colsLeft = 1;
// Absolute Width of the merged Column range
for (int i = 0; i < mergedColumns.Length; i++) {
var col = dgv.Columns[mergedColumns[i]];
colsWidth += col.Visible ? col.Width : 0;
}
// Absolute Left position of the first Column to merge
if (mergedColumns[0] > 0) {
colsLeft += dgv.Columns.OfType<DataGridViewColumn>()
.Where(c => c.Visible).Take(mergedColumns[0]).Sum(c => c.Width);
}
// Merged Headers raw drawing Rectangle
var r = new Rectangle(
dgv.RowHeadersWidth + colsLeft - dgv.HorizontalScrollingOffset, 2,
colsWidth, dgv.ColumnHeadersHeight);
// Measure the Height of the text to render - no wrapping
r.Height = TextRenderer.MeasureText(e.Graphics, mergedHeaderText, headerStyle.Font, r.Size, centerTopflags).Height;
// Draw the merged Headers only if visible on screen
if (r.Right > dgv.RowHeadersWidth || r.X < dgv.DisplayRectangle.Right) {
// Clip the drawing Region to exclude the Row Header
var clipRect = new Rectangle(
dgv.RowHeadersWidth + 1, 0,
dgv.DisplayRectangle.Width - dgv.RowHeadersWidth, dgv.ColumnHeadersHeight);
e.Graphics.SetClip(clipRect);
using (var brush = new SolidBrush(headerStyle.BackColor)) e.Graphics.FillRectangle(brush, r);
TextRenderer.DrawText(e.Graphics, mergedHeaderText, headerStyle.Font, r, headerStyle.ForeColor, centerTopflags);
e.Graphics.ResetClip();
}
}
在我的 WinForm 项目中,我有一个 DataGridView 控件。
我实现了合并 2 列的 Header 单元格。它工作正常,但滚动时出现问题。下图应该可以解释。
有人可以帮忙吗?
这是我用来合并列 Header 单元格的代码。
合并列的索引是 20
和 21
.
private void loadEvents()
{
this.dgv_db_door.Paint += new PaintEventHandler(dgv_db_door_Paint);
this.dgv_db_door.Scroll += new ScrollEventHandler(dgv_db_door_Scroll);
this.dgv_db_door.ColumnWidthChanged += DataGridView1_ColumnWidthChanged;
this.dgv_db_door.Resize += DataGridView1_Resize;
}
private void dgv_db_door_Paint(object sender, PaintEventArgs e)
{
string doorCloser = "DOOR CLOSER";
//Index numbers of merged columns ara 20 and 21;
int mergedColumn1 = 20;
int mergedColumn2 = 21;
Rectangle r1 = dgv_db_door.GetCellDisplayRectangle(mergedColumn1, -1, true);
int w2 = dgv_db_door.GetCellDisplayRectangle(mergedColumn2, -1, true).Width;
r1.X += 1;
r1.Y += 1;
r1.Width = r1.Width + w2 - 2;
r1.Height = r1.Height / 2 - 2;
e.Graphics.FillRectangle(new SolidBrush(dgv_db_door.ColumnHeadersDefaultCellStyle.BackColor), r1);
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
e.Graphics.DrawString(doorCloser, dgv_db_door.ColumnHeadersDefaultCellStyle.Font,
new SolidBrush(dgv_db_door.ColumnHeadersDefaultCellStyle.ForeColor), r1, format);
}
private void dgv_db_door_Scroll(object sender, ScrollEventArgs e)
{
if (e.OldValue > e.NewValue)
{
}
else
{
this.InvalidateHeader();
}
}
private void DataGridView1_Resize(object sender, EventArgs e)
{
this.InvalidateHeader();
}
private void DataGridView1_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)
{
this.InvalidateHeader();
}
private void InvalidateHeader()
{
Rectangle rtHeader = this.dgv_db_door.DisplayRectangle;
rtHeader.Height = this.dgv_db_door.ColumnHeadersHeight / 2;
this.dgv_db_door.Invalidate(rtHeader);
}
绘图程序的一些修改:
- 自定义 Header 绘图矩形的大小计算指定范围内所有列的宽度。
- 绘图区域的位置是使用 left-most 列的位置 RowHeadersWidth and the current DataGridView.HorizontalScrollingOffset 计算的,它定义了客户端矩形的水平滚动位置。
- 要绘制自定义 Header,将创建一个剪辑区域 - 使用 Graphics.SetClip() - 从绘图区域中排除行 Header。
- 自定义 Header 仅在屏幕上可见时呈现。
- 我使用 TextRenderer.DrawText 而不是
Graphics.DrawString()
,因为这是此控件用来呈现其内容的方法。 - 因此,我使用 TextRenderer.MeasureText() 计算文本边界的高度
- TextFormatFlags.PreserveGraphicsClipping用于指示TextRenderer不要在Graphicsobject. 的裁剪区域之外绘制文本
注意 1:我已将 Double-Buffering 添加到 DataGridView,以避免在单击 Header 单元格时出现任何闪烁。当网格需要呈现大量行时,它可能会产生影响。
注意 2:您可以删除所有这些 InvalidateHeader()
调用,这里不需要。
► 此新行为允许随时重置列的范围以包含在自定义 Header 和 Header 的文本中(如可视示例所示)。
这是现在的样子:
using System.Reflection;
TextFormatFlags centerTopflags =
TextFormatFlags.HorizontalCenter | TextFormatFlags.Top | TextFormatFlags.PreserveGraphicsClipping;
string mergedHeaderText = string.Empty;
int[] mergedColumns = null;
public void SomeForm()
{
this.InitializeComponent();
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
dgvTest.GetType().GetProperty("DoubleBuffered", flags).SetValue(dgvTest, true);
mergedColumns = new int[] { 20, 21 };
mergedHeaderText = "DOOR CLOSER"
}
private void dgv_db_door_Paint(object sender, PaintEventArgs e)
{
var dgv = sender as DataGridView;
var headerStyle = dgv.ColumnHeadersDefaultCellStyle;
int colsWidth = -1;
int colsLeft = 1;
// Absolute Width of the merged Column range
for (int i = 0; i < mergedColumns.Length; i++) {
var col = dgv.Columns[mergedColumns[i]];
colsWidth += col.Visible ? col.Width : 0;
}
// Absolute Left position of the first Column to merge
if (mergedColumns[0] > 0) {
colsLeft += dgv.Columns.OfType<DataGridViewColumn>()
.Where(c => c.Visible).Take(mergedColumns[0]).Sum(c => c.Width);
}
// Merged Headers raw drawing Rectangle
var r = new Rectangle(
dgv.RowHeadersWidth + colsLeft - dgv.HorizontalScrollingOffset, 2,
colsWidth, dgv.ColumnHeadersHeight);
// Measure the Height of the text to render - no wrapping
r.Height = TextRenderer.MeasureText(e.Graphics, mergedHeaderText, headerStyle.Font, r.Size, centerTopflags).Height;
// Draw the merged Headers only if visible on screen
if (r.Right > dgv.RowHeadersWidth || r.X < dgv.DisplayRectangle.Right) {
// Clip the drawing Region to exclude the Row Header
var clipRect = new Rectangle(
dgv.RowHeadersWidth + 1, 0,
dgv.DisplayRectangle.Width - dgv.RowHeadersWidth, dgv.ColumnHeadersHeight);
e.Graphics.SetClip(clipRect);
using (var brush = new SolidBrush(headerStyle.BackColor)) e.Graphics.FillRectangle(brush, r);
TextRenderer.DrawText(e.Graphics, mergedHeaderText, headerStyle.Font, r, headerStyle.ForeColor, centerTopflags);
e.Graphics.ResetClip();
}
}