滚动合并 DataGridView 的 Header 个单元格时出现问题

Problem while scrolling merged Header Cells of a DataGridView

在我的 WinForm 项目中,我有一个 DataGridView 控件。
我实现了合并 2 列的 Header 单元格。它工作正常,但滚动时出现问题。下图应该可以解释。
有人可以帮忙吗?

这是我用来合并列 Header 单元格的代码。
合并列的索引是 2021.

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);
}

绘图程序的一些修改:

注意 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();
    }
}