Отрисовка элементов управления Windows Forms

Отрисовка — это процесс создания визуального представления на экране пользователя. Windows Forms использует для отрисовки GDI (новую библиотеку графики Windows). Управляемые классы, предоставляющие доступ к GDI, находятся в пространстве имен System.Drawing и его подпространствах имен.

При отрисовке элемента управления используются следующие элементы:

  • Функции рисования, предоставляемые базовым классом System.Windows.Forms.Control.

  • Обязательные элементы библиотеки графических объектов GDI.

  • Геометрия области рисования.

  • Процедура высвобождения графических ресурсов.

Функции рисования, предоставляемые элементом управления

Базовый класс Control предоставляет функции рисования с помощью события Paint. Элемент управления вызывает событие Paint, когда ему требуется обновить свое отображение. Дополнительные сведения о событиях в .NET см. в статье Обработка и вызов событий.

Класс данных событий для события 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, далее в этом разделе. ClipRectangle является экземпляром структуры Rectangle и определяет доступную область, в которой можно нарисовать элемент управления. Разработчик элемента управления может вычислить ClipRectangle с помощью свойства ClipRectangle элемента управления, как описано в обсуждении геометрии далее в этом разделе.

Элемент управления должен предоставлять логику отрисовки путем переопределения метода OnPaint, который он наследует от Control. 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, в свою очередь, вызывает OnPaint. Метод Invalidate перегружен, и в зависимости от аргументов, предоставленных 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 Forms с отображением хода выполнения.

Геометрия области рисования

Свойство ClientRectangle элемента управления указывает прямоугольную область, доступную для элемента управления на экране пользователя, а свойство ClipRectangle объекта PaintEventArgs — фактически закрашиваемую область. (Помните, что рисование выполняется в методе события Paint, который принимает экземпляр PaintEventArgs в качестве аргумента). Элементу управления может потребоваться закрасить только часть его доступной области, например в случае, когда изменяется небольшой раздел элемента управления. В таких ситуациях разработчик элемента управления должен вычислить фактический прямоугольник для рисования и передать его в Invalidate. Перегруженные версии Invalidate, принимающие Rectangle или Region в качестве аргумента, используют этот аргумент для создания свойства ClipRectangle элемента PaintEventArgs.

В следующем фрагменте кода показано, как пользовательский элемент управления 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.Brush, System.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

См. также