滚动 DataGridView 的合并标题单元格时出现问题 [英] Problem while scrolling merged Header Cells of a DataGridView
问题描述
在我的 WinForm 项目中,我有一个 DataGridView 控件.
我实现了合并 2 列的标题单元格.它工作正常,但滚动时出现问题.下面的图片应该可以解释它.
有人可以帮忙吗?
这是我用来合并列标题单元格的代码.
合并后的Columns的索引为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;}私有无效 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.HorizontalCenter |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.HorizontalScrollingOffset, 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 are20
and21
.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屋!