DataGridView 真·列头不高亮 真·列头合并

news/2024/3/29 1:29:59/文章来源:https://blog.csdn.net/tiger_zhao/article/details/130367249

高亮BUG

VB.Net,在 .NET Framework 4.8 的 WinForm 下(即不是 WPF 的绘图模式、也不是 Core 或 Mono 的开发框架),使用 DataGridView 行模式,还是有个列头表现为高亮显示:
默认行模式
查找各种解决方式:

  • 设置 ColumnHeadersDefaultCellStyle ———— 无效
  • 直接修改每列的 HeaderCell.Style ———— 无效

既然有上述"解决方式",说明早期版本是有效的。至于从哪个版本开始无效,就不深究了,反正碰上了如下解决。

真·解决方式

只能在 CellPainting 事件中进行自绘了,顺便实现了列头合并功能(不需要多行列头)。

  1. 添加一个RowDataGridView用户控件,集成不需要设计,关掉直接改代码。
  2. RowDataGridView.Designer.vb 按注释修改
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class RowDataGridViewInherits System.Windows.Forms.DataGridView                      '<- 原先是 UserControl'UserControl 重写释放以清理组件列表。<System.Diagnostics.DebuggerNonUserCode()> _Protected Overrides Sub Dispose(ByVal disposing As Boolean)TryIf disposing AndAlso components IsNot Nothing Thencomponents.Dispose()End IfFinallyMyBase.Dispose(disposing)End TryEnd Sub'Windows 窗体设计器所必需的Private components As System.ComponentModel.IContainer'注意: 以下过程是 Windows 窗体设计器所必需的'可以使用 Windows 窗体设计器修改它。  '不要使用代码编辑器修改它。<System.Diagnostics.DebuggerStepThrough()> _Private Sub InitializeComponent()components = New System.ComponentModel.Container()Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font  '<- 编译错误,删除该行End SubEnd Class
  1. RowDataGridView.vb
Public Class RowDataGridViewPrivate m_ColHeadersSpan() As String''' <summary>列头合并</summary>''' <remarks>务必在定义<see cref="Columns"/>后修改。合并设置仅按次序、不随列定义同步调整。</remarks>''' <returns>每列格式:''' <list type="bullet">''' <description>数值后的注释仅供参考</description>''' <description>数值 1: (默认)非合并列头</description>''' <description>数值 0: 被合并列头</description>''' <description>其他正整数: 合并开始列头</description>''' </list>''' ☆ 数值正确性不检查。''' ☆ 合并不影响自动列宽计算(即标题可能撑开合并开始列)。</returns>Public Property ColHeadersSpan() As String()GetIf Me.ColumnCount > 0 ThenDim lastCount As IntegerIf m_ColHeadersSpan Is Nothing ThenlastCount = 0ReDim m_ColHeadersSpan(Me.ColumnCount)ElselastCount = m_ColHeadersSpan.LengthReDim Preserve m_ColHeadersSpan(Me.ColumnCount)End IfFor i As Integer = 0 To Me.ColumnCount - 1If i < lastCount Thenm_ColHeadersSpan(i) = $"{Val(m_ColHeadersSpan(i))} '{Me.Columns(i).HeaderText}"Elsem_ColHeadersSpan(i) = $"1 '{Me.Columns(i).HeaderText}"End IfNextEnd IfReturn m_ColHeadersSpanEnd GetSet(value As String())m_ColHeadersSpan = valueEnd SetEnd PropertyPrivate ReadOnly Property ColHeadersSpanValue(ByVal index As Integer) As IntegerGetReturn Val(m_ColHeadersSpan(index))End GetEnd PropertyPrivate Sub RowDataGridView_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles Me.CellPaintingIf (Not Me.DesignMode) And (e.RowIndex = -1) And (e.ColumnIndex <> -1) ThenDebug.Print($"{e.ColumnIndex} : {e.Value}")Dim colSpan As Integer = Me.ColHeadersSpanValue(e.ColumnIndex)If colSpan > 0 ThenDim cellRect As New Rectangle(e.CellBounds.X - 1, e.CellBounds.Y, e.CellBounds.Width, e.CellBounds.Height - 1)' RowHeadersVisible = False 时最左可见列不需要向左合并网格线If e.CellBounds.X = 1 ThencellRect.X = 1cellRect.Width -= 1End If' 添加被合并列的宽度For i As Integer = 1 To colSpan - 1cellRect.Width += Me.Columns(e.ColumnIndex + i).WidthNextDim foreColorBrash As New SolidBrush(e.CellStyle.ForeColor)Dim backColorBrush As New SolidBrush(e.CellStyle.BackColor)Dim gridBrush As New SolidBrush(Me.GridColor)Dim gridLinePen As New Pen(gridBrush)Trye.Graphics.FillRectangle(backColorBrush, cellRect)e.Graphics.DrawRectangle(gridLinePen, cellRect)If e.FormattedValue IsNot Nothing ThenDim format As New StringFormat()Select Case e.CellStyle.AlignmentCase DataGridViewContentAlignment.BottomLeft, DataGridViewContentAlignment.MiddleLeft, DataGridViewContentAlignment.TopLeftformat.Alignment = StringAlignment.NearCase DataGridViewContentAlignment.BottomCenter, DataGridViewContentAlignment.MiddleCenter, DataGridViewContentAlignment.TopCenterformat.Alignment = StringAlignment.CenterCase Elseformat.Alignment = StringAlignment.FarEnd SelectSelect Case e.CellStyle.AlignmentCase DataGridViewContentAlignment.BottomCenter, DataGridViewContentAlignment.BottomLeft, DataGridViewContentAlignment.BottomRightformat.LineAlignment = StringAlignment.CenterCase DataGridViewContentAlignment.MiddleCenter, DataGridViewContentAlignment.MiddleLeft, DataGridViewContentAlignment.MiddleRightformat.LineAlignment = StringAlignment.FarCase Elseformat.LineAlignment = StringAlignment.NearEnd SelectcellRect.Height += 1 ' 使得垂直居中和非自绘比较一致e.Graphics.DrawString(CStr(e.FormattedValue), e.CellStyle.Font, foreColorBrash, cellRect, format)End IfFinallygridLinePen.Dispose()gridBrush.Dispose()backColorBrush.Dispose()foreColorBrash.Dispose()End TryEnd Ife.Handled = TrueEnd IfEnd SubEnd Class

注:

  1. 没有包括 New() 统一初始化行模式、是否显示行头等。
  2. 实现列头合并最正统的做法是给DataGridView*Column写继承类。但是仅为了一个属性需要给每种列类型写继承类,不如直接加属性在 DataGridView 上。
  3. 本来想把 m_ColHeadersSpan 定义成 Integer 数组,然后属性 ColHeadersSpan 加注释变字符数组方便设计器中编辑;但是 Visual Studio 死活不支持。只能加属性 ColHeadersSpanValue 实时解析,反正之前有列头判断,不会频繁调用。
  4. 继承自 VB6 的 Val() 函数容错性高,直接忽略数值之后的内容;不需要字符串拆分后转类型。

效果

对于已设计表格,只需要在 窗体.Designer.vb 中把 DataGridView 替换成 RowDataGridView 即可(注意前缀命名空间)。需要列头合并时设计器中修改 ColHeadersSpan,比如上例表格设为

1 '机能
2 '键1
0 '值1
2 '键2
0 '值2
2 '键3
0 '值3
1 '加锁者
1 '加锁时间
1 '解锁

最终表现自绘模式

自绘BUG

如果不需要列头合并,把上面 Span 相关的代码删除,不需要看本章。
那么来看看列头合并在水平滚动时的表现:

  1. 向右滚动到完整合并列头可见 ———— 正常滚动1
  2. 继续向右滚动到完整合并列头部分可见 ———— 正常
    (其实看 Debug 输出,这时仅从"值3"列开始自绘,按照上面的代码,其实最左的列头没有重绘————居然还能显示半个标题!?)滚动2
  3. 继续向右滚动滚动3
  4. 然后向左滚动 ———— 不正常
    (和步骤2一样最左的列头没有重绘,保留了原先的图像————右滚/左滚表现不一致啊!)滚动4

各种DataGridView列头合并的例子没有考虑到这种BUG吧

真·实现方式

找到了原因,只需要在每个被合并列(Span=0)也进行重绘就能解决

    Private Sub SingleDataGridView_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles Me.CellPaintingIf (Not Me.DesignMode) And (e.RowIndex = -1) And (e.ColumnIndex <> -1) ThenDim cellRect As New Rectangle(e.CellBounds.X - 1, e.CellBounds.Y, e.CellBounds.Width, e.CellBounds.Height - 1)' RowHeadersVisible = False 时最左可见列不需要向左合并网格线If e.CellBounds.X = 1 ThencellRect.X = 1cellRect.Width -= 1End If' 修正水平滚动时合并列标题半可见时的显示问题Dim startColIndex As Integer = e.ColumnIndexWhile Me.ColHeadersSpanValue(startColIndex) <= 0startColIndex -= 1cellRect.X -= Me.Columns(startColIndex).WidthcellRect.Width += Me.Columns(startColIndex).WidthEnd WhileDim colSpan As Integer = Me.ColHeadersSpanValue(startColIndex)' 添加被合并列的宽度(计算startColIndex时已经加了一部分)For i As Integer = startColIndex + 1 To startColIndex + colSpan - 1If i > e.ColumnIndex ThencellRect.Width += Me.Columns(i).WidthEnd IfNextDim foreColorBrash As New SolidBrush(e.CellStyle.ForeColor)Dim backColorBrush As New SolidBrush(e.CellStyle.BackColor)Dim gridBrush As New SolidBrush(Me.GridColor)Dim gridLinePen As New Pen(gridBrush)Trye.Graphics.FillRectangle(backColorBrush, cellRect)e.Graphics.DrawRectangle(gridLinePen, cellRect)If e.FormattedValue IsNot Nothing ThenDim format As New StringFormat()Select Case e.CellStyle.AlignmentCase DataGridViewContentAlignment.BottomLeft, DataGridViewContentAlignment.MiddleLeft, DataGridViewContentAlignment.TopLeftformat.Alignment = StringAlignment.NearCase DataGridViewContentAlignment.BottomCenter, DataGridViewContentAlignment.MiddleCenter, DataGridViewContentAlignment.TopCenterformat.Alignment = StringAlignment.CenterCase Elseformat.Alignment = StringAlignment.FarEnd SelectSelect Case e.CellStyle.AlignmentCase DataGridViewContentAlignment.BottomCenter, DataGridViewContentAlignment.BottomLeft, DataGridViewContentAlignment.BottomRightformat.LineAlignment = StringAlignment.CenterCase DataGridViewContentAlignment.MiddleCenter, DataGridViewContentAlignment.MiddleLeft, DataGridViewContentAlignment.MiddleRightformat.LineAlignment = StringAlignment.FarCase Elseformat.LineAlignment = StringAlignment.NearEnd SelectcellRect.Height += 1 ' 使得垂直居中和非自绘比较一致e.Graphics.DrawString(CStr(e.FormattedValue), e.CellStyle.Font, foreColorBrash, cellRect, format)End IfFinallygridLinePen.Dispose()gridBrush.Dispose()backColorBrush.Dispose()foreColorBrash.Dispose()End Trye.Handled = TrueEnd IfEnd Sub
  • 滚动步骤4的效果滚动4修正

题外话

这其实是对象继承用Inherits方式而不是Implements方式带来的先天缺陷。Inherits在实现继承时很爽(其实就是少写代码而已),但是父类一旦有变动所有继承类的行为会变化。这其实要求基类不变才能保证兼容性;想想有了DataGrid还要来个DataGridView,就是因为无法兼容;再看看 .NET Framework 从 1.1 到 4.8.1 那么多的版本,就是做不到低版本程序兼容高版本 Framework。

Implements方式有不同的接口,按特定接口调用时和其他特性无关。它所谓的缺陷

  1. 没有Protected
    其实可以定义继承/公共两套接口来实现。
  2. 费代码
    编译器自动完成会添加对应的方法,只要填空而已,不是很麻烦。
  3. 费内存
    在物理内存单位是G的时代太无聊了。实现方法增加了一些exe大小;
    保留父类对象也只不过多了一个变量,对象量再怎么多还差每对象几字节,又不是内存单位M时代。

CSDN 赶紧把 MarkDown 编辑器的维护人员拖出去鞭笞,不兼容 MarkDown 语法规则:

  • 没有空行的多行文字应该是同一个段落,无需换行
    (比如本行应该紧接前面的句号)结果要很别扭地修改换行(包括删除空行、<br/>)。
  • 实时解析输入刷新预览可以,不要自动修改啊。
    比如贴了一个图片的 MarkDown 代码,准备按照本地文件名进行上传,直接被替换成毫无用处的废话,连注释都没了。
    严重怀疑监听了键盘消息,不仅输入法的切换异常,连输入的字符都异常了:输入*变成(、输入(变成)。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_103350.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

YOLOv1代码复现2:数据加载器构建

YOLOv1代码复现2&#xff1a;数据加载器构建 前言 ​ 在经历了Faster-RCNN代码解读的摧残后&#xff0c;下决心要搞点简单的&#xff0c;于是便有了本系列的博客。如果你苦于没有博客详细告诉你如何自己去实现YOLOv1&#xff0c;那么可以看看本系列的博客&#xff0c;也许可以帮…

【Java实战篇】Day13.在线教育网课平台--生成支付二维码与完成支付

文章目录 一、需求&#xff1a;生成支付二维码1、需求分析2、表设计3、接口定义4、接口实现5、完善controller 二、需求&#xff1a;查询支付结果1、需求分析2、表设计与模型类3、接口定义4、接口实现步骤一&#xff1a;查询支付结果步骤二&#xff1a;保存支付结果&#xff08…

VUE3如何定义less全局变量

默认已经安装好了less&#xff0c;这里不过多讲。 &#xff08;1&#xff09;首先我们需要下载一个插件依赖&#xff1a; npm i style-resources-loader --save-dev &#xff08;2&#xff09;VUE3里配置vue.config.js文件内容 代码&#xff1a; const path require("p…

HashMap如何解决哈希冲突

HashMap如何解决哈希冲突 Hash算法和Hash表Hash冲突解决哈希冲突的方法开放地址法链式寻址法再hash法建立公共溢出区 Hash算法和Hash表 Hash算法就是把任意长度的输入通过散列算法编程固定长度的输出。这个输出结果就是一个散列值。 Hash表又称为“散列表”&#xff0c;它是通…

SpringBoot中一个注解优雅实现重试Retry框架

目录: 1、简介2、实现步骤 1、简介 重试&#xff0c;在项目需求中是非常常见的&#xff0c;例如遇到网络波动等&#xff0c;要求某个接口或者是方法可以最多/最少调用几次&#xff1b;实现重试机制&#xff0c;非得用Retry这个重试框架吗&#xff1f;那肯定不是&#xff0c;相信…

Mysql 查询同类数据中某一数字最大的所有数据

方法一、将时间进行排序后再分组 该表表名为customer, park_id表示园区id&#xff0c;joined_at表示用户的加入时间&#xff0c;created_at表示用户的创建时间。 需求&#xff1a;查出每个园区中&#xff0c;最早加入园区的第一位用户 select * from (select * from custome…

数据库课设--基于Python+MySQL的餐厅点餐系统(表的设计)

文章目录 一、系统需求分析二、系统设计1. 功能结构设计2、概念设计2.2.1 bill_food表E-R图2.2.2 bills表E-R图2.2.3 categories E-R图2.2.4 discounts表 E-R图2.2.5 emp表E-R图2.2.6 food 表E-R图2.2.7 member表E-R图2.2.8 member_point_bill表E-R图2.2.9 servers表E-R图2.2.1…

操作系统考试复习—第二章 2.1 2.2程序和进程的描述

第二章 进程的描述与控制 程序&#xff1a;有序的指令集合 程序顺序执行的特征&#xff1a;1.顺序性 2.封闭性 3.可再现性(确定性) 在多道程序环境下&#xff0c;允许多个程序并发执行&#xff0c;此时他们将失去封闭性&#xff0c;并具有间断性和不可再现性的特征。为此引…

基于SGM431的电路设计问题分析

本案例中,采用SGM431芯片设计了一个过压保护电路。 这个电路初次设计,有很多的问题,下面逐一分析 1.当输入24V,测得Vref=1.59V。Vout为1.15V;,mos管关断 2。经过多次测量发现,临界值在10V到10.5之间; 当输入10.5V时,测量Vref=1.69V。vout=1.15V;mos管关断 当输入1…

智慧物联网边缘协同感知(EICS)技术方案: 低功耗无线扫描唤醒技术

物联网的传感器或控制节点通常有体积限制&#xff0c;只能使用钮扣电池、小型电池&#xff0c;甚至使用能量收集源进行运作。在许多工业应用中&#xff0c;需要人工更换电池的成本&#xff0c;特别是在难以接近地方更换所需的成本&#xff0c;使得人们更加重视降低平均电流消耗…

深度学习入门到实践:相关基础概述

绪论 深度学习&#xff08;Deep Learning&#xff09;是近年来发展十分迅速的研究领域&#xff0c;并且在人工智能的很多子领域都取得了巨大的成功。从根源来讲&#xff0c;深度学习是机器学习的一个分支&#xff0c;是指一类问题以及解决这类问题的方法。     深度学习问题…

halcon灰度积分投影/垂直积分投影

简介:关于灰度投影积分可以用到的场合很多,例如分割字符,分割尺子上的刻度等,适用于有规律的变化这些内容的检测。本文复现了论文《基于深度学习和灰度纹理特征的铁路接触网绝缘子状态检测》中灰度积分投影实现了对绝缘子缺陷位置的检测。见(图1)灰度积分垂直方向投影获得…

AI智能智能课程第四讲 -数据库领域专家

使用chatGPT让你成为数据库领域专家 作业 现在要测试电商的下单功能&#xff1a;测试员张三在公司的电商平台上下了几个单&#xff0c;现在需要验证&#xff1a;张三这个客户下单的所有订单信息&#xff0c;包含订单编号&#xff0c;商品名称&#xff0c;商品价格&#xff0c;…

分支和循环语句(2)

文章目录 3.2 for循环3.2.1 for语句的语法3.2.2 for循环中的break和continue3.2.3 for语句的循环控制变量3.2.4 一些for循环的变种3.2.5 一道笔试题 3.3 do while循环3.3.1 do语句的语法3.3.2 do语句的特点3.3.3 do while循环中的break和continue 3.4 练习3.4.1 计算 n的阶乘3.…

Compiler- 尾调用

我们还是用例子来引入本次要探讨的问题--尾调用 #include <stdio.h>int fib(int a) {return a < 2 ? 1 : fib(a - 1) fib(a - 2); }int main() {int n,result;scanf("%d",&n);result fib(n);printf("result is %d.\n",result);return 0; …

创建路由React router(使用react-router dom V6版本)

React路由 隔了很长一段时间&#xff0c;重新捡起来React学习。 发现React的路由从原来的 Switch改成了Routes。nice&#xff0c;nice&#xff0c;nice&#xff01;&#xff01;&#xff01;&#xff01; 刚开始接触确实还是有一点生疏的。之前的关于【传参】【js跳转】【跳转模…

矿井下无人值守变电所电力监控系统的探讨与产品选型

摘要&#xff1a;为了探讨井下无人值守变电所的电力监控系统技术&#xff0c;以西山煤电马兰矿为背景&#xff0c;详细阐述了井下无人值守变电所电力监控系统技术的各项基本参数&#xff0c;如额定工作电压及整机输入视在功率、交换机或监控分站的传输口、高压配电装置的传输口…

下载VMWare

1、首先登录到vmware官网 官网&#xff1a;https://www.vmware.com/ 2、点击Resource 3、找到Product Downloads 4、找到我们要下载的产品&#xff0c;点击download product 5、选择自己要下载的版本和对应的系统 6、点击去下载 7、点击download now

国云筑基“翼”气风发,天翼云以科技创新绘就数字中国蓝图

科技云报道原创。 全球新一轮技术革命方兴未艾&#xff0c;特别是以数字技术为核心的信息技术革命&#xff0c;正在实现群体突破和加快广泛深度应用。 从2017年的“促进数字经济加快成长”&#xff0c;到2019年的“壮大数字经济”&#xff0c;到2020年的“全面推进‘互联网&am…

SpringBoot的配置和日志

1.配置文件的作用和意义 配置文件中配置整个项目中所有重要的数据&#xff0c;比如&#xff1a; 1.数据库的连接信息&#xff08;包含用户名和密码的设置&#xff09;&#xff1b; 2.项目的启动端口&#xff1b; 3.第三方系统的调用秘钥等信息&#xff1b; 4.用于发现和定位问…