Доступ к объекту Graphics вне обработчика Paint

Доступ к объекту Graphics вне обработчика Paint

В некоторых редких случаях может понадобиться доступ к объекту Graphics вне контекста обработчика события Paint. Предположим, например, что нужно перерисовать небольшой круг с центром в точке (х, у), где был выполнен щелчок кнопки мыши. Чтобы получить действительный объект Graphics в рамках контекста обработчика событий MouseDown, можно, например, вызвать статический метод Graphics.FromHwnd(). Имея опыт использования Win32, вы можете знать, что HWND является структурой данных, представляющей окно Win32. В рамках платформы .NET наследуемое свойство Handle извлекает соответствующую структуру HWND, которую затем можно использовать в качестве параметра для Graphics. FromHwnd().

private void MainForm_MouseDown(object sender, MouseEventArgs e) {

 // Получение объекта Graphics через Hwnd.

 Graphics g = Graphics.FromHwnd(this.Handle);

 // Рисование круга 10*10 по щелчку мыши.

 g.FillEllipse(Brushes.Firebrick, e.X, e.Y, 10, 10);

 // Освобождение объектов Graphic, созданных напрямую.

 g.Dispose();

}

Эта логика отображает круг за пределами обработчика OnPaint(), но очень важно понимать, что когда выполняется обновление формы, все такие круги стираются! Это разумно, поскольку соответствующая визуализация выполнялась в контексте события MouseDown. Значительно лучшим подходом Является создание в обработчике события MouseDown нового типа Point, который добавляется к некоторой внутренней коллекции (например, List‹T›), и только затем вызывается Invalidate(). Тогда обработчик события Раint может просто "пройти" по коллекции и перерисовать каждый Point.

public partial class MainForm: Form {

 // Используется для хранения всех Point.

 private List‹Point› myPts = new List‹Point›();

 publiс MainForm() {

  …

  this.MouseDown += new MouseEventHandler(MainForm_MouseDown);

 }

 private void MainForm_MouseDown(object sender, MouseEventArgs e) {

  // Добавление в коллекцию.

  myPts.Add(new Point(e.X, e.Y));

  Invalidate();

 }

 private void MainForm_Paint(object sender, PaintEventArgs e) {

  Graphics g = e.Graphics;

  g.DrawString("Привет GDI+", new Font("Times New Roman", 20), new SolidBrush(Color.Black), 0, 0);

  foreach(Point p in myPts) g.FillEllipse(Brushes.Firebrick, p.X, p.Y, 10, 10);

 }

}

При таком подходе уже отображенные круги будут оставаться на месте, поскольку графическая визуализация обрабатывается в рамках события Paint. На рис. 20.1 показано окно тестового запуска этого приложения.

Рис 20.1. Простое графическое приложение

Исходный код. Проект BasiсPaintForm размещен в подкаталоге, соответствующем главе 20.