А что, если нужно нарисовать что-то на форме вне обработчика события Paint — например, по нажатию кнопки? Как получить объект Graphics, который станет поверхностью рисования нужного нам элемента управления или формы? Существует несколько способов сделать это, но мы рассмотрим только два, наиболее удобных с моей точки зрения.
Создайте новое приложение, положите на его поверхность кнопку и напишите следующий код, выполняемый по ее нажатию:
Graphics g = Graphics.FromHwnd(Handle); g.DrawRectangle(SystemPens.ActiveBorder, new Rectangle(10, 10, ClientSize.Width - 20, ClientSize.Height - 20));
Чтобы получить объект для рисования, в этом примере используется статичный метод FromHwnd() класса Graphics. Методу нужно передать свойство Handle окна, объект рисования которого вы хотите получить, а сам объект мы получаем в качестве результата работы метода.
Теперь мы можем рисовать на поверхности объекта рисования. Для примера я рисую прямоугольник с помощью метода DrawRectangle(). Этому методу передаются два параметра: перо (класс Pen), с помощью которого происходит рисование, и размеры в виде структуры Rectangle. С помощью класса Pen мы можем задать цвет и ширину пера (толщину получаемой линии). Мы пока с перьями не разбирались, поэтому я воспользовался системным пером под именем ActiveBorder из перечисления SystemPens. Перечисление SystemPens содержит системные перья, и перо ActiveBorder служит для рисования контуров окон.
В чем недостаток рисования по событию, отличному от события Paint? Непостоянность результата. Запустите пример и попробуйте нажать на кнопку. Прямоугольник появился, и пока вроде бы все прекрасно. А что, если переместить окно? Нарисованный прямоугольник снова на месте. А что, если свернуть окно и развернуть его заново? Наш рисунок исчезнет! До этого система запоминала наше окно и при перемещении окна сама восстанавливала его содержимое. Вы даже можете попытаться перекрыть свое окно другим окном, а потом убрать его, и, вполне вероятно, что прямоугольник снова окажется на своем месте. ОС пытается оптимизировать работу графики и сама восстанавливает рисунок формы. Но это не всегда так.
Когда система не может восстановить изображение формы (это обязательно происходит после восстановления окна из свернутого состояния), то она и не пытается прорисовать наш прямоугольник. Вместо этого ОС посылает окну сообщение о необходимости нарисовать свое содержимое и говорит — сделайте это самостоятельно. Поэтому то, что написано в обработчике Paint, гарантированно станет отображаться, а все остальное будет потеряно. Чтобы восстановить прямоугольник, придется нажимать кнопку заново. Вашего пользователя такой подход вряд ли обрадует, поэтому лучше выполнять рисование по событию Paint:
bool bDrawRectangle = false; private void button1_Click(object sender, EventArgs e) { bDrawRectangle = true; Invalidate(); } private void Form1_Paint(object sender, PaintEventArgs e) { if (bDrawRectangle) g.DrawRectangle(SystemPens.ActiveBorder, new Rectangle(10, 10, ClientSize.Width - 20, ClientSize.Height - 20)); }
В этом случае по нажатию кнопки значение переменной bDrawRectangle изменяется на true и вызывается метод Invalidate(), который заставляет форму перерисоваться. По событию Paint прямоугольник рисуется, только если переменная bDrawRectangle установлена в true. Теперь этот прямоугольник никуда не денется, даже если окно восстановят из свернутого состояния.
Да, я же обещал вам показать два способа получения объекта для рисования. Второй способ заключается в использовании метода CreateGraphics() формы:
Graphics g = this.CreateGraphics();
Этот метод возвращает объект рисования для формы или элемента управления, для которого он был вызван. В данном случае он вызывается для this, а значит, вернет объект рисования текущей формы.