分享一个鼠标按下就能画线的绘图窗体VBA
大家好这里是晚间有雨伴人眠。之前咱们聊过怎么创建自定义对话框窗口也聊过怎么让窗口显示在屏幕正中间。今天再分享一个新玩法——在窗体上画线绘图。这个功能说大不大但用对了场景还挺实用的。比如你可以把它做成一个简易的批注工具在表格数据上画圈标记或者做一个签名板让用户在窗体上手写签名甚至只是给演示文稿加个临时的标注图层都行。效果很简单鼠标左键按住拖动就能在窗体上画出一条连续的黑色线条松开就停止绘制。先看效果再说实现。一、核心思路在Windows里画线用的是GDI图形设备接口。这套接口里有两个最基础的函数MoveToEx 把“画笔”移动到某个坐标LineTo 从当前位置画一条线到目标坐标所以整个绘图的逻辑就是鼠标按下 → 记录起始位置 → 鼠标移动 → 不断从上一个位置画到当前位置 → 鼠标松开 → 停止绘制说白了就是一个连续画线的过程和你在Windows自带的画图软件里用铅笔工具画画是完全一样的道理。二、代码解析完整代码你直接复制就能用这里我挑几个关键点简单说一下方便你理解原理。1. 窗体事件的配合代码里用了三个鼠标事件UserForm_MouseDown 左键按下时设置一个“正在绘制”标记UserForm_MouseUp 左键松开时清除这个标记UserForm_MouseMove 鼠标移动时如果标记为真就执行画线这三个事件一配合就实现了“按下画、松开停”的效果。2. 坐标转换这里有个细节需要注意 GetCursorPos 获取的是鼠标在屏幕上的坐标但画线需要的是在窗体客户区内的坐标所以必须用 ScreenToClient 做一次转换否则画出来的位置就全偏移了。代码里是这样处理的GetCursorPos prpt 获取屏幕坐标 ScreenToClient Uf.Form, prpt 转换成窗体坐标3. 画笔的创建与释放在窗体初始化时创建画笔Uf.Penr CreatePen(PS_SOLID, 1, RGB(0, 0, 0))这里创建了黑色、1像素宽的实线画笔。想要换颜色或粗细改这里就行RGB(0, 0, 0) 改成 RGB(255,0,0) 就是红色中间那个1改成3就是3像素宽窗体关闭时记得释放画笔和设备上下文避免内存泄漏DeleteObject Uf.Penr ReleaseDC Uf.Form, Uf.Fhdc三、可以玩的花样这个基础版本还可以继续扩展给你几个思路换颜色加几个按钮点击后修改画笔颜色调整粗细加一个滚动条控制画笔宽度清空画布加一个“清除”按钮重绘窗体画直线/矩形把自由画线改成从起点到终点的规则图形这些都是在这个基础上顺理成章的扩展有兴趣的话可以自己动手试试。四、完整代码新建一个UserForm把下面的代码全部粘贴进去直接运行就能看到效果。晚间有雨伴人眠202607161122 #If VBA7 And Win64 Then Private Declare PtrSafe Function LineTo Lib gdi32 (ByVal hdc As LongPtr, ByVal X As Long, ByVal Y As Long) As Long Private Declare PtrSafe Function MoveToEx Lib gdi32 (ByVal hdc As LongPtr, ByVal X As Long, ByVal Y As Long, lpPoint As POINTAPI) As Long Private Declare PtrSafe Function CreatePen Lib gdi32 (ByVal nPenStyle As Long, ByVal nWidth As Long, ByVal crColor As Long) As LongPtr Private Declare PtrSafe Function DeleteObject Lib gdi32 (ByVal hObject As LongPtr) As Long Private Declare PtrSafe Function GetDC Lib user32 (ByVal hwnd As LongPtr) As LongPtr Private Declare PtrSafe Function ReleaseDC Lib user32 (ByVal hwnd As LongPtr, ByVal hdc As LongPtr) As Long Private Declare PtrSafe Function FindWindowA Lib user32 (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr Private Declare PtrSafe Function GetCursorPos Lib user32 (lpPoint As POINTAPI) As Long Private Declare PtrSafe Function ScreenToClient Lib user32 (ByVal hwnd As LongPtr, lpPoint As POINTAPI) As Long #Else Private Declare Function LineTo Lib gdi32 Alias LineTo (ByVal hdc As Long, ByVal x As Long, ByVal y As Long) As Long Private Declare Function MoveToEx Lib gdi32 Alias MoveToEx (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, lpPoint As POINTAPI) As Long Private Declare Function CreatePen Lib gdi32 Alias CreatePen (ByVal nPenStyle As Long, ByVal nWidth As Long, ByVal crColor As Long) As Long Private Declare Function DeleteObject Lib gdi32 Alias DeleteObject (ByVal hObject As Long) As Long Private Declare Function GetDC Lib user32 Alias GetDC (ByVal hwnd As Long) As Long Private Declare Function ReleaseDC Lib user32 Alias ReleaseDC (ByVal hwnd As Long, ByVal hdc As Long) As Long Private Declare Function FindWindowA Lib user32 Alias FindWindowA (ByVal lpClassName As String, ByVal lpWindowName As String) As Long Private Declare Function GetCursorPos Lib user32 Alias GetCursorPos (lpPoint As POINTAPI) As Long Private Declare Function ScreenToClient Lib user32 Alias ScreenToClient (ByVal hwnd As Long, lpPoint As POINTAPI) As Long #End If #If VBA7 And Win64 Then Private Type POINTAPI X As Long Y As Long End Type Private Type UserForm1Type Form As LongPtr Fhdc As LongPtr Penr As LongPtr GotoLine(1) As Boolean End Type #Else Private Type POINTAPI X As Long Y As Long End Type Private Type UserForm1Type Form As Long Fhdc As Long Penr As Long GotoLine(1) As Boolean End Type #End If Private Const PS_SOLID 0 Private Uf As UserForm1Type Private Sub UserForm_Initialize() Uf.Form FindWindowA(IIf(Application.Version 9, ThunderXFrame, ThunderDFrame), Me.Caption) Uf.Penr CreatePen(PS_SOLID, 1, RGB(0, 0, 0)) Uf.Fhdc GetDC(Uf.Form) End Sub Private Sub UserForm_Terminate() DeleteObject Uf.Penr ReleaseDC Uf.Form, Uf.Fhdc Dim EmptyUf As UserForm1Type: Uf EmptyUf End Sub Private Sub UserForm_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single) If Button 1 Then Uf.GotoLine(0) True: Uf.GotoLine(1) True End Sub Private Sub UserForm_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single) If Button 1 Then Uf.GotoLine(0) False End Sub Private Sub UserForm_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single) If Uf.GotoLine(0) Then Dim prpt As POINTAPI GetCursorPos prpt ScreenToClient Uf.Form, prpt If Uf.GotoLine(1) Then MoveToEx Uf.Fhdc, prpt.X, prpt.Y, prpt Uf.GotoLine(1) False Else LineTo Uf.Fhdc, prpt.X, prpt.Y End If End If End Sub五、写到最后GDI绘图是Windows编程里非常基础的一块VBA通过API调用也能玩起来。它的学习曲线不算陡但掌握了之后能做的事情比想象中多得多——从简单的画线画图到自定义控件的外观都能派上用场。代码不长但“按下拖动画线”这个交互是很多绘图工具的基石。如果你刚好需要一个轻量级的标注或签名功能这份代码改改就能用。你平时在VBA里有没有遇到过需要在窗体上画图的需求或者想在这份代码基础上增加什么功能欢迎在评论区聊聊。

相关新闻

OpenClaw 合规公开数据采集入门:合法边界、数据源选型与反爬规避实操指南

OpenClaw 合规公开数据采集入门:合法边界、数据源选型与反爬规避实操指南

一、引言:为什么我们需要一套合规的数据采集框架在大模型训练、市场竞品分析、舆情监控以及学术研究等多个领域,公开数据的获取已经从“锦上添花”变成了“刚性需求”。然而,随着全球数据隐私保护法规(如 GDPR、CCPA、中国的《个人…

2026/7/18 23:31:02阅读更多 →
编译原理硬核——编译器核心概念的全景速览(七)

编译原理硬核——编译器核心概念的全景速览(七)

1. 定位导航 用一个微型翻译器预演了编译器前端的全部核心思想。四篇笔记的关系: #4  形式⏟文法说"什么合法"  →  #5  含义⏟属性说"翻成什么"  →  #6  实现⏟代码把两者跑起来  →  #7  全景⏟本篇\underbrace{\#4\;\text{形式…

2026/7/18 23:29:02阅读更多 →
【Web安全】Portswigger 业务逻辑漏洞 Lab: Inconsistent handling of exceptional input 实验:特殊输入处理不一致

【Web安全】Portswigger 业务逻辑漏洞 Lab: Inconsistent handling of exceptional input 实验:特殊输入处理不一致

目录 一.实验环境 二.特殊输入处理不一致 ​编辑漏洞分析 注册用户 漏洞利用 三.小结 一.实验环境 实验室:特殊输入处理不一致 |网络安全学院 https://portswigger.net/web-security/logic-flaws/examples/lab-logic-flaws-inconsistent-handling-of-except…

2026/7/18 23:29:02阅读更多 →
C# WinForms登录窗口实现与安全实践

C# WinForms登录窗口实现与安全实践

1. 登录窗口的基本实现思路在C# WinForms中创建一个登录窗口,本质上是在构建一个能够验证用户身份的表单界面。这个看似简单的功能背后,其实涉及几个关键设计决策:首先需要考虑的是应用程序的入口点设计。根据我多年的开发经验,主…

2026/7/19 3:59:32阅读更多 →
零基础玩转bWAPP靶场(九):PHP 代码注入

零基础玩转bWAPP靶场(九):PHP 代码注入

摘要:本文是 bWAPP 靶场系列的第九篇,聚焦于 PHP Code Injection(PHP 代码注入)漏洞。文章从零基础角度出发,首先讲解什么是 PHP 代码注入、它和 OS 命令注入的本质区别、为什么 eval() 函数如此危险。随后按照 Low、M…

2026/7/19 3:59:32阅读更多 →
Python装饰器原理与实战:从基础到高级应用

Python装饰器原理与实战:从基础到高级应用

1. Python装饰器:从入门到精通在Python开发中,装饰器(Decorator)是一个强大而优雅的特性,它允许我们在不修改原函数代码的情况下,动态地扩展函数的功能。我第一次接触装饰器时,就被它的简洁和强…

2026/7/19 3:59:32阅读更多 →
Python登录系统开发:从基础到工业级安全实践

Python登录系统开发:从基础到工业级安全实践

1. 从零构建Python登录系统的核心逻辑登录系统作为每个开发者都会接触的基础功能,远比表面看起来复杂。我在多个商业项目中实现过登录模块,发现教科书示例和实际生产环境之间存在巨大鸿沟。让我们从《Python核心编程》第7章的基础示例出发,逐…

2026/7/19 3:59:32阅读更多 →
Zerto 10.0 Update 7:企业级灾备与勒索软件防护新方案

Zerto 10.0 Update 7:企业级灾备与勒索软件防护新方案

1. Zerto 10.0 Update 7核心价值解析Zerto 10.0 Update 7作为企业级灾备解决方案的最新迭代版本,其核心价值在于通过单一平台实现跨本地数据中心、混合云和多云环境的统一数据保护。不同于传统备份方案依赖定时快照的方式,Zerto采用基于日志的持续数据保…

2026/7/19 3:59:32阅读更多 →
Flask框架核心特性与Python Web开发实践

Flask框架核心特性与Python Web开发实践

1. Flask框架概述与核心特性Flask是一个基于Python的轻量级Web应用框架,采用WSGI工具集Werkzeug和模板引擎Jinja2构建。作为典型的"微框架",Flask的核心代码仅约2000行,却提供了构建现代Web应用所需的基础组件。我在实际项目中发现…

2026/7/19 3:57:32阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/19 0:01:04阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/19 0:01:04阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/19 0:01:04阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/19 0:01:04阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/19 0:01:04阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/19 0:01:04阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/18 22:49:46阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/18 14:49:24阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/18 18:49:35阅读更多 →