呈现 Windows 窗体控件

更新:2007 年 11 月

呈现是指在用户屏幕上创建可视化表示的过程。Windows 窗体使用 GDI(新的 Windows 图形库)来完成呈现。提供对 GDI 进行访问的托管类位于 System.Drawing 命名空间及其子命名空间中。

控件呈现中包括以下元素:

  • 由基类 System.Windows.Forms.Control 提供的绘制功能。

  • GDI 图形库的基本元素。

  • 绘图区域的几何图形。

  • 释放图形资源的步骤。

由控件提供的绘图功能

基类 Control 通过其 Paint 事件提供绘制功能。控件在需要更新其显示时引发 Paint 事件。有关 .NET Framework 中事件的更多信息,请参见处理和引发事件

Paint 事件的事件数据类 PaintEventArgs 保存绘制控件所需的数据,即表示绘制区域的图形对象或矩形对象的句柄。这些对象在以下代码片段中显示为粗体。

Public Class PaintEventArgs
   Inherits EventArgs
   Implements IDisposable
   
   Public ReadOnly Property ClipRectangle() As System.Drawing.Rectangle
      ...
   End Property
   
   Public ReadOnly Property Graphics() As System.Drawing.Graphics
      ...
   End Property
   ' Other properties and methods.
   ...
End Class
public class PaintEventArgs : EventArgs, IDisposable {
public System.Drawing.Rectangle ClipRectangle {get;}
public System.Drawing.Graphics Graphics {get;}
// Other properties and methods.
...
}

Graphics 是一个封装了绘制功能的托管类,该内容将在本主题中稍后对 GDI 的讨论中详细介绍。ClipRectangleRectangle 结构的一个实例,它定义绘制控件的可用区域。控件开发者可以使用控件的 ClipRectangle 属性计算 ClipRectangle,详情将在本主题稍后的几何图形论述中进行说明。

控件必须通过重写从 Control 继承的 OnPaint 方法来提供呈现逻辑。OnPaint 通过传递给它的 PaintEventArgs 实例的 GraphicsClipRectangle 属性来访问图形对象以及要在其中进行绘制的矩形。

Protected Overridable Sub OnPaint(pe As PaintEventArgs)
protected virtual void OnPaint(PaintEventArgs pe);

Control 基类的 OnPaint 方法不实现任何绘制功能,而仅仅是调用使用 Paint 事件注册的事件委托。当重写 OnPaint 时,通常应调用基类的 OnPaint 方法,以便已注册的委派可以接收到 Paint 事件。但是,绘制其整个图面的控件不应调用基类的 OnPaint,因为这样会引起闪烁。有关重写 OnPaint 事件的示例,请参见 如何:创建显示进度的 Windows 窗体控件

说明:

不要直接从控件调用 OnPaint,而应调用 Invalidate 方法(继承自 Control)或其他某个调用 Invalidate 的方法。Invalidate 方法随后调用 OnPaintInvalidate 方法被重载,根据提供给 Invalidate e 的参数,控件将重绘其部分或全部屏幕区域。

基类 Control 定义了另一个可用于绘制的方法,即 OnPaintBackground 方法。

Protected Overridable Sub OnPaintBackground(pevent As PaintEventArgs)
protected virtual void OnPaintBackground(PaintEventArgs pevent);

OnPaintBackground 绘制窗口的背景(即形状),并能保证快速完成,而 OnPaint 绘制的是详细内容,并且由于各个绘制请求都组合到一个涵盖所有必须重绘的区域的 Paint 事件中,速度可能会慢一些。在某些情况下(例如需要为控件绘制颜色渐变的背景),您可能需要调用 OnPaintBackground

虽然 OnPaintBackground 的命名法与事件类似,并具有与 OnPaint 方法相同的参数,但 OnPaintBackground 不是真正的事件方法。没有 PaintBackground 事件,并且 OnPaintBackground 不调用事件委托。当重写 OnPaintBackground 方法时,不要求派生类调用其基类的 OnPaintBackground 方法。

GDI+ 基础

Graphics 类提供了绘制各种形状(如圆、三角形、圆弧和椭圆)的方法,同时也提供了显示文本的方法。System.Drawing 命名空间及其子命名空间包含有封装了图形元素的类,图形元素包括形状(圆、矩形、圆弧及其他形状)、颜色、字体和笔刷等。有关 GDI 的更多信息,请参见 使用托管图形类。GDI 的要点在 如何:创建显示进度的 Windows 窗体控件 中也有介绍。

绘图区域的几何图形

控件的 ClientRectangle 属性指定用户屏幕上控件可用的矩形区域,而 PaintEventArgsClipRectangle 属性指定实际绘制的区域。(切记,绘制是在 Paint 事件方法中完成的,该方法的参数中包含一个 PaintEventArgs 实例)。在控件的一小部分显示发生变化时,控件可能仅需要绘制其可用区域的一部分。在这些情况下,控件开发者必须计算要在其中绘图的实际矩形并将其传递给 Invalidate。将 RectangleRegion 作为一个参数的 Invalidate 的重载版本使用该参数生成 PaintEventArgsClipRectangle 属性。

以下代码片段展示了 FlashTrackBar 自定义控件如何计算要在其中进行绘制的矩形区域。client 变量表示 ClipRectangle 属性。有关完整示例,请参见 如何:创建显示进度的 Windows 窗体控件

Dim invalid As Rectangle = New Rectangle( _
    client.X + lmin, _
    client.Y, _
    lmax - lmin, _
    client.Height)

Invalidate(invalid)
Rectangle invalid = new Rectangle(
    client.X + min, 
    client.Y, 
    max - min, 
    client.Height);

Invalidate(invalid);

释放图形资源

图形对象非常昂贵,因为它们占用很多系统资源。此类对象包括 System.Drawing.Graphics 类的实例以及 System.Drawing.BrushSystem.Drawing.Pen 和其他图形类的实例。应该仅在需要时才创建图形资源,并在使用完毕后立即将其释放,这一点非常重要。如果创建一个实现 IDisposable 界面的类型,请在使用完毕后调用其 Dispose 方法以释放资源。

下面的代码片段展示了 FlashTrackBar 自定义控件如何创建和释放 Brush 资源。有关完整的源代码,请参见 如何:创建显示进度的 Windows 窗体控件

Private baseBackground As Brush
private Brush baseBackground = null;
MyBase.OnPaint(e)

If (baseBackground Is Nothing) Then

    If (myShowGradient) Then
        baseBackground = New LinearGradientBrush(New Point(0, 0), _
                                                 New Point(ClientSize.Width, 0), _
                                                 StartColor, _
                                                 EndColor)
    ElseIf (BackgroundImage IsNot Nothing) Then
        baseBackground = New TextureBrush(BackgroundImage)
    Else
        baseBackground = New SolidBrush(BackColor)
    End If

End If
base.OnPaint(e);
if (baseBackground == null) {
    if (showGradient) {
        baseBackground = new LinearGradientBrush(new Point(0, 0),
                                                 new Point(ClientSize.Width, 0),
                                                 StartColor,
                                                 EndColor);
    }
    else if (BackgroundImage != null) {
        baseBackground = new TextureBrush(BackgroundImage);
    }
    else {
        baseBackground = new SolidBrush(BackColor);
    }
}
Protected Overrides Sub OnResize(ByVal e As EventArgs)
    MyBase.OnResize(e)
    If (baseBackground IsNot Nothing) Then
        baseBackground.Dispose()
        baseBackground = Nothing
    End If
End Sub
protected override void OnResize(EventArgs e) {
    base.OnResize(e);
    if (baseBackground != null) {
        baseBackground.Dispose();
        baseBackground = null;
    }
}

请参见

任务

如何:创建显示进度的 Windows 窗体控件