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

news2024/11/24 18:51:44

高亮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 RowDataGridView
    Inherits System.Windows.Forms.DataGridView                      '<- 原先是 UserControl

    'UserControl 重写释放以清理组件列表。
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            If disposing AndAlso components IsNot Nothing Then
                components.Dispose()
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End 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 Sub

End Class
  1. RowDataGridView.vb
Public Class RowDataGridView

    Private 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()
        Get
            If Me.ColumnCount > 0 Then
                Dim lastCount As Integer
                If m_ColHeadersSpan Is Nothing Then
                    lastCount = 0
                    ReDim m_ColHeadersSpan(Me.ColumnCount)
                Else
                    lastCount = m_ColHeadersSpan.Length
                    ReDim Preserve m_ColHeadersSpan(Me.ColumnCount)
                End If
                For i As Integer = 0 To Me.ColumnCount - 1
                    If i < lastCount Then
                        m_ColHeadersSpan(i) = $"{Val(m_ColHeadersSpan(i))} '{Me.Columns(i).HeaderText}"
                    Else
                        m_ColHeadersSpan(i) = $"1 '{Me.Columns(i).HeaderText}"
                    End If
                Next
            End If
            Return m_ColHeadersSpan
        End Get
        Set(value As String())
            m_ColHeadersSpan = value
        End Set
    End Property

    Private ReadOnly Property ColHeadersSpanValue(ByVal index As Integer) As Integer
        Get
            Return Val(m_ColHeadersSpan(index))
        End Get
    End Property

   Private Sub RowDataGridView_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles Me.CellPainting
        If (Not Me.DesignMode) And (e.RowIndex = -1) And (e.ColumnIndex <> -1) Then
            Debug.Print($"{e.ColumnIndex} : {e.Value}")
            Dim colSpan As Integer = Me.ColHeadersSpanValue(e.ColumnIndex)
            If colSpan > 0 Then
                Dim 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 Then
                    cellRect.X = 1
                    cellRect.Width -= 1
                End If
                ' 添加被合并列的宽度
                For i As Integer = 1 To colSpan - 1
                    cellRect.Width += Me.Columns(e.ColumnIndex + i).Width
                Next
                Dim 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)

                Try
                    e.Graphics.FillRectangle(backColorBrush, cellRect)
                    e.Graphics.DrawRectangle(gridLinePen, cellRect)

                    If e.FormattedValue IsNot Nothing Then
                        Dim format As New StringFormat()
                        Select Case e.CellStyle.Alignment
                            Case DataGridViewContentAlignment.BottomLeft, DataGridViewContentAlignment.MiddleLeft, DataGridViewContentAlignment.TopLeft
                                format.Alignment = StringAlignment.Near
                            Case DataGridViewContentAlignment.BottomCenter, DataGridViewContentAlignment.MiddleCenter, DataGridViewContentAlignment.TopCenter
                                format.Alignment = StringAlignment.Center
                            Case Else
                                format.Alignment = StringAlignment.Far
                        End Select
                        Select Case e.CellStyle.Alignment
                            Case DataGridViewContentAlignment.BottomCenter, DataGridViewContentAlignment.BottomLeft, DataGridViewContentAlignment.BottomRight
                                format.LineAlignment = StringAlignment.Center
                            Case DataGridViewContentAlignment.MiddleCenter, DataGridViewContentAlignment.MiddleLeft, DataGridViewContentAlignment.MiddleRight
                                format.LineAlignment = StringAlignment.Far
                            Case Else
                                format.LineAlignment = StringAlignment.Near
                        End Select
                        cellRect.Height += 1 ' 使得垂直居中和非自绘比较一致
                        e.Graphics.DrawString(CStr(e.FormattedValue), e.CellStyle.Font, foreColorBrash, cellRect, format)
                    End If
                Finally
                    gridLinePen.Dispose()
                    gridBrush.Dispose()
                    backColorBrush.Dispose()
                    foreColorBrash.Dispose()
                End Try
            End If
            e.Handled = True
        End If
    End Sub

End 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.CellPainting
        If (Not Me.DesignMode) And (e.RowIndex = -1) And (e.ColumnIndex <> -1) Then
            Dim 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 Then
                cellRect.X = 1
                cellRect.Width -= 1
            End If
            ' 修正水平滚动时合并列标题半可见时的显示问题
            Dim startColIndex As Integer = e.ColumnIndex
            While Me.ColHeadersSpanValue(startColIndex) <= 0
                startColIndex -= 1
                cellRect.X -= Me.Columns(startColIndex).Width
                cellRect.Width += Me.Columns(startColIndex).Width
            End While
            Dim colSpan As Integer = Me.ColHeadersSpanValue(startColIndex)
            ' 添加被合并列的宽度(计算startColIndex时已经加了一部分)
            For i As Integer = startColIndex + 1 To startColIndex + colSpan - 1
                If i > e.ColumnIndex Then
                    cellRect.Width += Me.Columns(i).Width
                End If
            Next

            Dim 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)

            Try
                e.Graphics.FillRectangle(backColorBrush, cellRect)
                e.Graphics.DrawRectangle(gridLinePen, cellRect)

                If e.FormattedValue IsNot Nothing Then
                    Dim format As New StringFormat()
                    Select Case e.CellStyle.Alignment
                        Case DataGridViewContentAlignment.BottomLeft, DataGridViewContentAlignment.MiddleLeft, DataGridViewContentAlignment.TopLeft
                            format.Alignment = StringAlignment.Near
                        Case DataGridViewContentAlignment.BottomCenter, DataGridViewContentAlignment.MiddleCenter, DataGridViewContentAlignment.TopCenter
                            format.Alignment = StringAlignment.Center
                        Case Else
                            format.Alignment = StringAlignment.Far
                    End Select
                    Select Case e.CellStyle.Alignment
                        Case DataGridViewContentAlignment.BottomCenter, DataGridViewContentAlignment.BottomLeft, DataGridViewContentAlignment.BottomRight
                            format.LineAlignment = StringAlignment.Center
                        Case DataGridViewContentAlignment.MiddleCenter, DataGridViewContentAlignment.MiddleLeft, DataGridViewContentAlignment.MiddleRight
                            format.LineAlignment = StringAlignment.Far
                        Case Else
                            format.LineAlignment = StringAlignment.Near
                    End Select
                    cellRect.Height += 1 ' 使得垂直居中和非自绘比较一致
                    e.Graphics.DrawString(CStr(e.FormattedValue), e.CellStyle.Font, foreColorBrash, cellRect, format)
                End If
            Finally
                gridLinePen.Dispose()
                gridBrush.Dispose()
                backColorBrush.Dispose()
                foreColorBrash.Dispose()
            End Try
            e.Handled = True
        End If
    End 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.coloradmin.cn/o/460326.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python3语法笔记(前篇)

文章目录 前言基础杂项变量和数据变量与运算数值字符串列表&#xff08;list&#xff09;、元组&#xff08;tuple&#xff09;和range序列类型&#xff08;sequence types&#xff09;和切片&#xff08;slicing&#xff09;集合&#xff08;set&#xff09;和字典&#xff08…

PS批量给图片加水印

一、打开PS&#xff0c;导入图片 点击菜单栏-文件-打开-载入一张需要设置文字水印的图片 二、点击菜单栏-窗口-动作&#xff0c;打开Photshop的动作组工作台窗口 先点击新建组图标新建一个组类别&#xff0c;再点击新建动作图标新建一个动作&#xff0c;新建后自动开始录制 …

DASFAA 2023|创邻周研博士分享前沿图数据库观点

4月17-20日&#xff0c;2023年第28届高级应用数据库系统国际会议&#xff08;DASFAA2023&#xff09;在天津成功举行。创邻科技CTO周研博士受邀参会&#xff0c;围绕Galaxybase国产高性能图数据库进行精彩分享。 DASFAA 2023由DASFAA指导委员会&#xff08;DASFAA Steering Co…

CVPR 2023 | 语义分割新范式:点监督遇上隐式场

密集预测(dense prediction)网络是解决诸如语义分割和图像本征分解(intrinsic decomposition)等场景理解问题的基本框架。现有工作[1-2] 通常使用像素级标注作为训练密集预测模型的监督。但是像素级别的密集标注非常昂贵&#xff0c; 对一些任务也无法给出精准的像素标注&#…

Linux-初学者系列3——虚拟光驱使用mount挂载操作

虚拟光驱使用mount挂载操作 一、mount挂载操作1、利用图形将光盘镜像文件&#xff0c;放入光驱设备中2、找到Linux光驱设备&#xff08;青色&#xff09; 1、挂载光盘和分区mount命令语法&#xff1a;示例&#xff1a;卸载&#xff08;注意事项&#xff09; 二、Linux快捷键 一…

Vue(绑定样式、条件渲染、列表渲染)

一、绑定样式 1. class绑定样式名 标签中使用 :class"xxx" xxx可以是字符串&#xff0c;对象&#xff0c;数组 字符串&#xff1a;适用于类名不确定&#xff0c;动态获取 数组&#xff1a;适用于绑定多个样式&#xff0c;个数不确定&#xff0c;字数不确定 对象&…

ProteinGAN扩展蛋白质序列空间

为了更广泛的医学应用&#xff0c;为催化任何所需的化学反应而重新设计蛋白质是蛋白质工程的重点。因此作者开发了ProteinGAN&#xff0c;这是一种基于自注意力的生成对抗网络&#xff0c;能够学习自然蛋白质序列的多样性&#xff0c;并生成功能性蛋白质序列。 来自&#xff1…

基于GEE平台的植被覆盖度(FVC)像元二分法计算

一、植被覆盖度计算方法 植被覆盖度FVC&#xff08;Fractional Vegetation Cover&#xff09;定义为单位面积内绿色植被冠层垂直投影面积所占比例。FVC是衡量地表植被状况的重要指标之一&#xff0c;也是区域生态系统环境变化的重要指示&#xff0c;对水文、生态、区域变化等都…

线程同步方式之一互斥锁

线程同步的4种方式&#xff1a;互斥锁、条件变量、读写锁、信号量 了解概念-临界资源、互斥、临界区、原子性 回想一下在信号量那部分提起过的几个概念&#xff0c;将多个执行流串行安全访问的共享资源称为临界资源&#xff0c;多个执行流中访问临界资源的代码所在的地址空间…

第4章 数据结构之“队列”

队列简介(queue) 1.一个先进先出的数据结构 2.javascript中没有这个数据结构&#xff0c;但是可以使用array实现队列的所有功能。 3.队列常用操作&#xff1a;push&#xff0c;shift&#xff0c;获取队列头部的元素&#xff1a;queue[0] const queue []// 入队&#xff1a; …

网络基础之网络传输基本流程

网络基础 此小节介绍网络基础概念 首先要明确的是 网络是层状结构&#xff01;分层->OP->解耦 网络发展&#xff1a;最早的时候&#xff0c;每台计算机之间是相互独立的。后续发展到网络互联&#xff0c;就是将多台计算机连接在一起&#xff0c;完成数据共享。 协议&…

Jmeter配置元件之csv数据文件配置

一、csv简介 csv是非常通用的一种文件格式&#xff0c;适用于批量导入数据到接口参数中&#xff0c;或者保存测试结果都可以使用csv数据文件(jmeter不仅仅支持这一种读取文件的方式)&#xff0c;csv数据一行即为数据表的一行&#xff0c;多个字段用逗号隔开。 登录接口 …

A+CLUB管理人支持计划第四期 | 香农投资

免责声明 本文内容仅对合格投资者开放&#xff01; 私募基金的合格投资者是指具备相应风险识别能力和风险承担能力&#xff0c;投资于单只私募基金的金额不低于100 万元且符合下列相关标准的单位和个人&#xff1a; &#xff08;一&#xff09;净资产不低于1000 万元的单位&…

Vue3技术7之toRaw与markRaw、customRef、provide与inject、响应式数据的判断、组合式API的优势分析

Vue3技术7 toRaw与markRawtoRawApp.vueDemo.vue markRawDemo.vue 总结 customRefApp.vue总结 provide与inject目录结构App.vueChild.vueSon.vue总结 响应式数据的判断App.vue总结 组合式API的优势配置式的API存在的问题组合式API的优势 toRaw与markRaw toRaw App.vue <te…

HDR tone mapping介绍

文章目录 HDR and tone mapping1.什么是HDR&#xff1f;2.为什么需要HDR&#xff1f;3.hdr文件格式4.tone mapping4.1 aces tone mapping4.2 Fast Bilateral Filtering for the Display of High-Dynamic-Range Images 5 参考 HDR and tone mapping 1.什么是HDR&#xff1f; 就…

nginx + springboot 实现限流

1.spring项目打成jar包后&#xff0c;运行起来 &#xff1a;例如我启动项目 ip 端口号&#xff1a;172.168.0.217:8090 2.修改nginx配置&#xff0c;增加如下配置 nginx 中有两个主要的指令可以用来配置限流&#xff1a;limit_req_zone 和 limit_req upstream myserver{serve…

面试篇:Redis

一、缓存穿透 1、缓存穿透 查询一个不存在的数据&#xff0c;mysql查询不到数据也不会直接写入缓存&#xff0c;就会导致每次请求都查数据库。即&#xff1a;大量请求根本不存在的key 2、查询流程 3、出现原因 业务层误将缓存和库中的数据删除了&#xff0c;也可能是有人恶…

3台服务器+StarVCenter,搭建“超融合云平台”-完美体验-跑100台虚拟机

3台服务器StarVCenter&#xff0c;搭建“超融合云平台”-完美体验-跑100台虚拟机 我们通常讲的“超融合&#xff08;HCI&#xff09;”是一种云平台基础架构方案&#xff0c;它无需专用的存储设备&#xff0c; 每台服务器既承担计算又存储数据&#xff0c; 只需增加服务器&…

SpringMVC-RESTful架构风格

目录 RESTful架构风格 1、RESTful概述 2、RESTful的六大原则 3、RESTful的实现 4、HiddenHttpMethodFilter RESTful风格的CRUD 1、环境搭建 2、功能需求 3、功能&#xff1a;访问首页 4、功能&#xff1a;查询所有数据 5、功能&#xff1a;删除一条数据 6、功能&…

Web服务

安装 WEB 服务&#xff1b; ~ 服务以用户 webuser 系统用户运行&#xff1b; ~ 限制 web 服务只能使用系统 500M 物理内存&#xff1b; ~ 全站点启用 TLS 访问&#xff0c;使用本机上的“CSK Global Root CA”颁 发机构颁发&#xff0c;网站证书信息如下&#xff1a; C…