控制項的自訂繪製是 Windows Forms 所簡化的許多複雜工作之一。 製作自訂控制項時,有許多選項可用來處理控制項的圖形化外觀。 如果您要撰寫 自定義控件,也就是繼承自 Control的控件,您可以使用程式代碼來呈現其圖形表示法。
如果您要建立 複合控件,也就是繼承自 UserControl 或其他 現有 Windows Forms 控件 的控件,您可以覆寫標準圖形表示法,並提供您自己的圖形程式代碼。
如果您想要為現有的控制項提供自定義呈現而不建立新的控制項,請處理 Paint 事件,這個事件允許您在控制項上繪製。
下列元素涉及控制項轉譯:
- 基底類別 System.Windows.Forms.Control 所提供的繪圖功能。
- GDI 圖形程式庫的基本元素。
- 繪圖區域的幾何。
- 釋放圖形資源的程序。
由控制提供的繪製
基底類別 Control 透過其 Paint 事件提供繪圖功能。 控件每當需要更新其顯示並完成繪圖時,就會引發Paint事件。 如需 .NET 中事件的詳細資訊,請參閱 處理和引發事件。
事件 Paint 的事件資料類別 PaintEventArgs 保存繪製控制項所需的資料 —— 包括一個圖形物件的控制端以及一個代表繪圖區域的矩形。
public class PaintEventArgs : EventArgs, IDisposable
{
public System.Drawing.Rectangle ClipRectangle {get;}
public System.Drawing.Graphics Graphics {get;}
// Other properties and methods.
}
Public Class PaintEventArgs
Inherits EventArgs
Implements IDisposable
Public ReadOnly Property ClipRectangle As System.Drawing.Rectangle
Public ReadOnly Property Graphics As System.Drawing.Graphics
' Other properties and methods.
End Class
Graphics (英文) 是封裝繪圖功能的受控類別,如本文稍後的 GDI 討論中所述。 ClipRectangle 是 Rectangle 結構的實例,定義了控制項可以繪圖的可用區域。 控制項開發人員可以使用控制項的 ClipRectangle 屬性來計算 ClipRectangle,如本文稍後的幾何討論中所述。
OnPaint
控制項必須覆寫它從 OnPaint 繼承的 Control 方法,以提供轉譯邏輯。 OnPaint 可以透過 Graphics 和 ClipRectangle 屬性,從傳遞給它的 PaintEventArgs 實例中存取圖形物件以及用於繪圖的矩形。
下列程式碼使用 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);
}
Protected Overrides Sub OnPaint(e As PaintEventArgs)
MyBase.OnPaint(e)
' Declare and instantiate a drawing pen.
Using myPen = New System.Drawing.Pen(Color.Aqua)
' Create a rectangle that represents the size of the control, minus 1 pixel.
Dim area = New Rectangle(New Point(0, 0), New Size(Me.Size.Width - 1, Me.Size.Height - 1))
' Draw an aqua rectangle in the rectangle represented by the control.
e.Graphics.DrawRectangle(myPen, area)
End Using
End Sub
基底 OnPaint 類別的 Control 方法不會實作任何繪圖功能,而只會叫用向 Paint 事件註冊的事件委派。 當您覆寫 OnPaint 時,您一般應該叫用基底類別的 OnPaint 方法 (部分機器翻譯),以便已註冊的委派可以接收 Paint 事件 (部分機器翻譯)。 不過,繪製其整個表面的控制項不應該呼叫基底類別的 OnPaint,因為這會導致閃爍。
備註
請勿直接從控制項呼叫 OnPaint;請改為呼叫由 Invalidate 繼承的 Control 方法,或者呼叫某個調用 Invalidate 的其他方法。
Invalidate 方法會進一步呼叫 OnPaint。
Invalidate 方法會多載,而且,視提供給 Invalidatee
的引數而定,會重新繪製其部分或所有螢幕區域。
控制項的 OnPaint 方法中的程式碼會在控制項第一次繪製時執行,並在每次重新整理時執行。 要確保您的控制項在每次調整大小時都能重新繪製,請將以下這行程式碼新增至控制項的構造函式中:
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.ResizeRedraw, True)
OnPaintBackground (繪製背景)
基底 Control 類別定義了另一個對繪圖有用的方法,即 OnPaintBackground 方法。
protected virtual void OnPaintBackground(PaintEventArgs e);
Protected Overridable Sub OnPaintBackground(e As PaintEventArgs)
OnPaintBackground 負責繪製視窗的背景(以及以此方式形成視窗的形狀),並保證能快速完成;而 OnPaint 則負責繪製細節,速度可能較慢,因為個別的繪製請求會合併成一個 Paint 事件,涵蓋所有需要重新繪製的區域。 例如,如果您想要為控制項繪製漸層色彩的背景,您可能會想要叫用 OnPaintBackground。
雖然 OnPaintBackground (英文) 具有類似事件的命名法,並接受與 OnPaint
方法相同的引數,但 OnPaintBackground
不是真正的事件方法。 沒有 PaintBackground
事件,且 OnPaintBackground
不會叫用事件委派。 當覆寫 OnPaintBackground
方法時,衍生類別不需要呼叫其基底類別的 OnPaintBackground
方法。
GDI+ 基本概念
Graphics 類別 (英文) 提供繪製各種圖形 (例如圓形、三角形、弧線和橢圓形) 的方法,以及顯示文字的方法。 System.Drawing 命名空間包含命名空間和類別,封裝了圖形元素,例如形狀(如圓形、矩形、弧形等)、色彩、字型、筆刷等等。
繪圖區域的幾何
控制項的 ClientRectangle 屬性 (英文) 會指定使用者螢幕上可供控制項使用的矩形區域,而 ClipRectangle (部分機器翻譯) 的 PaintEventArgs 屬性 (部分機器翻譯) 則會指定繪圖的區域。 控制項可能只需要繪製其可用區域的一部分,就像控制項的小區段顯示變更時的情況一樣。 在這些情況下,控制項開發者必須計算真正要繪製的矩形並將其傳遞至 Invalidate。 接受 Invalidate 或 Rectangle 作為引數的 Region 多載版本會使用該引數來生成 ClipRectangle 的 PaintEventArgs 屬性。
釋放圖形資源
圖形物件的成本高昂,因為它們會使用系統資源。 此類物件包括 System.Drawing.Graphics 類別 (英文) 的執行個體,以及 System.Drawing.Brush (英文)、System.Drawing.Pen (英文) 和其他圖形類別的執行個體。 請務必只在需要圖形資源時建立圖形資源,並在使用完畢後立即釋放它。 如果您建立實作 IDisposable 介面 (部分機器翻譯) 的類型執行個體,請在使用完畢後呼叫其 Dispose 方法 (英文) 以釋放資源。