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

news2024/11/27 18:41:24

文章目录

  • 需求来源
  • 实现思路
  • 实施
  • 请看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
{
    // 自定义工具提示类,继承自 ToolTip
    public class CustomToolTip : ToolTip
    {
        private string[] Column1; // 用于存储第一列的文本数组
        private string[] Column2; // 用于存储第二列的文本数组
        private Font TextFont; // 工具提示文本的字体
        // 记录第一列的宽度
        private int Column1MaxWidth = 0;
		
        // 构造函数,初始化自定义工具提示
        public CustomToolTip()
        {
            TextFont = new Font("微软雅黑", 15.0f); // 设置字体为“微软雅黑”,大小为15
            this.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
{
    // 自定义工具提示类,继承自 ToolTip
    public 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); // 设置字体为“微软雅黑”,大小为15
            this.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);
        }

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

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

相关文章

邮件推送API如何集成到现有系统发送邮件?

邮件推送API安全性策略&#xff1f;如何选择邮件推送API服务商&#xff1f; 在当今数字化时代&#xff0c;邮件通信是企业和个人交流的重要方式之一。集成邮件推送API到现有系统可以大大提升通信效率和自动化程度。AokSend将介绍如何将邮件推送API集成到现有系统中&#xff0c…

关于P2P(点对点)

P2P 是一种客户端与客户端之间&#xff0c;点对点连接的技术&#xff0c;在早前的客户端都是公网IP&#xff0c;没有NAT的情况下&#xff0c;P2P是较为容易实现的。 但现在的P2P&#xff0c;实现上面会略微有一些复杂&#xff1a;需要采取UDP打洞的技术&#xff0c;但UDP打出来…

自动控制: 时间最优的PID控制算法

自动控制&#xff1a; 时间最优的PID控制算法 在计算机控制系统中&#xff0c;时间最优控制旨在使系统从一个初始状态转到另一个目标状态所经历的过渡时间最短。利用最大值原理&#xff0c;可以设计出控制量只在 u ( t ) ≤ 1 u(t) \leq 1 u(t)≤1范围内取值的时间最优控制系…

(39)智能电池

文章目录 前言 1 通过任务规划器进行设置 2 补充信息 3 限制条件 4 参数说明 前言 虽然还不是很普遍&#xff0c;但智能电池更容易从飞行器上安装和拆卸&#xff0c;并且能够提供更多关于电池状态的信息&#xff0c;包括容量、单个电池电压、温度等。 ArduPilot 支持几种…

【分布式系统】 单机架构 | 分布式架构 | 集群 | 主从架构 | 分库分表 | 冷热分离 | 微服务

文章目录 [toc] 分布式系统一、单机架构二、分布式系统三、应用服务器集群四、读写分离 / 主从分离架构五、引入缓存/冷热分离架构六、垂直分库七、微服务架构——业务拆分代价优势 八、名词解释1.应用&#xff08;Application&#xff09;/系统(System)2.模块&#xff08;Mode…

解决“QtCreator无法呼出搜狗输入法“问题

由于在Ubuntu系统上&#xff0c;QtCreator软件默认使用IBus类型的输入法&#xff0c;而搜狗输入法是fcitx类型的&#xff0c;所以需要在Linux的系统设置 -->区域与语言 里 -->勾选 fcitx类型&#xff0c;如图(1)所示。     这里以QtCreator 4.5.2Ubuntu 18为例&#xf…

学习测试14-实战3-复习-使用CANoe打开半成品

数据 链接: https://pan.baidu.com/s/1k0SFq0luDvEbqimFgtfyKg?pwd9a5t 提取码: 9a5t 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 1&#xff0c;导入信号、报文、节点 2&#xff0c;导入数据库 3&#xff0c;导入can代码 4&#xff0c;导入环境变量 5&#x…

RTP协议基础

概述 1. 基本概念 RTP协议&#xff0c;全称为Real-time Transport Protocol&#xff08;实时传输协议&#xff09;是一种用于在IP网络上传输音频、视频等实时数据的网络协议。 在流媒体&#xff08;流媒体就是指可在线/实时观看音视频的互联网产品&#xff09;数据传输过程中&…

抄作业-跟着《React通关秘籍》捣鼓React-playground-上集

文章目录 前言1. 搭建react 开发环境2、react hooks 知识3. 目标&#xff1a;跟着小册实现 react-playground3.1 整体布局初始化项目使用Alloment 来实现左右分屏的拖拉功能 3.2 代码编辑器Monaco Editor 3.3 实现了多文件的切换用 useContext 来共享数据。优化 tab的样式&…

Vue响应式的原理

一. Vue响应式原理的核心概念 1. Vue响应式原理基于以下核心概念&#xff1a; ① 响应式对象&#xff1a;Vue使用Object.defineProperty()来 reactive&#xff08;反应&#xff09;对象中的属性&#xff0c;使其变化可以被检测。 注意&#xff1a; ★ Object.definePropert…

Python字符串处理技巧:一个小技巧竟然能省下你一半时间!

获取Pyhon及副业知识&#xff0c;关注公众号【软件测试圈】 效率翻倍的秘密&#xff1a;Python字符串操作的5个惊人技巧 在Python编程中&#xff0c;字符串处理在数据分析、Web开发、自动化脚本等多个领域都有广泛应用。Python提供了一系列强大的字符串处理函数&#xff0c;能够…

蚓链数字化生态平台:构建城市智能商业,引领协同发展新潮流

​在当今数字化飞速发展的时代&#xff0c;城市商业的运行模式正在经历着数字化变革。蚓链数字化生态平台应运而生&#xff0c;以其强大的功能和创新的理念&#xff0c;成为构建城市智能商业枢纽中心的关键力量&#xff0c;推动着平台互通、业务贯通、管理协同的全新发展格局。…

MySQL数据库-索引和视图

一、视图 1.什么是视图 MySQL中的视图&#xff08;view&#xff09;是一种虚拟表&#xff0c;其内容由查询定义&#xff0c;视图本身并不包含数据。视图看起来和真实的表完全相同&#xff0c;但其中的数据来自定义视图时用到的基本表&#xff0c;并且在打开视图时动态生成&am…

【JavaWeb项目】——外卖订餐系统之登入、登入后显示餐品信息、用户注册、注销部分

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大二学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…

什么是内网ip地址?如何查询电脑内网ip地址

在数字化时代&#xff0c;互联网已经成为我们日常生活和工作中不可或缺的一部分。无论是家庭网络还是企业办公环境&#xff0c;每台接入网络的设备都需要一个独特的标识来区分彼此&#xff0c;这个标识就是IP地址。IP地址全称为“互联网协议地址”&#xff0c;是设备在网络中的…

springboot整合junit-用于测试用例

package impl;public interface BookDao {public void save(); }第一步&#xff1a;打开软件&#xff0c;点击file&#xff0c;点击new 然后选择module&#xff0c;在右侧选择springboot 第二步&#xff1a;选择配置和JDK以及java版本 ①选择maven类型 ②选择JDK1.8版本 ③选…

react中路由懒加载

// 1.引入方法&#xff0c;用于创建路由实例 // createBrowserRouter是用于创建history模式 // createHashRouter是用于创建hash模式 // 路由模式的切换只需要更改创建路由实例的方法就行了&#xff0c;其他地方不需要更改 import { createBrowserRouter,createHashRouter } fr…

deployment

一.deployment rc和rs控制器都是控制pod的副本数量的&#xff0c;但是&#xff0c;他们两个有个缺点&#xff0c;就是在部署新版本pod或者回滚代码的时候&#xff0c;需要先apply资源清单&#xff0c;然后再删除现有pod&#xff0c;通过资源控制&#xff0c;重新拉取新的pod来实…

【Vue3复习】Vite创建项目报错解决

报错&#xff1a; Cannot find package ‘vite’ 出错原因分析 使用命令npm create vuelatest创建项目时&#xff0c;没有按顺序执行以下提示命令 解决 依序执行这三条指令 注意 如果没有执行 npm install 这条指令&#xff0c;直接用VS Code打开项目时&#xff0c;env.d.…

职场英语培训柯桥成人学外语|邮件里别乱用“Dear”,老外才不会这么写!

写邮件是职场上的必修课&#xff0c;而一封好的英语邮件应当从适合的称呼开始。 说到称呼&#xff0c;你的脑子里是不是冒出了一个单词“Dear” 从小我们就被教育写英语作文时&#xff0c;开头先来个Dear&#xff0c;结尾写个Sincerely&#xff0c;简直不要太顺手~ 然&#xff…