本文内容
- 控件提供的绘图
- GDI+ 基础知识
- 绘图区域的几何图形
- 释放图形资源
控件的自定义绘制是 Windows 窗体可以轻松完成的众多复杂任务之一。 创作自定义控件时,有许多选项可用于处理控件的图形外观。 如果要创作自定义控件
(即从 Control 继承的控件),则必须提供代码以呈现其图形表示形式。
如果要创建
复合控件(即从 UserControl 继承的控件或某个
现有的 Windows 窗体控件),则可以替代标准图形表示形式,并提供你自己的图形代码。
如果要在不创建新控件的情况下为现有控件提供自定义呈现,选项会变得更为有限。 但是,对于控件和应用程序,仍有各种各样的图形。
控件呈现涉及以下元素:
- 基类 System.Windows.Forms.Control 提供的绘图功能。
- GDI 图形库的基本元素。
- 绘图区域的几何图形。
- 释放图形资源的过程。
1、控件提供的绘图
基类 Control 通过其 Paint 事件提供绘图功能。 每当控件需要更新其显示时,它都会引发 Paint 事件。
Paint 事件的事件数据类 PaintEventArgs 包含绘制控件所需的数据 - 图形对象的句柄和表示绘制区域的矩形。
public class PaintEventArgs : EventArgs, IDisposable
{
public System.Drawing.Rectangle ClipRectangle {get;}
public System.Drawing.Graphics Graphics {get;}
// Other properties and methods.
}
Graphics 是一个可封装绘图功能的托管类,如本文后面的 GDI 讨论中所述。 ClipRectangle 是 Rectangle 结构的实例,它定义了可在其中绘制控件的可用区域。 控件开发人员可以使用控件的 ClipRectangle 属性来计算 ClipRectangle,如本文后面的几何图形讨论中所述。
OnPaint
控件必须通过重写它从 Control 继承的 OnPaint 方法来提供呈现逻辑。 OnPaint 可访问图形对象和矩形,以通过传递给它的 PaintEventArgs 实例的 Graphics 和 ClipRectangle 属性进行绘制。
下面的代码使用 System.Drawing
命名空间:
protected override void OnPaint(PaintEventArgs e)
{
// Call the OnPaint method of the base class.
base.OnPaint(e);
// Declare and instantiate a new pen that will be disposed of at the end of the method.
using var myPen = new Pen(Color.Aqua);
// Create a rectangle that represents the size of the control, minus 1 pixel.
var area = new Rectangle(new Point(0, 0), new Size(this.Size.Width - 1, this.Size.Height - 1));
// Draw an aqua rectangle in the rectangle represented by the control.
e.Graphics.DrawRectangle(myPen, area);
}
Control 基类的 OnPaint 方法不实现任何绘图功能,只是调用注册到 Paint 事件的事件委托。 重写 OnPaint 时,应确保调用基类的 OnPaint 方法,以便注册的委托可接收 Paint 事件。 但是,绘制整个表面的控件不应调用基类的 OnPaint,因为这会引起闪烁。
备注
请勿直接从控件调用 OnPaint;请改为调用 Invalidate 方法(从 Control 继承)或调用 Invalidate 的其他方法。 Invalidate 方法又会调用 OnPaint。 重载 Invalidate 方法,并根据提供给 Invalidatee
的参数重绘其部分屏幕区域或整个屏幕区域。
控件的 OnPaint 方法中的代码将在第一次绘制控件以及每次刷新该控件时执行。 若要确保在每次调整控件大小时都重新进行绘制,请将下面的行添加到控件的构造函数中:
SetStyle(ControlStyles.ResizeRedraw, true);
OnPaintBackground
Control 基类定义了另一种可用于绘图的方法,即 OnPaintBackground 方法。
protected virtual void OnPaintBackground(PaintEventArgs e);
OnPaintBackground 绘制窗口的背景(并以相同方式绘制形状),并保证速度较快,而 OnPaint 绘制详细信息,速度可能较慢,因为单个绘制请求会合并为一个 Paint 事件,其中涵盖了所有需要重新绘制的区域。 例如,如果想要为控件绘制颜色渐变的背景,则可能需要调用 OnPaintBackground。
虽然 OnPaintBackground 具有类似事件的命名法并采用与 OnPaint
方法相同的参数,但 OnPaintBackground
不是真正的事件方法。 不存在 PaintBackground
事件,并且 OnPaintBackground
不调用事件委托。 重写 OnPaintBackground
方法时,无需使用派生类即可调用其基类的 OnPaintBackground
方法。
2、GDI+ 基础知识
Graphics 类提供用于绘制各种形状(如圆形、三角形、弧形和椭圆形)的方法,以及用于显示文本的方法。 System.Drawing 命名空间包含命名空间和类,它们可用于封装图形元素,如形状(圆形、矩形、弧形等)、颜色、字体、画笔等。
3、绘图区域的几何图形
控件的 ClientRectangle 属性指定可用于用户屏幕上的控件的矩形区域,而 PaintEventArgs 的 ClipRectangle 属性指定所绘制的区域。 当控件的一小部分显示发生变化时,控件可能只需要绘制部分可用区域。 在这些情况下,控件开发人员必须计算要在其中进行绘制的实际矩形,并将其传递到 Invalidate。 采用 Rectangle 或 Region 作为参数的 Invalidate 的重载版本使用该参数生成 PaintEventArgs 的 ClipRectangle 属性。
4、释放图形资源
图形对象成本昂贵,因为它们使用系统资源。 此类对象包括 System.Drawing.Graphics 类的实例以及 System.Drawing.Brush、System.Drawing.Pen 和其他图形类的实例。 请务必仅在需要时才创建图形资源,并在使用完之后立即释放。 如果创建实现 IDisposable 接口的类型的实例,请在使用完该实例之后,调用其 Dispose 方法来释放资源。