直接使用 Cursors 静态类(如 Cursors.WaitCursor、Cursors.Hand)可快速切换系统指针,无需加载文件或手动释放;自定义指针须用 Stream 构造 Cursor 实例并显式 Dispose;控件 Cursor 属性仅作用于自身区域,且受父容器、鼠标捕获及模态对话框影响。

怎么用 Cursors 类快速切换系统内置指针
系统自带的指针样式(如箭头、等待、手型)直接用 Cursors 静态类就能换,不用加载文件,也不涉及资源释放问题。
常见错误是以为要手动 new 一个 Cursor 实例再赋值给控件——其实多数场景直接赋 Cursors.WaitCursor 就行。
-
Button.Cursor = Cursors.Hand:按钮悬停时显示手型,比 CSS 方式更可靠(尤其 WinForms 高 DPI 下) - 在耗时操作前设
this.Cursor = Cursors.WaitCursor,结束后务必还原,否则指针会卡死在沙漏状态 - 不要在子线程里改
Cursor,会抛InvalidOperationException;必须用Invoke回 UI 线程
如何从 .cur 或 .ani 文件加载自定义指针
自定义指针必须用 Cursor 构造函数配合 Stream,不能直接传路径字符串——这是最常踩的坑。
典型错误:new Cursor("loading.ani") 会报 ArgumentException,因为构造函数只接受流或句柄。
- 用
File.OpenRead("loading.ani")获取流,再传给new Cursor(stream) - .ani 动画光标只在 Windows 支持,.cur 是通用格式;.png/.bmp 不行,哪怕重命名也不生效
- 加载后必须自己调用
cursor.Dispose(),否则资源泄漏(Cursors类里的静态实例除外) - 如果指针尺寸不是 32×32 或 64×64,在高 DPI 屏幕上可能模糊或偏移
为什么设置 Control.Cursor 有时不生效
控件的 Cursor 属性只在它自身区域生效,且会被父容器或鼠标捕获状态覆盖。
比如 Panel 里放了 TextBox,你给 Panel 设了手型指针,但鼠标移到 TextBox 上就变回 I 型——因为 TextBox 自己控制了 Cursor。
- 想全局生效?改窗体级
this.Cursor,但注意它会被子控件显式设置覆盖 - 拖拽过程中(
DoDragDrop)或模态对话框弹出时,系统会强制接管指针,代码设置无效 - WPF 用户别试这个——WinForms 的
Cursor和 WPF 的Mouse.OverrideCursor完全不兼容
资源释放和多显示器 DPI 适配要点
自定义 Cursor 对象不是托管资源,using 块或析构函数都救不了它——忘了 Dispose,下次加载同名文件可能失败,甚至导致 GDI 句柄耗尽。
DPI 适配容易被忽略:系统会自动缩放 Cursors.Default,但你用 new Cursor(stream) 加载的不会,得自己按 Graphics.DpiX 缩放图像再重建。
- 推荐做法:把不同 DPI 的 .cur 分开存放(如
loading_96.cur、loading_144.cur),运行时选对应文件加载 - 卸载指针前先检查是否为 null,避免重复
Dispose抛ObjectDisposedException - 调试时留意任务管理器的“GDI 对象”数,持续上涨基本就是
Cursor没释放


















