共用方式為


呈現 Windows Form 控制項

轉譯指的是在使用者螢幕上建立視覺表示法的流程。 Windows Forms 使用 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,如本主體稍後的幾何討論中所述。

控制項必須覆寫繼承自 OnPaintControl 方法,以提供轉譯邏輯。 OnPaint 透過傳遞給它之 Graphics 執行個體的 ClipRectangle 屬性和 PaintEventArgs 屬性,來存取圖形物件和可在其中繪圖的矩形。

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

基底 OnPaint 類別的 Control 方法不會實作任何繪圖功能,而只會叫用向 Paint 事件註冊的事件委派。 當您覆寫 OnPaint 時,您一般應該叫用基底類別的 OnPaint 方法,以便已註冊的委派可以接收 Paint 事件。 不過,繪製其整個表面的控制項不應該叫用基底類別的 OnPaint,因為這會導致閃爍。 如需覆寫 OnPaint 事件的範例,請參閱 如何:建立顯示進度的 Windows Forms 控制項

注意

請勿直接從控制項叫用 OnPaint;請改為叫用 Invalidate 方法 (繼承自 Control),或叫用 Invalidate 的一些其他方法。 Invalidate 方法會接著叫用 OnPaintInvalidate 方法會多載,而且,視提供給 Invalidatee 的引數而定,控制項會重新繪製其部分或所有螢幕區域。

基底 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 的詳細資訊,請參閱使用 Managed Graphics 類別。 GDI 的基本概念也說明於如何:建立顯示進度的 Windows Forms 控制項

繪圖區域的幾何

控制項的 ClientRectangle 屬性會指定使用者螢幕上可供控制項使用的矩形區域,而 ClipRectanglePaintEventArgs 屬性則會指定實際繪圖的區域。 (請記住,繪製是在採用 Paint 執行個體作為引數的 PaintEventArgs 事件方法中完成的)。 控制項可能只需要繪製其可用區域的一部分,就像控制項的小區段顯示變更時的情況一樣。 在那些情況下,控制項開發人員必須計算實際矩形,以在其中繪圖並傳遞至 Invalidate。 接受 InvalidateRectangle 做為引數的 Region 多載版本會使用該引數來產生 ClipRectanglePaintEventArgs 屬性。

下列程式碼段顯示 FlashTrackBar 自訂控制項如何計算要繪製的矩形區域。 client 變數表示 ClipRectangle 屬性。 如需完整的範例,請參閱如何:建立顯示進度的 Windows Forms 控制項

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)

釋放圖形資源

圖形物件的成本高昂,因為它們會使用系統資源。 此類物件包括 System.Drawing.Graphics 類別的執行個體,以及 System.Drawing.BrushSystem.Drawing.Pen 和其他圖形類別的執行個體。 請務必只在需要圖形資源時建立圖形資源,並在使用完畢後立即釋放它。 如果您建立實作 IDisposable 介面的類型,請在完成時呼叫其 Dispose 方法,以釋放資源。

下列程式碼段顯示 FlashTrackBar 自訂控制項如何建立及釋放 Brush 資源。 如需完整的原始程式碼,請參閱如何:建立顯示進度的 Windows Forms 控制項

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

另請參閱