分享一个鼠标按下就能画线的绘图窗体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/10/9 17:51:12 阅读更多 →
使用 C# 将 HTML 转 Word 或者 Word 转 HTML

使用 C# 将 HTML 转 Word 或者 Word 转 HTML

清理收藏夹。HTML作为Web内容的标准格式,与Word这种通用的办公文档格式之间的相互转换,是许多企业级应用中的常见需求。无论是将数据库中的动态内容生成为Word报告,还是将现有的Word文档发布为网页,掌握高效的转换方法都能显著提升…

2026/10/14 15:10:41 阅读更多 →
EMB3531-RK3399工控机救砖!进maskrom模式

EMB3531-RK3399工控机救砖!进maskrom模式

参考下恩山的https://www.right.com.cn/forum/thread-8482116-1-1.html文档

2026/10/13 21:31:35 阅读更多 →

最新新闻

UK-5099在肿瘤代谢与线粒体功能研究中的应用指南

UK-5099在肿瘤代谢与线粒体功能研究中的应用指南

1. 项目概述UK-5099作为一种线粒体丙酮酸载体(MPC)抑制剂,近年来在肿瘤代谢研究和线粒体功能调控领域引起了广泛关注。这个实验指南旨在为科研人员提供从基础原理到实操细节的完整解决方案,帮助研究者快速掌握UK-5099在肿瘤微环境…

2026/10/14 16:07:13 阅读更多 →
工厂厂区电子地图定位导航:人员车辆实时定位与监控

工厂厂区电子地图定位导航:人员车辆实时定位与监控

如果您正在做智慧园区、医院、商场、厂区人/车定位导航、停车场、会展等场景的室内外地图及导航项目,想了解可快速落地的高性价比、低成本地图应用、集成方案与工期等。欢迎访问上海懒图科技官网:lazymap。com您可以在官网获取行业完整案例演示视频、技术…

2026/10/14 16:07:13 阅读更多 →
金属疲劳强度理论与工程实践全解析

金属疲劳强度理论与工程实践全解析

1. 疲劳强度理论的核心概念金属材料在循环载荷作用下的失效行为一直是工程实践中的关键问题。我十五年前参与某型航空发动机叶片研发时,曾亲眼见证过因疲劳强度计算偏差导致的试验件提前断裂。那次教训让我深刻理解到,掌握疲劳强度理论不是纸上谈兵&…

2026/10/14 16:07:13 阅读更多 →
工业协议仿真平台:28种协议+133设备模板+场景编排

工业协议仿真平台:28种协议+133设备模板+场景编排

1. 项目概述:这不是一个“玩具”,而是一套能直接塞进产线调试包里的工业级联调工具你有没有遇到过这种场景:刚接手一个新产线的IoT改造项目,客户现场摆着七八种不同品牌的PLC、温控器、电表和传感器——西门子S7-1200用的是S7协议…

2026/10/14 16:07:13 阅读更多 →
无缝掌控:iPhone远程操作安卓手机,打造独立安全空间

无缝掌控:iPhone远程操作安卓手机,打造独立安全空间

在这个数字化裸奔的时代,打工人的手机简直比老板的脸还忙。白天它是工作利器,微信群里回复“收到”的速度决定了年终奖的厚度;晚上它又成了生活垃圾桶,塞满了各种不想让人知道的秘密。想象一下,你正开着重要的视频会议…

2026/10/14 16:07:13 阅读更多 →
shadcn-dashboard-landing-template部署指南:Vercel、Netlify与静态托管方案对比

shadcn-dashboard-landing-template部署指南:Vercel、Netlify与静态托管方案对比

【免费下载链接】shadcn-dashboard-landing-template Accelerate your development with a sleek, open-source admin dashboard and landing page built on Vite-React, Next.js, Tailwind CSS, and Shadcn/UI which is fully customizable and production-ready. 项目地址&am…

2026/10/14 16:06:12 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →