呈现 Windows 窗体控件Rendering a Windows Forms Control

呈现是指在用户屏幕上创建视觉对象表示形式的过程。Rendering refers to the process of creating a visual representation on a user's screen. Windows 窗体使用 GDI (新的 Windows 图形库) 来呈现。Windows Forms uses GDI (the new Windows graphics library) for rendering. 提供对 GDI 的访问的托管类位于System.Drawing命名空间及其子命名空间中。The managed classes that provide access to GDI are in the System.Drawing namespace and its subnamespaces.

控件呈现涉及以下元素:The following elements are involved in control rendering:

  • 基类提供的绘制功能System.Windows.Forms.ControlThe drawing functionality provided by the base class System.Windows.Forms.Control.

  • GDI 图形库的重要元素。The essential elements of the GDI graphics library.

  • 绘图区域的几何。The geometry of the drawing region.

  • 用于释放图形资源的过程。The procedure for freeing graphics resources.

控件提供的绘制功能Drawing Functionality Provided by Control

基类Control通过其Paint事件提供绘制功能。The base class Control provides drawing functionality through its Paint event. 每当控件需要更新Paint其显示时, 都会引发事件。A control raises the Paint event whenever it needs to update its display. 有关 .NET Framework 中事件的详细信息, 请参阅处理和引发事件For more information about events in the .NET Framework, see Handling and Raising Events.

Paint事件的事件数据类包含绘制控件所需的数据-图形对象的句柄和表示要在其中绘制的区域的矩形对象。 PaintEventArgsThe event data class for the Paint event, PaintEventArgs, holds the data needed for drawing a control — a handle to a graphics object and a rectangle object that represents the region to draw in. 这些对象在以下代码片段中显示为粗体。These objects are shown in bold in the following code fragment.

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 讨论中所述。Graphics is a managed class that encapsulates drawing functionality, as described in the discussion of GDI later in this topic. ClipRectangleRectangle结构的实例, 用于定义可在其中绘制控件的可用区域。The ClipRectangle is an instance of the Rectangle structure and defines the available area in which a control can draw. 控件开发人员可以ClipRectangle使用控件的ClipRectangle属性计算, 如本主题后面的几何讨论中所述。A control developer can compute the ClipRectangle using the ClipRectangle property of a control, as described in the discussion of geometry later in this topic.

控件必须通过重写OnPaint继承自Control的方法来提供呈现逻辑。A control must provide rendering logic by overriding the OnPaint method that it inherits from Control. OnPaint获取对图形对象的访问, 以及通过Graphics ClipRectangle传递给它的PaintEventArgs实例的属性在中绘制的矩形。OnPaint gets access to a graphics object and a rectangle to draw in through the Graphics and the ClipRectangle properties of the PaintEventArgs instance passed to it.

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

基类OnPaint Paint的方法不会实现任何绘制功能, 而只会调用向事件注册的事件委托。 ControlThe OnPaint method of the base Control class does not implement any drawing functionality but merely invokes the event delegates that are registered with the Paint event. 重写OnPaint时, 通常应OnPaint调用基类的方法, 以便注册Paint的委托接收事件。When you override OnPaint, you should typically invoke the OnPaint method of the base class so that registered delegates receive the Paint event. 但是, 绘制整个图面的控件不应调用基类的OnPaint, 因为这会引入闪烁。However, controls that paint their entire surface should not invoke the base class's OnPaint, as this introduces flicker. 有关替代OnPaint事件的示例, 请参阅如何:创建显示进度的 Windows 窗体控件。For an example of overriding the OnPaint event, see the How to: Create a Windows Forms Control That Shows Progress.

备注

不要直接从OnPaint控件调用, 而应Invalidate调用方法 (继承自Control) 或调用的其他方法InvalidateDo not invoke OnPaint directly from your control; instead, invoke the Invalidate method (inherited from Control) or some other method that invokes Invalidate. 方法又会调用OnPaintInvalidateThe Invalidate method in turn invokes OnPaint. 重载方法时, 控件会重绘其部分或全部屏幕Invalidate区域, 具体取决于提供给e的参数。 InvalidateThe Invalidate method is overloaded, and, depending on the arguments supplied to Invalidate e, a control redraws either some or all of its screen area.

基类Control定义可用于绘制的OnPaintBackground另一种方法, 即方法。The base Control class defines another method that is useful for drawing — the OnPaintBackground method.

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

OnPaintBackground绘制窗口的背景 (进而使其成为一种形状), 并保证其速度非常快OnPaint , 同时绘制详细信息并可能比较慢, 因为单个绘制请求合并Paint为一个事件, 该事件涵盖了需要重绘.OnPaintBackground paints the background (and thereby the shape) of the window and is guaranteed to be fast, while OnPaint paints the details and might be slower because individual paint requests are combined into one Paint event that covers all areas that have to be redrawn. 例如, OnPaintBackground如果想要绘制控件的渐变颜色背景, 则可能需要调用。You might want to invoke the OnPaintBackground if, for instance, you want to draw a gradient-colored background for your control.

虽然OnPaintBackground具有类似事件的命名法并采用与OnPaint方法相同的参数, OnPaintBackground但并不是真正的事件方法。While OnPaintBackground has an event-like nomenclature and takes the same argument as the OnPaint method, OnPaintBackground is not a true event method. 没有事件, 并且OnPaintBackground不调用事件委托。 PaintBackgroundThere is no PaintBackground event and OnPaintBackground does not invoke event delegates. 重写OnPaintBackground方法时, 派生类不需要调用其基类的OnPaintBackground方法。When overriding the OnPaintBackground method, a derived class is not required to invoke the OnPaintBackground method of its base class.

GDI + 基础知识GDI+ Basics

Graphics类提供用于绘制各种形状 (如圆、三角形、弧形和椭圆) 的方法, 以及用于显示文本的方法。The Graphics class provides methods for drawing various shapes such as circles, triangles, arcs, and ellipses, as well as methods for displaying text. System.Drawing命名空间及其子命名空间包含的类可封装图形元素 (如圆形、矩形、弧形等)、颜色、字体、画笔等。The System.Drawing namespace and its subnamespaces contain classes that encapsulate graphics elements such as shapes (circles, rectangles, arcs, and others), colors, fonts, brushes, and so on. 有关 GDI 的详细信息, 请参阅使用托管图形类For more information about GDI, see Using Managed Graphics Classes. 还介绍了 GDI 的基本原理。创建显示进度的 Windows 窗体控件。The essentials of GDI are also described in the How to: Create a Windows Forms Control That Shows Progress.

绘图区域的几何图形Geometry of the Drawing Region

控件ClientRectangle的属性指定可用于用户屏幕上的控件的矩形区域, ClipRectangle而的属性PaintEventArgs指定实际绘制的区域。The ClientRectangle property of a control specifies the rectangular region available to the control on the user's screen, while the ClipRectangle property of PaintEventArgs specifies the area that is actually painted. (请记住, 在将Paint PaintEventArgs实例作为其参数的事件方法中完成绘制)。(Remember that painting is done in the Paint event method that takes a PaintEventArgs instance as its argument). 控件可能只需要绘制部分可用区域, 这种情况下, 当控件的小部分显示更改时。A control might need to paint only a portion of its available area, as is the case when a small section of the control's display changes. 在这些情况下, 控件开发人员必须计算要在其中Invalidate进行绘制的实际矩形, 并将其传递给。In those situations, a control developer must compute the actual rectangle to draw in and pass that to Invalidate. Invalidate ClipRectangle采用或作为参数PaintEventArgs的的重载版本使用该参数来生成的属性。 Region RectangleThe overloaded versions of Invalidate that take a Rectangle or Region as an argument use that argument to generate the ClipRectangle property of PaintEventArgs.

下面的代码段演示FlashTrackBar自定义控件如何计算要在中绘制的矩形区域。The following code fragment shows how the FlashTrackBar custom control computes the rectangular area to draw in. client 变量ClipRectangle表示属性。The client variable denotes the ClipRectangle property. 有关完整示例, 请参阅如何:创建显示进度的 Windows 窗体控件。For a complete sample, see How to: Create a Windows Forms Control That Shows Progress.

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

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

Invalidate(invalid)

释放图形资源Freeing Graphics Resources

图形对象开销高昂, 因为它们使用系统资源。Graphics objects are expensive because they use system resources. System.Drawing.Graphics类对象包括类的实例System.Drawing.Brush, 以及、 System.Drawing.Pen和其他图形类的实例。Such objects include instances of the System.Drawing.Graphics class as well as instances of System.Drawing.Brush, System.Drawing.Pen, and other graphics classes. 只有在需要图形资源并在使用完毕后立即将其释放, 这一点非常重要。It is important that you create a graphics resource only when you need it and release it soon as you are finished using it. 如果创建实现IDisposable接口的类型, 请在完成该操作Dispose后调用其方法, 以便释放资源。If you create a type that implements the IDisposable interface, call its Dispose method when you are finished with it in order to free resources.

下面的代码段演示FlashTrackBar自定义控件如何创建和Brush释放资源。The following code fragment shows how the FlashTrackBar custom control creates and releases a Brush resource. 有关完整的源代码, 请参阅如何:创建显示进度的 Windows 窗体控件。For the complete source code, see How to: Create a Windows Forms Control That Shows Progress.

private Brush baseBackground = null;
Private baseBackground As Brush
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);
    }
}
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
protected override void OnResize(EventArgs e) {
    base.OnResize(e);
    if (baseBackground != null) {
        baseBackground.Dispose();
        baseBackground = null;
    }
}
Protected Overrides Sub OnResize(ByVal e As EventArgs)
    MyBase.OnResize(e)
    If (baseBackground IsNot Nothing) Then
        baseBackground.Dispose()
        baseBackground = Nothing
    End If
End Sub

请参阅See also