滚动 DataGridView 的合并标题单元格时出现问题 [英] Problem while scrolling merged Header Cells of a DataGridView

查看:50
本文介绍了滚动 DataGridView 的合并标题单元格时出现问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

这是我用来合并列标题单元格的代码.
合并后的Columns的索引为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;}私有无效 dgv_db_door_Paint(对象发送者,PaintEventArgs e){字符串 doorCloser = "DOOR CLOSE";//合并列ara 20和21的索引号;int 合并列 1 = 20;int 合并列 2 = 21;矩形 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 格式 = new StringFormat();format.Alignment = StringAlignment.Center;format.LineAlignment = StringAlignment.Center;e.Graphics.DrawString(doorCloser, dgv_db_door.ColumnHeadersDefaultCellStyle.Font,新的 SolidBrush(dgv_db_door.ColumnHeadersDefaultCellStyle.ForeColor),r1,格式);}私人无效 dgv_db_door_Scroll(对象发送者,ScrollEventArgs e){如果(e.OldValue > e.NewValue){}别的{this.InvalidateHeader();}}private void DataGridView1_Resize(对象发送者,EventArgs e){this.InvalidateHeader();}private void DataGridView1_ColumnWidthChanged(对象发送者,DataGridViewColumnEventArgs e){this.InvalidateHeader();}私有无效 InvalidateHeader(){矩形 rtHeader = this.dgv_db_door.DisplayRectangle;rtHeader.Height = this.dgv_db_door.ColumnHeadersHeight/2;this.dgv_db_door.Invalidate(rtHeader);}

解决方案

对绘图程序的一些修改:

  • 自定义标题绘制矩形的大小计算指定范围内所有列的宽度.
  • 绘图区域的位置是使用最左侧列的位置计算的,


    使用 System.Reflection;TextFormatFlags centerTopflags =TextFormatFlags.Horizo​​ntalCenter |TextFormatFlags.Top |TextFormatFlags.PreserveGraphicsClipping;字符串mergedHeaderText = string.Empty;int[] 合并列 = null;public void SomeForm(){this.InitializeComponent();var flags = BindingFlags.Instance |BindingFlags.NonPublic;dgvTest.GetType().GetProperty(DoubleBuffered", flags).SetValue(dgvTest, true);合并列 = 新 int[] { 20, 21 };mergeHeaderText = "DOOR CLOSE";}私有无效 dgv_db_door_Paint(对象发送者,PaintEventArgs e){var dgv = 发送方为 DataGridView;var headerStyle = dgv.ColumnHeadersDefaultCellStyle;int colsWidth = -1;int colsLeft = 1;//合并列范围的绝对宽度for (int i = 0; i  0){colsLeft += dgv.Columns.OfType().Where(c => c.Visible).Take(mergedColumns[0]).Sum(c => c.Width);}//合并标题原始绘制矩形var r = 新矩形(dgv.RowHeadersWidth + colsLeft - dgv.Horizo​​ntalScrollingOffset, 2,colsWidth, dgv.ColumnHeadersHeight);//测量要渲染的文本的高度 - 不换行r.Height = TextRenderer.MeasureText(e.Graphics,mergedHeaderText,headerStyle.Font,r.Size,centerTopflags).Height;//仅在屏幕上可见时绘制合并的标题if (r.Right > dgv.RowHeadersWidth || r.X < dgv.DisplayRectangle.Right) {//裁剪绘图区域以排除行标题var clipRect = 新矩形(dgv.RowHeadersWidth + 1, 0,dgv.DisplayRectangle.Width - dgv.RowHeadersWidth, dgv.ColumnHeadersHeight);e.Graphics.SetClip(clipRect);使用 (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();}}

    In my WinForm project I have a DataGridView control.
    I achieved to merge the Header cells of 2 Columns. It is working fine but there is a problem when scrolling. The image below should explain it.
    Can anyone help?

    This is the code I use to merging the Column Header cells.
    The indexes of the merged Columns are 20 and 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);
    }
    

    解决方案

    A few modifications to the drawing procedure:

    • The custom Header drawing Rectangle is sized calculating the Width of all the Columns in the specified Range.
    • The position of the drawing area is calculated using the left-most Column's position, the RowHeadersWidth and the current DataGridView.HorizontalScrollingOffset, which defines the horizontal scroll position of the client rectangle.
    • To draw the custom Header, a clipping region is created - using Graphics.SetClip() - to exclude the Row Header from the drawing area.
    • The custom Header is only rendered when visible on screen.
    • I'm using TextRenderer.DrawText instead of Graphics.DrawString(), since this is the method used by this control to render its content.
    • Hence, I'm calculating the height of the text bounds using TextRenderer.MeasureText()
    • TextFormatFlags.PreserveGraphicsClipping is used to instruct TextRenderer not to draw the text outside the clipping region of the Graphics object.

    Note 1: I've added Double-Buffering to the DataGridView, to avoid any flickering when clicking the Header Cells. It may have an impact when the grid needs to render a high number of Rows.

    Note 2: You can remove all those InvalidateHeader() calls, not needed here.

    ► This new behavior allows to reset the range of Columns to include in the custom Header and the Header's Text at any time (as shown in the visual example).


    This is how it looks now:


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

    这篇关于滚动 DataGridView 的合并标题单元格时出现问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆