Рисование элементов управления (Windows Forms .NET)

Пользовательское рисование элементов управления — одна из многих сложных задач, которые можно с легкостью выполнить в Windows Forms. При разработке пользовательского элемента управления доступно множество параметров, которые управляют графическим отображением элемента управления. Если вы создаете настраиваемый элемент управления, то есть элемент управления, который наследуется от Control, необходимо предоставить код для преобразования его графического представления для просмотра.

Важно!

Документация по рабочему столу для .NET 7 и .NET 6 находится в стадии разработки.

Если вы создаете составной элемент управления, то есть элемент управления, наследующий или UserControl один из существующих элементов управления Windows Forms, можно переопределить стандартное графическое представление и предоставить собственный графический код.

Если вы хотите предоставить настраиваемый рендеринг для существующего элемента управления без создания нового элемента управления, набор параметров становится более ограниченным. Однако по-прежнему существует широкий набор возможностей графического представления ваших элементов управления и приложений.

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

  • Функции рисования, предоставляемые базовым классом 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, когда завершите работу с ним, чтобы освободить ресурсы.

См. также