C# 滚动DataGridView的合并标题单元格时出现问题

C# 滚动DataGridView的合并标题单元格时出现问题,c#,winforms,merge,datagridview,graphics,C#,Winforms,Merge,Datagridview,Graphics,在我的WinForm项目中,我有一个DataGridView控件。 我实现了合并两列的标题单元格。它工作正常,但滚动时出现问题。下面的图片应该对此进行解释。 有人能帮忙吗 这是我用来合并列标题单元格的代码。 合并列的索引为20和21 private void loadEvents() { this.dgv_db_door.Paint += new PaintEventHandler(dgv_db_door_Paint); this.dgv_db_door.Scroll += ne

在我的WinForm项目中,我有一个DataGridView控件。
我实现了合并两列的标题单元格。它工作正常,但滚动时出现问题。下面的图片应该对此进行解释。
有人能帮忙吗

这是我用来合并列标题单元格的代码。
合并列的索引为
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);
}

对绘图程序的一些修改:

  • 自定义标题图形矩形的大小是通过计算指定范围内所有列的宽度来确定的
  • 绘图区域的位置是使用最左边的列的位置和当前值计算的,当前值定义了客户端矩形的水平滚动位置
  • 要绘制自定义标题,将创建一个剪裁区域-使用-从绘图区域中排除行标题
  • 自定义标题仅在屏幕上可见时呈现
  • 我使用的是而不是
    Graphics.DrawString()
    ,因为这是该控件用于呈现其内容的方法
  • 因此,我使用
  • 用于指示TextRenderer不要在图形对象的剪裁区域之外绘制文本
注意1:我在DataGridView中添加了双缓冲,以避免单击标题单元格时出现任何闪烁。当网格需要渲染大量行时,它可能会产生影响

注意2:您可以删除此处不需要的所有
InvalidateHeader()
调用

► 此新行为允许随时重置要包含在自定义标题和标题文本中的列范围(如可视示例所示)


这就是它现在的样子:


使用系统反射;
TextFormatFlags centerTopflags=
TextFormatFlags.HorizontalCenter | TextFormatFlags.Top | TextFormatFlags.PreserveGraphicsClipping;
string mergedHeaderText=string.Empty;
int[]mergedColumns=null;
公共形式
{
this.InitializeComponent();
var flags=BindingFlags.Instance | BindingFlags.NonPublic;
dgvTest.GetType().GetProperty(“双缓冲”,标志).SetValue(dgvTest,true);
mergedColumns=newint[]{20,21};
mergedHeaderText=“闭门器”
}
私有void dgv_db_door_Paint(对象发送器,PaintEventArgs e)
{
var dgv=作为DataGridView的发送方;
var headerStyle=dgv.ColumnHeadersDefaultCellStyle;
int colsWidth=-1;
int colsLeft=1;
//合并列范围的绝对宽度
for(int i=0;i0){
colsLeft+=dgv.Columns.OfType()
其中(c=>c.Visible).Take(mergedColumns[0]).Sum(c=>c.Width);
}
//合并标题原始图形矩形
var r=新矩形(
dgv.RowHeadersWidth+colsLeft-dgv.HorizontalScrollingOffset,2,
colsWidth,dgv.ColumnHeadershight);
//测量要渲染的文本的高度-无环绕
r、 高度=TextRenderer.MeasureText(例如图形、合并标题文本、标题样式、字体、r.Size、centerTopflags)。高度;
//仅当在屏幕上可见时绘制合并的标题
如果(r.Right>dgv.RowHeadersWidth | | r.X
thanx可获取有用的信息。你帮助我理解了工作的逻辑。关于边境局势,你是对的。我用你的密码换了密码。但问题依然存在。如果你感兴趣,我想和你分享。现在很完美!。再次重申一遍:)。
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();
    }
}