Ключевым обстоятельством, которое должно быть вам абсолютно понятным, если вы используете подключение к функциям Paint, является то, что вы не в состоянии контролировать, когда именно будут вызываться эти функции. Вы можете сделать это принудительно, объявив область экрана недействительной, но не имеете никакой возможности контролировать внешние факторы, которые могут инициировать запросы перерисовки экрана. Поэтому крайне важно, чтобы операции перерисовки осуществлялись как можно быстрее и с малыми накладными расходами. Если перерисовка экрана осуществляется очень медленно, то будет создаваться впечатление, что ваше приложение работает в замедленном режиме.
Листинг 11.8. Подключение к функции Paint формы
//Кисти, которые мы хотим кэшировать, чтобы избавить себя
//от необходимости все время создавать их и уничтожать
System.Drawing.Brush m_brushBlue;
System.Drawing.Brush m_brushYellow;
//Ради интереса подсчитаем, сколько раз осуществлялся вызов
int m_paintCount;
//-----------------------------------------------------------------------------
//Мы перекрываем обработчики событий Paint наших базовых классов. Это означает,
//что каждый раз, когда форма вызывается для перерисовки самой себя, будет
//вызываться эта функция.
//-----------------------------------------------------------------------------
protected override void OnPaint(PaintEventArgs e) {
//ВАЖНО: Вызвать базовый класс и дать ему возможность
//выполнить всю необходимую работу по рисованию
base.OnPaint(e);
//Увеличить на 1 значение счетчика вызовов
m_paintCount = m_paintCount + 1;
//------------------------------------------------------------------------
//Важно:
//Вместо того чтобы создавать объект Graphics, мы получаем его на время
//данного вызова. Это означает, что освобождать память путем вызова
//метода .Dispose() объекта - не наша забота
//------------------------------------------------------------------------
System.Drawing.Graphics myGfx;
myGfx = e.Graphics;
//-------------------------------------------------------------------
//Поскольку эту операцию рисования необходимо выполнить быстро,
//кэшируем кисти, чтобы избавить себя от необходимости создавать их и
//уничтожать при каждом вызове
//-------------------------------------------------------------------
if (m_brushBlue == null) {
m_brushBlue = new System.Drawing.SolidBrush(System.Drawing.Color.Blue);
}
if (m_brushYellow == null) {
m_brushYellow = new System.Drawing.SolidBrush(System.Drawing.Color.Yellow);
}
//-------------------
//Выполнить рисование
//-------------------
myGfx.FillRectangle(m_brushBlue, 2, 2, 100, 100);
myGfx.DrawString("PaintCount: " + m_paintCount.ToString(), this.Font, mbrushYellow, 3, 3);
//Выход: Объекты, для которых мы должны были бы вызывать метод
//.Dispose(), отсутствуют.
}
Обработчики событий или перекрытые функции?
Существует еще один способ подключения к запросам Paint. В приведенном выше примере для этого использовался подход, основанный на механизме наследования. Однако обработку событий Paint можно осуществлять и при помощи обработчика событий. Это выполняется точно так же, как и подключение к обработчику событий Click; для получения запросов событий необходимо зарегистрировать функцию. Если вместо перекрытия метода OnPaint() использовать обработчик событий, то в приведенный выше код необходимо внести три вида изменений:
1. Следует изменить имя и сигнатуру метода таким образом, чтобы они соответствовали тому, что требуется для обработчика событий Paint (например, protected void PaintEventHandler(object sender, PaintEventArgs e) вместо protected override void OnPaint(PaintEventArgs e)).
2. Вызов base.OnPaint (e) необходимо удалить, поскольку осуществление этого вызова не входит в обязанности обработчика событий. В действительности, именно базовая реализация base.OnPaint(e) вызывает любой зарегистрированный обработчик событий.
3. В функцию InitializeComponent формы необходимо добавить код для подключения нового обработчика событий. Например, добавьте такой код:
this.Paint += new System.Windows.Forms.PaintEventHandler(this.PaintEventHandler);
InitializeComponent();
Выбирая между обработчиком событий и механизмом наследования, вы можете руководствоваться собственными соображениями. Что касается меня, то для такой низкоуровневой работы, как подключение к обработке запросов перерисовки экрана (Paint), я предпочитаю использовать подход, основанный на механизме наследования, который мне более понятен. Для высокоуровневых событий, например щелчков (Click), я предпочитаю прибегать к подходу, основанному на использовании обработчиков событий, поскольку этот код автоматически генерируется для меня после двойного щелчка на элементе управления в окне конструктора форм.
Читать дальше