在 WinForms 控件上绘制






4.86/5 (27投票s)
使用 Graphics 对象在窗体控件的顶部绘制几乎任何东西
引言
GraphicalOverlay
组件在窗体控件的顶部进行绘制。它本质上就像在窗体上铺了一层玻璃,然后在玻璃上进行绘制。我使用这个组件在控件之间绘制线条,以帮助用户理解 UI 中复杂的关联。我很少使用它,但有时你只需要一个大红箭头来表示:这会影响那个!如果做得好,效果会相当优雅。
Using the Code
要使用 GraphicalOverlay
组件,请按照以下步骤操作:
- 将以下两个文件复制到项目的目录中:GraphicalOverlay.cs、GraphicalOverlay.designer.cs。
- 将 GraphicalOverlay.cs 添加到你的项目中。Visual Studio 会自动包含 GraphicalOverlay.designer.cs。
编译项目。这将在 Visual Studio 工具箱中创建一个
GraphicalOverlay
组件。 - 将
GraphicalOverlay
组件从工具箱拖放到窗体设计器表面。该组件将自动命名为graphicalOverlay1
。 - 在你的窗体中,为
graphicalOverlay1
创建一个 paint 事件处理程序(graphicalOverlay1_Paint()
)。 - 在你的窗体构造函数中,在
InitializeComponents();
行之后,添加以下行:public Form1() { InitializeComponent(); graphicalOverlay1.Owner = this; }
- 在
graphicalOverlay1_Paint()
(参见步骤 5)中,使用e.Graphics
以窗体相对坐标绘制任何你想要的东西。private void graphicalOverlay1_Paint(object sender, PaintEventArgs e) { // This event will fire for the form and each control on the form. // The graphical overlay component will have already transformed the // graphics object to use the form's coordinate system, // so no control-specific calculations are required. Rectangle rect = this.ClientRectangle; rect.Inflate(-10, -10); using(Pen pen = new Pen(Color.Red, 5)) e.Graphics.DrawEllipse(pen, rect); using(Font font = new Font("Arial", 14)) e.Graphics.DrawString("Now is the time.", font, Brushes.Green, 60, 110); [...] }
- 要绘制控件相对图形,请使用
Coordinates()
方法获取控件的窗体相对坐标。请参阅演示代码中的Form1.graphicalOverlay1_Paint()
事件处理程序以获取示例。private void graphicalOverlay1_Paint(object sender, PaintEventArgs e) { [...] // To draw relative to a control, use the Coordinates method. using (Pen pen = new Pen(Color.Blue, 3)) e.Graphics.DrawEllipse(pen, pictureBox1.Coordinates()); }
关注点
该组件包含以下事件处理程序:
Form_Resize
Control_Paint
当设置了 graphicalOverlay1.Owner
属性后,该组件会将它的 Form_Resize
事件处理程序连接到所有者窗体的 Resize
事件。然后,该组件将其 Control_Paint
事件处理程序附加到所有者窗体(包括所有者窗体本身)的每个控件的 Paint
事件上。
当每个控件被重绘时,组件会处理 Paint
事件,转换 e.Graphics
对象的坐标,然后触发它自己的 Paint
事件,该事件将被窗体的 graphicalOverlay1_Paint
事件处理程序处理。
由于传递给 graphicalOverlay1_Paint()
的 e.Graphics
对象已被转换为使用窗体的坐标系,因此所有绘图逻辑都是窗体相对的。就像在窗体的客户区内绘图一样即可。
然而,这种方法使得相对于控件进行绘图变得困难。因此,为了使控件相对绘图更容易,我在 System.Windows.Forms.Control
类中添加了一个名为 Coordinates()
的扩展方法。Coordinates()
方法将控件的位置转换为窗体相对坐标。只需使用 Coordinates(control)
而不是 control.Location
进行绘图即可。
由于图形叠加层可以绘制在窗体的所有控件之上,因此重绘它需要使整个窗体失效。调用组件的 Invalidate()
方法将使窗体及其每个控件失效。
限制
该组件响应每个控件的 Paint
事件。TextBox
控件不触发 Paint
事件,因此该组件无法在 TextBox
控件上进行绘制。可以使用第三方文本框控件,但我没有测试过任何一个。
该组件只能在控件的客户区域上进行绘制。某些控件包含不属于其客户区域的边框,因此该组件无法绘制在边框上。解决此问题的一个方法是关闭控件的边框,然后使用该组件进行绘制。
该组件只能在窗体的客户区域内进行绘制。它无法绘制在窗体的标题栏或边框上,也无法绘制在窗体之间。要模拟在窗体边框上绘制,你需要关闭窗体的边框并使用该组件进行绘制,并重新实现所有丢失的功能。
演示
我包含了两个演示窗体,以帮助你了解如何实现自己的 graphicalOverlay1_Paint
事件处理程序。
Form1.cs 是带有带有红色和蓝色圆环以及绿色文本的图片的那个。
注意:当 Form1
演示运行时,请几次调整窗体大小。
Form2.cs 是带有分组框和单选按钮的演示。
注意:要切换 Form1
和 Form2
演示,你必须修改 Program.cs 文件以运行 Form1
或 Form2
。默认情况下,演示将运行 Form2
。
在 Form2
演示中,graphicalOverlay1_Paint()
中的代码包含大量硬编码值。我可以计算这些值,但这只会使代码更难阅读。如果你的 UI 文化与我不同,或者你的字体配置与我不同,这些硬编码值可能无法完全正常工作。文章开头的截图将显示它们应该是什么样子。