Программирование мобильных устройств на платформе .NET Compact Framework - Иво Салмре
Шрифт:
Интервал:
Закладка:
//должен быть перерисован, как только представится возможность
//------------------------------------------------------------
this.Invalidate();
}
//----------------------------------------------------------------
//Ради интереса подсчитаем, сколько раз осуществлялась перерисовка
//----------------------------------------------------------------
int m_paintCount;
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) {
//--------------------------------------------
//ВАЖНО: Вызвать базовый класс и позволить ему
//выполнить работу по рисованию
//--------------------------------------------
base.OnPaint(e);
//Увеличить на единицу значение счетчика вызовов
m_paintCount = m_paintCount + 1;
//-------------------------------------------------------------------
//Важно:
//Вместо того чтобы создавать объект Graphics, мы получаем его
//на время данного вызова. Это означает, что освобождать память путем
//вызова метода .Dispose() объекта - не наша забота
//-------------------------------------------------------------------
System.Drawing.Graphics myGfx;
myGfx = e.Graphics;
//Нарисовать прямоугольник
myGfx.FillRectangle(m_RectangleBrush, 0, 0, this.Width,this.Height);
//Нарисовать текст
myGfx.DrawString("Button! Paint: " + m_paintCount.ToString(), this.Parent.Font, m_TextBrush, 0, 0);
} //конец функции
} //конец класса
Листинг 11.10. Код, который должен быть помещен в форму для создания экземпляра пользовательского элемента управления//--------------------------------------------------------------
//Этот код будет подключен в качестве нашего обработчика событий
//--------------------------------------------------------------
private void CallWhenButtonTurningBlue(object sender, System.EventArgs e) {
System.Windows.Forms.MessageBox.Show("Button is about to turn blue!");
}
//Наша новая кнопка
myButton m_newControl;
//----------------------------------------------
//Эта функция подключается для обработки событий
//щелчка на кнопке Button1
//----------------------------------------------
private void button1_Click(object sender, System.EventArgs e) {
//----------------------------------------------
//Для простоты мы допускаем существование только
//одного экземпляра элемента управления.
//----------------------------------------------
if (m_newControl != null) {
return;
}
//Создать экземпляр нашей кнопки
m_newControl = new myButton();
//Указать ему его местоположение внутри родительского объекта
m_newControl.Bounds = new Rectangle(10, 10, 150, 40);
//-------------------------------
//Присоединить обработчик событий
//-------------------------------
m_newControl.EventButtonTurningBlue += new System.EventHandler(this.CallWhenButtonTurningBlue);
//Добавить его в список элементов управления данной формы.
//Это сделает его видимым
this.Controls.Add(m_newControl);
}
Где рисовать — на экране или вне экрана?
Создание привлекательной графики — в равной степени и искусство, и ремесло; под этим подразумевается, что одинаково важная роль принадлежит как используемым методикам, так и планированию. Для того чтобы нарисовать на экране одиночный прямоугольник или одиночный фрагмент текста, требуется очень мало времени. Рисование же сложной диаграммы или игрового поля на лету в видимой области экрана, скорее всего, заставят пользователя дожидаться появления результатов в течение некоторого времени. Рассмотрим случай сложной диаграммы, на рисование которой уходит 1,5 секунды. Эта длительность заметно превышает порог человеческого восприятия, и глаз человека в состоянии различить довольно много событий, которые могут произойти на протяжении такого временного промежутка. Если рисование диаграммы состоит из стадий рисования фонового изображения, вычерчивания и сопровождения надписями осей диаграммы, размещения точек, соответствующих фактическим данным, и генерации таблицы ключей, в которой каждый набор данных идентифицируется определенным цветом, то процесс построения диаграммы, происходящий на глазах у пользователя, будет представлять собой весьма неприглядную картину. Результат будет еще более плачевным, если получаемая графика предназначена для динамической игры; в этом случае пользователь столкнется с мерцанием экрана, сопровождающим перерисовку элементов игры при их перемещениях на экране.
Другая причина, по которой не рекомендуется делать процесс рисования видимым для пользователя, заключается в том, что область растровых изображений пользовательского интерфейса обычно не является постоянной. В отличие от внеэкранных битовых карт, принадлежащих вашему коду, экран является разделяемым ресурсом, который совместно используется окнами и элементами управления всех выполняющихся приложений, а также операционной системой. Большинством операционных систем поддержка изображений, которые пользователь может рисовать на поверхности пользовательского интерфейса, не предусматривается. Это означает, что любые изображения, созданные вами в области пользовательского интерфейса, будут утеряны, если окно, в котором ваш код выполнял рисование, будет перекрыто окном другого приложения.
Гораздо менее уязвимым с точки зрения эстетики процесса рисования и подверженности ошибкам является рисование на внеэкранном буфере с последующим копированием результатов на передний план после того, как создание рисунка будет завершено. Кроме того, решения этого типа отличаются лучшей переносимостью, их проще проектировать, сопровождать и отлаживать. Вместо того чтобы погружаться во все тонкости модели рисования, используемой операционной системой, вы можете организовать все так, что изображение будет в максимально возможной степени при надлежать вашему приложению, выполняющему операции рисования в собственном строго определенном окружении и взаимодействующему непосредственно с пользовательским интерфейсом лишь тогда, когда эта работа будет закончена. Подход, основанный на использовании внеэкранных буферов для объектов рисования, почти всегда является наиболее оптимальным для выполнения графических операций любой степени сложности, даже если при этом вам и придется использовать вывод курсора ожидания на период визуализации изображения.
Как уже обсуждалось в предыдущих разделах, в .NET Compact Framework имеются два неплохих способа переноса внеэкранных рисунков в пользовательский интерфейс. Во-первых, это использование элемента управления PictureBox и задание посредством его свойства Image того битового образа, рисование которого вы только что завершили (например, pictureBox1.Image = myNewBitmap).
Второй способ заключается в получении объекта Graphics для того элемента интерфейса, в котором он будет прорисовываться (обычно таковым является объект Form) и вызове метода Graphics.DrawImage() для переноса изображения:
//Получить объект Graphics для формы
System.Drawing.Graphics gfx;
gfx = this.CreateGraphics();
//Нарисовать изображение в объекте с использованием
//начальной точки с координатами x=10, y=15);
gfx.DrawImage(myBitmap, 10, 15);
Следует подчеркнуть, что для метода Graphics.DrawImage() предусмотрено несколько перегруженных версий. Та, которая фигурировала выше, является самой простой и работает быстрее остальных; она просто копирует биты из одного битового образа в другой. Другие перегруженные версии обладают более развитыми возможностями, что позволяет им выполнять такие, например, операции, как перенос в целевое изображение строго определенной области исходного изображения, растягивание или сжатие копируемых изображений или использование масок прозрачности, позволяющих делать прозрачными отдельные области изображения, через которые могут "просматриваться'' другие изображения. Каждая из этих разновидностей метода выполняет определенное преобразование при копировании изображения из объекта-источника в объект назначения. В общем случае, чем сложнее преобразование изображения, которое требует выполнить ваш код, тем дороже оно обходится с точки зрения производительности.
Для поддержания производительности приложения на приемлемом уровне лучше всего стремиться к тому, чтобы исходное и целевое растровые изображения в максимальной степени совпадали друг с другом. По возможности стремитесь к тому, чтобы при переносе изображений создавались их точные битовые копии, а пиксели не растягивались и не сжимались. Маски прозрачности следует использовать лишь тогда, когда в этом есть необходимость. Хотя к .NET Compact Framework это и не относится, но другие среды выполнения могут предлагать вам выбрать количество цветов, которые должны использоваться в изображении, и указать битовую глубину цвета, используемую для поддержания этой информации. Совпадение этой информации для исходного и целевого изображений способно существенно улучшить производительность. Общая задача заключается в сведении к минимуму любых расхождений между исходным и конечным изображениями, чтобы операция переноса изображения была максимально приближена к операции простого копирования битов в память. Оптимизация таких операций для достижения высокой производительности обычно обеспечивается на всех платформах.