Рейтинговые книги
Читем онлайн C# для профессионалов. Том II - Симон Робинсон

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 62 63 64 65 66 67 68 69 70 ... 167

Что же делать? Проблема возникает, если окно или его часть становятся скрытыми по какой-либо причине (например, оно минимизируется или закрывается другим окном), Windows обычно немедленно отбрасывает всю информацию, относящуюся к тому, что там было изображено. Система должна это делать, иначе используемая память для хранения данных экрана будет слишком большой. Типичный компьютер может работать с видеоплатой, настроенной для вывода 1024×768 пикселей, возможно, в режиме с 24-битовым цветом. Мы покажем, что означает 24-битовый цвет позже, но в данный момент можно сказать, что каждый пиксель на экране занимает 3 байта, т. е. 2.25 Мбайт для изображения экрана. Однако нет ничего необычного, если пользователь имеет во время работы более 10 минимизированных окон в своей панели задач, в худшем сценарии 20, каждое из которых занимает весь экран, если оно не минимизировано. Если бы Windows действительно хранил визуальную информацию, которую содержат эти окна, готовую на случай, если пользователь захочет их восстановить, то речь шла бы о 45 Мбайт. Сегодня хорошая графическая карта может иметь 64 Мбайта памяти, но еще пару лет назад 4 Мбайта считалось большим объемом для графической платы, а избыточные данные необходимо было хранить в основной памяти компьютера. Множество людей все еще используют старые машины. Очевидно, что для Windows было бы непрактично управлять интерфейсом своих пользователей подобным образом.

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

Это проблема для нашего примера приложения. Мы поместили код рисования в конструктор Form1, который вызывается только один раз при запуске приложения, и невозможно вызывать конструктор снова, чтобы перерисовать фигуры, когда это потребуется позже.

В главе 9 при рассмотрении элементов управления нам ничего этого не нужно было знать, так как стандартные элементы управления достаточно развиты и могут правильно перерисовать себя, когда Windows об этом попросит. Это одна из причин, почему при программировании элементов управления вообще не нужно беспокоиться о реальном процессе рисования. Если мы берем на себя ответственность в приложении за рисование на экране, то нам нужно также гарантировать, что приложение будет отвечать правильно, когда Windows попросит перерисовать все или часть окна. В следующем разделе мы изменим пример так, чтобы это было возможно.

Рисование фигур с помощью OnPaint

Если приведенное выше объяснение заставило вас подумать, что рисование своего собственного пользовательского интерфейса будет очень сложной задачей, то не стоит волноваться. Это не так. Были приведены различные детали процесса, для того чтобы вы поняли, с какими проблемами столкнетесь. Но заставить приложение перерисовать себя, когда необходимо, — в действительности легкая задача.

Когда возникает необходимость, Windows уведомляет приложение, что требуется выполнить некоторую перерисовку изображения, инициируя событие Paint. Интересно то, что класс Form уже реализовал обработчик для этого события, поэтому не нужно создавать свой собственный. Можно воспользоваться этой архитектурой, исходя из факта, что обработчик Form1 для события Paint будет вызывать в процессе обработки виртуальный метод OnPaint(), передавая в него единственный параметр PaintEventArgs. Это означает, что для выполнения рисования необходимо просто переопределить метод OnPaint(). Мы создадим для этого новый пример, называемый DrawShapes. Как и раньше, определяем DrawShapes как приложение Windows, генерируемое с помощью Visual Studio.NET, и добавим следующий код класса Form1:

protected override void OnPaint(PaintEventArgs e) {

 Graphics dc = e.Graphics;

 Pen BluePen = new Pen(Color.Blue, 3);

 dc.DrawRectangle(BluePen, 0, 0, 50, 50);

 Pen RedPen = new Pen(Color.Red, 2);

 dc.DrawEllipse(RedPen, 0, 50, 80, 60);

 base.OnPaint(e);

}

Отметим, что метод OnPaint() объявлен как protected. OnPaint() обычно используется внутри класса, поэтому нет необходимости любому другому коду вне класса знать о его существовании.

PaintEventArgs является производным классом от EventArgs, используемого обычно для передачи информации о событиях. PaintEventArgs имеет два дополнительных свойства, из которых наиболее важным является экземпляр Graphics, уже настроенный и оптимизированный для рисования требуемой части окна. Это означает, что нам не нужно вызывать CreateGraphics(), чтобы получить контекст устройства в методе OnPaint(), — он уже существует. Мы вскоре рассмотрим другое дополнительное свойство, оно содержит более подробную информацию о том, какая область окна действительно нуждается в перерисовке.

В данной реализации метода OnPaint() мы сначала получаем ссылку на объект Graphics из PaintEventArgs, затем рисуем фигуры так же, как это делалось раньше. В конце вызывается метод OnPaint() базового класса. Этот шаг является важным. Мы переопределили OnPaint() для выполнения нашего собственного рисования, но возможно, что Windows должен выполнить свою собственную работу в процессе рисования, и такая работа будет связана с методом OnPaint() в одном из базовых классов .NET.

Для этого примера может оказаться, что удаление вызова base.OnPaint() не оказывает никакого влияния на работу, но никогда не пытайтесь удалить вызов. Это может привести к некорректному завершению работы Windows и непредсказуемым результатам.

OnPaint() будет также вызываться, когда приложение впервые запускается и окно приложения выводится в первый раз, поэтому нет необходимости дублировать код рисования в конструкторе, хотя по-прежнему нужно задать здесь цвет фона и все другие свойства формы. Это обычно задается либо добавлением команды явно, либо заданием цвета в окне свойств Visual Studio.NET:

private void InitializeComponent() {

 this.components = new System.ComponentModel.Container();

 this.Size = new System.Drawing.Size(300, 300);

 this.Text = "Draw Shapes";

 this.BackColor = Color.White;

}

Выполнение этого кода вначале дает те же результаты, что и в предыдущем примере, за исключением того, что теперь приложение ведет себя правильно, когда окно минимизируется или скрывается его часть.

Использование области вырезания

Пример приложения DrawShapes из предыдущего раздела иллюстрирует основные принципы, используемые при рисовании в окне, однако оно не очень эффективно. Причина в том, что оно пытается рисовать в окне все, независимо от того, сколько должно быть перерисовано. Рассмотрим ситуацию, показанную на следующем рисунке. После выполнения приложения DrawShapes было открыто другое окно, которое закрыло часть формы DrawShapes.

До сих пор все хорошо. А что произойдет, когда перекрывающее окно (в данном случае Task Manager) будет удалено, так что окно DrawShapes будет снова полностью видно? Windows, как обычно, пошлет форме событие Paint, чтобы она себя перерисовала. Прямоугольник и эллипс находится в верхнем левом углу клиентской области и поэтому видны все время, так что в действительности нет ничего, что требуется сделать в данном случае, помимо перерисовки белой фоновой области. Однако Windows этого не знает. В той степени, в какой это касается Windows, необходимо перерисовать часть окна. Это означает, что надо инициировать событие Paint, которое вызывается в нашей реализации OnPaint(). OnPaint() будет затем пытаться излишне перерисовать прямоугольник и эллипс.

В таком случае фигуры не нужно перерисовывать. Причина этого связана с контекстом устройства. Ранее говорилось, что контекст устройства внутри объекта Graphics, передаваемого в метод OnPaint(), будет оптимизирован операционной системой Windows для выполнения конкретной ближайшей задачи. Это означает, что Windows предварительно инициализирует контекст устройства информацией о том, какая область в действительности должна быть перерисована. Это прямоугольник, который был покрыт окном Task Manager на снимке экрана, приведенном выше. Во время использования GDI, область помеченная для перерисовки, называлась недействительной областью, но с появлением GDI+ терминология существенно изменилась, и эта область стала называться областью вырезания. Контекст устройства знает, какая это область, и поэтому прервет любые попытки рисования вне этой области и не будет передавать соответствующие команды рисования графической плате. Это звучит хорошо, но все равно здесь существует потенциальная потеря производительности. Мы не знаем какой объем обработки потребуется выполнить контексту устройства, чтобы определить, что рисование произойдет вне недействительной области. В некоторых случаях это может оказаться существенной величиной, так как определение того, какие пиксели необходимо изменить и в какой цвет может оказаться очень трудоемким (хотя хорошая графическая плата обеспечит аппаратное ускорение). Прямоугольник является достаточно легкой фигурой. Эллипс — труднее, поскольку необходимо вычислять положение кривой. Вывод текста потребует еще больших усилий, так как необходимо обрабатывать информацию в шрифте, чтобы определить форму каждой буквы, а каждая буква состоит из ряда линий и кривых, которые должны быть нарисованы по отдельности. Если, как в большинстве распространенных шрифтов, это будет шрифт с переменной шириной, т. е., когда у каждой буквы нет фиксированного размера и она занимает столько места, сколько ей требуется, то невозможно даже определить, сколько пространства займет текст, не выполнив предварительных громоздких вычислений

1 ... 62 63 64 65 66 67 68 69 70 ... 167
На этой странице вы можете бесплатно читать книгу C# для профессионалов. Том II - Симон Робинсон бесплатно.

Оставить комментарий