当前位置: 首页 > news >正文

日常开发记录分享——C#控件ToolTip实现分栏显示内容

文章目录

  • 需求来源
  • 实现思路
  • 实施
  • 请看VCR
  • 等等别走,有优化

需求来源

需要在鼠标浮动到指定位置后提示出详细的信息,一开始使用的tooltip实现,但是里面的内容效果并不理想,需要有条理性,于是就想到能不能将展示的东西分列。

实现思路

使用两个字符串数据接收通过字符串切割后的内容,然后通过在tooltip的draw事件绘制时将内容分为两次绘制。

实施

自定封装一个ToolTip控件,继承ToolTIp然后添加两个事件,分别时Draw Popup
DrawPopup 这两个事件在 ToolTip 类中扮演着重要的角色,用于自定义工具提示的显示和绘制。

Draw 事件在工具提示需要绘制时触发。通过处理这个事件,可以自定义工具提示的外观和内容。

  • 作用

    • 自定义绘制工具提示:在处理 Draw 事件时,可以完全控制工具提示的绘制,包括背景颜色、边框、文本内容和文本样式等。
    • 实现高级图形效果:可以使用 Graphics 对象来实现复杂的绘制效果,比如渐变色、图片、各种形状等。
  • 使用场景

    • 当默认的工具提示外观不能满足需求时,可以通过 Draw 事件自定义绘制工具提示。
    • 需要在工具提示中显示非文本内容(如图像、图表)时,可以在 Draw 事件中实现。

Popup 事件在工具提示显示之前触发。通过处理这个事件,可以动态调整工具提示的大小和内容。

  • 作用

    • 动态调整工具提示大小:在处理 Popup 事件时,可以根据内容的大小动态设置工具提示的尺寸,以确保内容完全显示。
    • 准备绘制环境:可以在 Popup 事件中进行一些准备工作,比如计算文本的最大宽度和高度,为后续的 Draw 事件做准备。
  • 使用场景

    • 需要根据内容动态调整工具提示的大小时,可以在 Popup 事件中进行计算和设置。
    • 需要在工具提示显示前进行一些准备工作,比如加载图片、计算文本尺寸等,可以在 Popup 事件中处理。
using System;
using System.Drawing;
using System.Windows.Forms;namespace Test1
{// 自定义工具提示类,继承自 ToolTippublic class CustomToolTip : ToolTip{private string[] Column1; // 用于存储第一列的文本数组private string[] Column2; // 用于存储第二列的文本数组private Font TextFont; // 工具提示文本的字体// 记录第一列的宽度private int Column1MaxWidth = 0;// 构造函数,初始化自定义工具提示public CustomToolTip(){TextFont = new Font("微软雅黑", 15.0f); // 设置字体为“微软雅黑”,大小为15this.OwnerDraw = true; // 启用自定义绘制工具提示this.Draw += new DrawToolTipEventHandler(OnDraw); // 订阅 Draw 事件this.Popup += new PopupEventHandler(OnPopup); // 订阅 Popup 事件}// 设置工具提示的内容,将其拆分为两列public void SetContent(string content){var parts = content.Split(new string[] { "," }, StringSplitOptions.None); // 按逗号拆分内容int midPoint = (parts.Length + 1) / 2; // 计算拆分成两列的中间点Column1 = new string[midPoint]; // 初始化第一列数组Column2 = new string[parts.Length - midPoint]; // 初始化第二列数组// 填充列数组for (int i = 0; i < parts.Length; i++){if (i < midPoint){Column1[i] = parts[i];}else{Column2[i - midPoint] = parts[i];}}}// 自定义工具提示的绘制事件处理程序private void OnDraw(object sender, DrawToolTipEventArgs e){e.DrawBackground(); // 绘制工具提示的背景e.DrawBorder(); // 绘制工具提示的边框Brush brush = Brushes.Black; // 用于绘制文本的画笔Rectangle rct2 = e.Bounds; // 工具提示的边界e.Graphics.FillRectangle(Brushes.Bisque, rct2); // 用浅橙色填充背景e.Graphics.DrawRectangle(Pens.DarkGray, new Rectangle(0, 0, rct2.Width - 1, rct2.Height - 1)); // 绘制边框// 绘制第一列文本for (int i = 0; i < Column1.Length; i++){e.Graphics.DrawString(Column1[i], TextFont, brush, new PointF(5, i * 25));}// 绘制第二列文本for (int i = 0; i < Column2.Length; i++){e.Graphics.DrawString(Column2[i], TextFont, brush, new PointF(Column1MaxWidth, i * 25));}}// 在工具提示显示之前计算其大小的事件处理程序private void OnPopup(object sender, PopupEventArgs e){int Column2MaxWidth = 0; // 用于存储第二列的最大宽度int maxHeight = 0; // 用于存储工具提示的最大高度// 计算第一列的最大宽度和高度foreach (var text in Column1){var sz = TextRenderer.MeasureText(text, TextFont);if (sz.Width > Column1MaxWidth)Column1MaxWidth = sz.Width;maxHeight += sz.Height;}// 计算第二列的最大宽度foreach (var text in Column2){var sz = TextRenderer.MeasureText(text, TextFont);if (sz.Width > Column2MaxWidth)Column2MaxWidth = sz.Width;}// 确保高度适应两列中较高的一列maxHeight = Math.Max(maxHeight, Column2.Length * TextRenderer.MeasureText("A", TextFont).Height);e.ToolTipSize = new Size(Column1MaxWidth + Column2MaxWidth + 20, maxHeight + 30); // 设置工具提示大小,并添加一些间距}}
}

这里对字符串的分割是根据,来的,根据个人需要修改SetContent方法中切割字符,当然也可以封装一下,这里本人偷懒了。
下面是使用的方式,先在我们窗体中创建一个自定义的Tooltip对象,具体使用就是先设置SetContent方法将要显示的内容传递进去。最后将要tooltip关联的控件对象绑定就行了

  private CustomToolTip custom = new CustomToolTip();private void Form1_Load(object sender, EventArgs e){string aa = $"工作人员姓名:aaa,出勤地点:aaa333344445555555555," +$"工号:aaa,出勤时间:aaa," +$"手机:aaaaaaaa,本站时间:aaa," +$"站名:aaa,工作班制:aaa," +$"当前已工作时间:aaa,班制时长:aaa1111," +$"工作人员所属部门:aaa";custom.SetContent(aa);custom.SetToolTip(button1,aa);//这里传递第二个参数只要是字符串就行,因为在SetContent方法时已经设置好要显示的内容了。}

请看VCR

在这里插入图片描述

等等别走,有优化

鉴于上面我们使用的在From_Load方法中去使用自定义tip时调用SetToolTip时第二个参数传递有些冗余,这里把自定义的tip控件给优化了一下,优化虽小也是进步

using System;
using System.Drawing;
using System.Windows.Forms;namespace Test1
{// 自定义工具提示类,继承自 ToolTippublic class CustomToolTip : ToolTip{private string[] Column1; // 用于存储第一列的文本数组private string[] Column2; // 用于存储第二列的文本数组private Font TextFont; // 工具提示文本的字体priavte Control ParentCtrl;//父窗体控件// 记录第一列的宽度private int Column1MaxWidth = 0;// 构造函数,初始化自定义工具提示public CustomToolTip(){TextFont = new Font("微软雅黑", 15.0f); // 设置字体为“微软雅黑”,大小为15this.OwnerDraw = true; // 启用自定义绘制工具提示this.Draw += new DrawToolTipEventHandler(OnDraw); // 订阅 Draw 事件this.Popup += new PopupEventHandler(OnPopup); // 订阅 Popup 事件}// 设置工具提示的内容,将其拆分为两列private void SetContent(string content){var parts = content.Split(new string[] { "," }, StringSplitOptions.None); // 按逗号拆分内容int midPoint = (parts.Length + 1) / 2; // 计算拆分成两列的中间点Column1 = new string[midPoint]; // 初始化第一列数组Column2 = new string[parts.Length - midPoint]; // 初始化第二列数组// 填充列数组for (int i = 0; i < parts.Length; i++){if (i < midPoint){Column1[i] = parts[i];}else{Column2[i - midPoint] = parts[i];}}}// 自定义工具提示的绘制事件处理程序private void OnDraw(object sender, DrawToolTipEventArgs e){e.DrawBackground(); // 绘制工具提示的背景e.DrawBorder(); // 绘制工具提示的边框Brush brush = Brushes.Black; // 用于绘制文本的画笔Rectangle rct2 = e.Bounds; // 工具提示的边界e.Graphics.FillRectangle(Brushes.Bisque, rct2); // 用浅橙色填充背景e.Graphics.DrawRectangle(Pens.DarkGray, new Rectangle(0, 0, rct2.Width - 1, rct2.Height - 1)); // 绘制边框// 绘制第一列文本for (int i = 0; i < Column1.Length; i++){e.Graphics.DrawString(Column1[i], TextFont, brush, new PointF(5, i * 25));}// 绘制第二列文本for (int i = 0; i < Column2.Length; i++){e.Graphics.DrawString(Column2[i], TextFont, brush, new PointF(Column1MaxWidth, i * 25));}}// 在工具提示显示之前计算其大小的事件处理程序private void OnPopup(object sender, PopupEventArgs e){int Column2MaxWidth = 0; // 用于存储第二列的最大宽度int maxHeight = 0; // 用于存储工具提示的最大高度//设置将文本拆分两个数组,用于后期显示为两列---在这里通过tip控件自带的GetToolTip方法获取提示文本内容然后进行拆分初始化SetContent(this.GetToolTip(ParentCtrl));// 计算第一列的最大宽度和高度foreach (var text in Column1){var sz = TextRenderer.MeasureText(text, TextFont);if (sz.Width > Column1MaxWidth)Column1MaxWidth = sz.Width;maxHeight += sz.Height;}// 计算第二列的最大宽度foreach (var text in Column2){var sz = TextRenderer.MeasureText(text, TextFont);if (sz.Width > Column2MaxWidth)Column2MaxWidth = sz.Width;}// 确保高度适应两列中较高的一列maxHeight = Math.Max(maxHeight, Column2.Length * TextRenderer.MeasureText("A", TextFont).Height);e.ToolTipSize = new Size(Column1MaxWidth + Column2MaxWidth + 20, maxHeight + 30); // 设置工具提示大小,并添加一些间距}}
}
        private CustomToolTip custom ;public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){custom = new CustomToolTip(button1);string aa = $"工作人员姓名:aaa,出勤地点:aaa333344445555555555," +$"工号:aaa,出勤时间:aaa," +$"手机:aaaaaaaa,本站时间:aaa," +$"站名:aaa,工作班制:aaa," +$"当前已工作时间:aaa,班制时长:aaa1111," +$"工作人员所属部门:aaa";custom.SetToolTip(button1,aa);}

相关文章:

日常开发记录分享——C#控件ToolTip实现分栏显示内容

文章目录 需求来源实现思路实施请看VCR等等别走&#xff0c;有优化 需求来源 需要在鼠标浮动到指定位置后提示出详细的信息&#xff0c;一开始使用的tooltip实现&#xff0c;但是里面的内容效果并不理想&#xff0c;需要有条理性&#xff0c;于是就想到能不能将展示的东西分列…...

Kettle下载安装

环境说明 虚拟机&#xff1a;Win7&#xff1b;MySql8.0 主机&#xff1a;Win11&#xff1b;JDK1.8&#xff1b;Kettle 9.4&#xff08;Pentaho Data Integration 9.4&#xff09;&#xff08;下载方式见文末&#xff09; 安装说明 【1】解压后运行Spoon.bat 【2】将jar包 复…...

最新版Golang pprof使用(引入、抓取、分析,图文结合)

最新版Golang pprof使用 &#x1f525;具体实践: Go调试神器pprof使用教程Golang线上内存爆掉问题排查&#xff08;pprof&#xff09; Github地址:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/go-demo/go-pprof 引入pprof:import _ “net/http/pprof” …...

vue3学习记录1:emit的写法

emit是用于child组件向parent组件通信的工具&#xff0c;因为vue3的script可以设置为setup&#xff0c;写法同vue2有较大区别。 一、script setup - 直接写 <script lang"ts" setup>const emit defineEmits([close]);function handleClose() {emit(close);}…...

Visual Studio Code + vue快速安装配置Node.js+Vue+webpack+vscode

第一部分&#xff1a;Node.js 第一步&#xff1a;下载Node.js 方法1&#xff1a;链接 下载 | Node.js 中文网 (nodejs.cn) 方法2&#xff1a;百度网盘 链接&#xff1a;https://pan.baidu.com/s/1zIqu8H9rb_I1i-1OWD7swQ?pwdaurk 提取码&#xff1a;aurk --来自百度网盘…...

【Dart 教程系列第 49 篇】什么是策略设计模式?如何在 Dart 中使用策略设计模式

这是【Dart 教程系列第 49 篇】&#xff0c;如果觉得有用的话&#xff0c;欢迎关注专栏。 博文当前所用 Flutter SDK&#xff1a;3.22.1、Dart SDK&#xff1a;3.4.1 文章目录 一&#xff1a;什么是策略设计模式&#xff1f;二&#xff1a;为什么要使用策略设计模式&#xff1…...

BGP路由反射器

原理概述 缺省情况下&#xff0c;路由器从它的一个 IBGP对等体那里接收到的路由条目不会被该路由器再传递给其他IBGP对等体&#xff0c;这个原则称为BGP水平分割原则&#xff0c;该原则的根本作用是防止 AS内部的BGP路由环路。因此&#xff0c;在AS内部&#xff0c;一般需要每台…...

DolphinDB Web 端权限管理:可视化操作指南

在现代数据库管理中&#xff0c;高效和直观的权限管理对于用户的数据安全是至关重要的。过去 DolphinDB 用户需要依赖系统脚本来管理用户和权限&#xff0c;这对于缺乏技术背景的管理员来说既复杂又容易出错。 为了提升用户体验和操作效率&#xff0c;DolphinDB 目前在 Web 上…...

学习Vue2收藏这一篇就够了(如何创建Vue实例)

什么是Vue&#xff1f; Vue是什么&#xff1a;是一个用于构建用户界面的渐进式框架 什么是构建用户界面&#xff1a;基于数据动态渲染页面 什么是渐进式&#xff1a;循序渐进的学习 什么是框架&#xff1a;一整套完整的项目解决方案 创建Vue实例 核心步骤&#xff08;4步…...

Mysql数据库第四次作业

mysql> create table student(sno int primary key auto_increment,sname varchar(30) not null unique,Ssex varchar(2) check (Ssex男 or Ssex女) not null,Sage int not null,Sdept varchar(10) default计算机 not null); mysql> create table Course(Con int primar…...

使用Docker搭建MySql的主从同步+ShardingSphere搭建Mysql的读写分离

参考课程 尚硅谷ShardingSphere5实战教程&#xff08;快速入门掌握核心&#xff09;_哔哩哔哩_bilibili 主服务器 创建容器 docker run -d \ -p 3306:3306 \ -v /kira/mysql/master/conf:/etc/mysql/conf.d \ -v /kira/mysql/master/data:/var/lib/mysql \ -e MYSQL_ROOT…...

数据结构:数据类型与抽象数据类型

数据类型与抽象数据类型 数据类型基本数据类型构造数据类型指针类型枚举类型 抽象数据类型&#xff08;ADT&#xff09;抽象数据类型的组成部分常见的抽象数据类型示例 数据类型与抽象数据类型的区别实现抽象数据类型的具体方式用数组实现栈用链表实现栈 总结 数据类型 数据类…...

西方逻辑史简介

西方逻辑史研究&#xff0c;对形式逻辑实现现代化&#xff0c;对加强西方哲学史研究&#xff0c;对开展科学方法论的研究都有重要意义。西方逻辑史一般被划分成古代、中世纪、现代三个历史时期。本文拟对这三个时期中的七个重要逻辑学家和逻辑学派&#xff1a;亚里士多德、斯多…...

【论文10】复现代码tips

一、准备工作 1.创建一个虚拟环境 conda create --name drgcnn38 python=3.8.18 2.激活虚拟环境 conda activate drgcnn38 注意事项 在Pycharm中终端(terminal)显示PS而不是虚拟环境base 问题如下所示 解决方法:shell路径改成cmd.exe 重启终端显示虚拟环境 3.安装torch …...

分布式缓存获取以及设置

1. 通用代码 public SysUser getCache(String sysUserId) {String cacheKey "litgery:warehouse:" sysUserId;// 尝试从缓存中获取数据CacheData cacheData redisUtils.get(cacheKey);if (null ! cacheData) {if (Boolean.TRUE.equals(cacheData.getExist())) {re…...

SMO算法,platt论文的原始算法及优化算法

platt论文&#xff1a;[PDF] Sequential Minimal Optimization : A Fast Algorithm for Training Support Vector Machines | Semantic Scholar 算法优化&#xff1a;[PDF] Improvements to Platts SMO Algorithm for SVM Classifier Design | Semantic Scholar 包含个人plat…...

2.3 openCv -- 对矩阵执行掩码操作

在矩阵上进行掩模操作相当简单。其基本思想是根据一个掩模矩阵(也称为核)来重新计算图像中每个像素的值。这个掩模矩阵包含的值决定了邻近像素(以及当前像素本身)对新的像素值产生多少影响。从数学角度来看,我们使用指定的值来做一个加权平均。 具体而言,掩模操作通常涉…...

【Django】 js实现动态赋值、显示show隐藏hide效果

文章目录 需要达到的前端效果预览&#xff1a;实现步骤复制bootstrp代码&#xff08;buttons&#xff09;复制bootstrp代码&#xff08;Alert警告框&#xff09;写js测试效果 需要达到的前端效果预览&#xff1a; {% load static %} <!DOCTYPE html> <html lang"…...

qt--做一个拷贝文件器

一、项目要求 使用线程完善文件拷贝器的操作 主窗口不能假死主窗口进度条必须能动改写文件大小的单位&#xff08;自适应&#xff09; 1TB1024GB 1GB1024MB 1MB1024KB 1KB1024字节 二、所需技术 1.QFileDialog 文件对话框 QFileDialog也继承了QDialog类&#xff0c;直接使用静态…...

Eclipse 搭建 C/C++ 开发环境以及eclipse的使用

一、下载、安装 MinGW 1、下载: 下载地址&#xff1a;MinGW - Minimalist GNU for Windows - Browse Files at SourceForge.net 点击“Download Latest Version”即可 下载完成后&#xff0c;得到一个名为 mingw-get-setup.exe 的安装文件。双击运行&#xff0c;安装即可。 …...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么&#xff1f;它的作用是什么&#xff1f; Spring框架的核心容器是IoC&#xff08;控制反转&#xff09;容器。它的主要作用是管理对…...

上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式

简介 在我的 QT/C 开发工作中&#xff0c;合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式&#xff1a;工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...