Лабораторная работа № 8-9




Разработка приложений с использованием GDI+


 

Содержание

 

1. Вводная часть

2. Создание приложения Windows Forms

3. Модификация приложения Windows Forms: подготовка интерфейса и добавление TabControl

4. Модификация приложения Windows Forms: вкладка «Просто фон!»

5. Модификация приложения Windows Forms: вкладка «Объекты и градиент»

6. Модификация приложения Windows Forms: вкладка «Мой монитор сломался?!»

7. Модификация приложения Windows Forms: вкладка «Векторные часы»

8. Модификация приложения Windows Forms: вкладка «Огонь!!!»

9. Модификация приложения Windows Forms: вкладка «Дождик»

10. Завершающая часть

11. О приложении к Лабораторной работе № 19


 

1. Вводная частьложение интерфейс

В этой работе будет рассмотрена расширенная работа с графикой GDI+ в C#. Фактически это «демонстрационная» лабораторная работа, призванная показать богатство возможностей «обычной» системной графики на примере обычного Windows Forms. А этих возможностей на поверку оказывается очень много...

Непосредственно о том, что такое GDI+ уже было рассказано ранее в предыдущей работе практикуму, но повторимся...

Что же такое GDI+? Официальная документация скромно называет её Class-based API, то есть основанным на классах интерфейсе прикладных программ для ОС Windows поколения «не GDI ». Так как она встроена в Windows XP и все последующие ОС вплоть до Windows 7, её называют частью этих операционных систем. Часто встречается также определение «библиотека» или «библиотека классов». В действительности, предоставляемый GDI+ набор классов является тонкой оболочкой над множеством обычных функций, реализованных в одной динамической библиотеке GdiPlus.dll.

Итак, GDI+ — это библиотека, призванная заменить существующий уже больше 11 (или 18, в зависимости от того, как считать) лет интерфейс GDI, являющийся графическим ядром предыдущих версий Windows. Она сочетает в себе (по крайней мере, по замыслу) все достоинства своего предшественника и предоставляет множество новых мощных возможностей. Кроме того, при её проектировании заранее ставилась цель наименее болезненного переноса приложений на 64-битные платформы.

Само слово GDI: Graphics Device Interface, Graphical Device Interface. «Устройство(-а) графического интерфейса» или «интерфейс графического устройства», но проще: «пользовательский интерфейс».

В чём будет заключаться суть нашего приложения? Хм... Во вкладках окна. На каждой вкладке будет демонстрация возможностей по работе с GDI+. Всё просто.

2. Создание приложения Windows Forms

 

Запускаем Visual Studio 2010, откроется Начальная страница:

Для начала, надо создать проект, для этого выполним последовательно: Файл -> Создать -> Проект … (также можно просто нажать сочетание клавиш Ctrl+Shift+N или пункт «Создать проект…» на Начальной странице):

 

Рис. 2.1 Создание нового проекта

 

Выберем слева в пункте Установленные шаблоны язык Visual C#, далее найдём в списке Приложение Windows Forms. Также здесь можно выбрать какой использовать «фреймворк» (набора компонентов для написания программ). В нашем случае выберем. NET Framework 4.


 

Рис. 2.2 Окно создания нового проекта

 

В поле Имя вводим LWP19GDIPlus — это название программы (выбрано по названию лабораторного практикума, номеру и названию работы). В поле Расположение указана конечная директория, где будет находиться весь проект. Выберем расположение удобное для быстрого поиска. В поле Имя решения вводится либо название программы «по умолчанию» из поля Имя автоматически, либо можно ввести своё собственное. Под этим именем будет создана конечная папка проекта (если Имя и Имя решения разные).


 

Рис. 2.3 Вводим данные нового проекта приложения Windows Forms

 

После нажатия клавиши ОК. мы увидим сформированный проект и исходный код приложения Windows Forms (не пустого изначально).

 

Рис. 2.4 Обозреватель решений: состав проекта приложения Windows Forms сформированного средой разработки

Теперь, можно откомпилировать созданную программу, нажав клавишу F5 (Отладка -> Начать отладку или нажав на иконку . Тем самым мы запускаем приложение в режиме отладки (и производим компиляцию debug-версии программы) (Debug выбрано изначально).

 

Рис. 2.5 Запуск приложения Windows Forms по конфигурации Debug

 

3. Модификация приложения Windows Forms: подготовка интерфейса и добавление TabControl

 

Для начала изменим размер нашей единственной формы. Для этого можно потянуть за уголок в нужном направлении на странице визуального представления формы 1. Но также размер можно менять на панели свойств этой формы. Для этого нужно поменять значение размера в пикселях (высоту и ширину) в поле Size.

ПРИМЕЧАНИЕ № 1: Для перехода на визуальное представление формы, необходимо двойным нажатием в обозревателе решений нажать на значок формы () или выбрать вкладку на панели вкладок с именем <имя формы>.cs [Конструктор].

Задаём следующие параметры формы на панели Свойства:

 

(Name) изменим с Form1.cs2 на LWP19Main

 

^ Поменяем внутреннее имя формы.

 

Text изменим с Form1 на Расширенная работа с GDI+ (C#)

 

^ Поменяем заголовок формы (то, что отображается в шапке приложения слева).

 

Icon изменим изображение (иконку) приложения

 

^ Необходим файл значка *.ico.

 

Size изменим со значений 300; 300 на 800; 600

 

^ Поменяем размер формы.

ПРИМЕЧАНИЕ № 2: Для того, чтобы поменять имя файла нашей формы, необходимо выполнить следующее: выделить в обозревателе решений значок формы () и нажать правую кнопку мыши, затем выбрать Переименовать. Ввести необходимое новое имя СОХРАНЯЯ расширение *.cs. После смены имени, автоматически поменяются имена проассоциированных непосредственно с формой файлов:

 

 

Разместим на форме единственный элемент: TabControl (), находится в группе Контейнеры панели элементов.

Растянем этот элемент до краёв формы и измени имя элемента:

 

TabControl

(Name): TB_Main
Anchor: Top, Bottom, Left, Right

 

Изменение последнего свойства потребуется, чтобы весь элемент менял размер при изменении размеров формы.

То, что должно получиться в итоге, показано на рисунке ниже:

 

Рис. 3.1 Модифицированная форма приложения и расстановка необходимых элементов управления


 

4. Модификация приложения Windows Forms: вкладка «Просто фон!»

 

Первая вкладка TabControl будет показана первой при старте приложения. На неё просто необходимо установить что-нибудь простое.

Для начала изменим текст самой вкладки. Выделим элемент управления и перейдём к свойствам (панель Свойства) элемента. Нам нужно свойство TabPages:

 

 

Жмём «...» и открываем окно редактирования вкладок:

 

Рис. 4.1 Редактор коллекции TabPage: меняем Text и Cursor

 

Для вкладки tabPage1 (имя не будем менять), изменим свойства:

 

TabPage

Text: Просто фон!
Cursor: No

 

В качестве фона вкладки будет выбрано некое изображение. Пусть это будет изображение с именем Background_Image.jpg (использованное в данной работе изображение можно получить в ZIP-архиве по ссылке в конец этого материала). Для добавления выполним действия: Проект -> Существующий элемент... (Shift + Alt + A), в открывшемся окне находим изображение и жмём Добавить.

Теперь приступаем к коду вкладки. Для начальной вкладки tabPage1 проинициализируем событие Paint (панель свойств, вкладка События):

 

 

Код события такой:

 

private void tabPage1_Paint(object sender, PaintEventArgs e)

{

// При создании вкладки и её прорисовки, вызываем метод DoPaint

// Используем аргумент e для получение параметров рисования (устройста и прямоугольной области)

DoPaint(e.Graphics, e.ClipRectangle);

}

 

Добавим и сам метод DoPaint в код под событие Paint:

 

protected void DoPaint(Graphics g, Rectangle clipBox)

{

RectangleF bounds = clipBox;

String welcome = "Расширенная работа с GDI+ (C#):: Вкладка \"Просто фон\"";

Bitmap bg = new Bitmap("/Background_Image.gif"); // Абсолютный путь до изображения

g.DrawImage(bg, bounds); // Рисуем и растягиваем изображение на весь фон вкладки

// Создаём градентную заливку для текста

LinearGradientBrush brush = new LinearGradientBrush(bounds,

Color.FromArgb(130, 255, 0, 0), Color.FromArgb(255, 0, 0, 255),

LinearGradientMode.BackwardDiagonal);

// Создаём форматирование текста и помещаем его в центре вкладки (по вертикали и горизонтали)

StringFormat format = new StringFormat();

format.Alignment = StringAlignment.Center;

format.LineAlignment = StringAlignment.Center;

// Задаём параметры шрифта

Font font = new Font("Verdana", 48, FontStyle.Bold);

g.DrawString(welcome, font, brush, bounds, format);

}

 

В самом начале файла формы LWP19Main.cs добавим следующую строчку:

 

using System.Drawing.Drawing2D;

 

Всё предельно просто. Комментарии расставлены. Собственно этим кодом был реализован простейший пример работы с GDI+.

Откомпилируем и запустим приложение (Debug). Увидим, что текст отрисовывается без сглаживания. Это связано с тем, что в Windows Forms по умолчанию отключается улучшенный режим отрисовки шрифтов. Включить можно, например, так (размещать код перед кодом рисования объекта):

g.SmoothingMode = SmoothingMode.AntiAlias;

 

В объекте Graphics имеется также соответствующее свойство InterpolationMode. Выигрыш в качестве в данном случае приводит к проигрышу в скорости, поэтому при использовании режима с наивысшим качеством InterpolationModeHighQualityBicubic медленные компьютеры могут выводить изображения больших размеров в течение нескольких секунд (речь идет об экранном выводе, то есть умеренно больших, так как бикубическая интерполяция изображений полиграфического разрешения может длиться минутами и на самых современных компьютерах)! Но только этот метод способен адекватно отображать, например картинку при уменьшении её до 25 процентов (и менее) от оригинала. Этот режим очень поможет различным автоматическим генераторам иконок (thumbnail) изображений в ASP.NET.

На качество (и скорость) вывода растров также влияют некоторые другие установки объектов Graphics. Перечислим их:

 

Метод Назначение
SmoothingMode Как уже говорилось, позволяет указать метод устранения ступенчатости (Anti Aliasing) при выводе примитивов – линий и геометрических фигур.
CompositingMode Устанавливает или отключает учет прозрачности при наложении растровых изображений.
CompositingQuality Управляет качеством расчета цветовых компонентов при наложении растров.
PixelOffsetMode Задает метод учета смещения пикселов при интерполяции. Грубо говоря, определяет, являются ли координаты пикселов (или их центров) целыми числами при расчетах.
RenderingOrigin Устанавливает позицию начальной точки при псевдосмешении (dithering) цветов в 8- и 16-битных режимах.

5. Модификация приложения Windows Forms: вкладка «Объекты и градиент»

 

Теперь поработаем с градиентами более с более близкого расстояния. Редактируем вторую вкладку tabPage2 следующим образом:

 

TabPage

Text: Объекты и градиент
Cursor: Hand

 

Рис. 5.1 Редактор коллекции TabPage: меняем Text и Cursor

 

На второй вкладке разместим сначала элемент Panel (, группа «Контейнеры» панели элементов), а затем три кнопки. Свойства всего этого дела (имена элементов не трогаем):


 

Panel

(Name): panel1
BackColor: Black
AutoScroll: True

 

Button

(Name): button1
Text: Прямоугольник

 

Button

(Name): Button2
Text: Треугольник

 

Button

(Name): Button3
Text: Круг

 

Расстановка элементов выглядит примерно так:

 

Рис. 5.2 Модифицированная форма приложения и расстановка необходимых элементов управления на второй вкладке

 

Кнопки находятся внутри элемента Panel (поверх).

Открываем код нашей формы LWP19Main (файл LWP19Main.cs). Ищем:

 

public LWP19Main()

{

InitializeComponent();

 

Добавляем после:

 

DoubleBuffered = true;

 

Ищем:

 

public partial class LWP19Main: Form

{

 

Добавляем после:

 

// Начало: вкладка "Объекты и градиент"

// Для работы с появлением/скрытием фигур

bool bDrawFigure1 = false;

bool bDrawFigure2 = false;

bool bDrawFigure3 = false;

// Создаём цвета

Color c1 = Color.FromArgb(51, 204, 51);

Color c2 = Color.FromName("Green");

Color c3 = Color.FromArgb(0, 0, 0);

Color c4 = Color.FromArgb(51, 104, 51);

// Конец: вкладка "Объекты и градиент"

Инициализируем событие Paint для элемента Panel второй вкладки:

 

private void panel1_Paint(object sender, PaintEventArgs e)

{

Graphics g = panel1.CreateGraphics();

// Задаём визуализацию со сглаживанием для объекта Graphics

g.SmoothingMode = SmoothingMode.HighQuality;

// Массив точек треугольника

Point[] triangle = { new Point(80, 150), new Point(200, 50), new Point(320, 150) };

// Задаём перья с цветами c1 и c2 и толщиной в 1 пиксель

Pen pen1 = new Pen(c1, 1);

Pen pen2 = new Pen(c2, 1);

// Заливка и цвета градиента

Brush brush1 = new LinearGradientBrush(triangle[0], triangle[2], c1, c4);

Brush brush2 = new LinearGradientBrush(triangle[0], triangle[2], c3, c2);

if (bDrawFigure1)

{

// Заливаем цветом прямоугольник

g.FillRectangle(brush2, new Rectangle(100, 150, 200, 100));

}

if (bDrawFigure2)

{

// Заливаем цветом треугольник

g.FillPolygon(brush2, triangle);

}

if (bDrawFigure3)

{

// Заливаем цветом эллипс

g.FillEllipse(brush1, new Rectangle(175, 175, 50, 50));

}

}

 

И последовательно инициализируем двойным нажатием события каждой кнопки. Событие Click для каждой из кнопок будет таким:

 

private void button1_Click(object sender, EventArgs e)

{

if (bDrawFigure1 == true) { bDrawFigure1 = false; }

else bDrawFigure1 = true;

panel1.Invalidate();

}

private void button2_Click(object sender, EventArgs e)

{

if (bDrawFigure2 == true) { bDrawFigure2 = false; }

else bDrawFigure2 = true;

panel1.Invalidate();

}

private void button3_Click(object sender, EventArgs e)

{

if (bDrawFigure3 == true) { bDrawFigure3 = false; }

else bDrawFigure3 = true;

panel1.Invalidate();

}

 

Компилируем (Debug) и запускаем. Переходим на закладку «Объекты и градиент» и нажимаем на кнопочки... Любуемся, как появляется «домик» с круглым окном...


6. Модификация приложения Windows Forms: вкладка «Мой монитор сломался!»

 

Следующая вкладка будет реализовывать вот что: откроем её, нажмём где-нибудь на свободном участке вкладки левую кнопку мыши и потащить курсор. Выделенный участок инвертируется (инвертируются его цветовая палитра) даже за пределами формы это такое взаимодействие высокоуровневой GDI+ и низкоуровневой GDI...

Создаём новую вкладку tabPage3. Для создания вкладки выделяем мышкой TabControl и на панели свойств в области команд жмём «Добавить вкладку». Свойства новой вкладки:

 

TabPage

Text: Объекты и градиент
Cursor: Cross

 

Накидаем на вкладку различных элементов, каких не важно. В нашем случае на вкладку было помещено две кнопки, два TextBox и один Label. Изменим свойства элементов управления как угодно? Изменим цвет фона элемента, параметры шрифты и прочее.


 

Рис. 6.1 Модифицированная форма приложения и расстановка необходимых элементов управления на третьей вкладке

 

Теперь, последовательно, для вкладки tabPage3 (нужно переключиться на неё в конструкторе и выделить область самой вкладки) инициализируем три события: MouseDown, MouseMove и MouseUp:

Код дополнительной переменной, всех событий и двух дополнительных методов будет таким:

 

Rectangle frameRect; // Вкладка "Мой монитор сломался?!"

private void tabPage3_MouseDown(object sender, MouseEventArgs e)

{

if (e.Button == MouseButtons.Left)

{

// Фиксируем стартовую точку

frameRect.Location = new Point(e.X, e.Y);

frameRect.Size = new Size(0, 0);

}

base.OnMouseDown(e); // Вызываем базовый метод

}

private void tabPage3_MouseMove(object sender, MouseEventArgs e)

{

if (e.Button == MouseButtons.Left)

{

DrawFrame(); // Стираем старый прямоугольник

frameRect.Width = e.X - frameRect.Left;

frameRect.Height = e.Y - frameRect.Top;

DrawFrame(); // Рисуем новый прямоугольник

}

base.OnMouseMove(e); // Вызываем базовый метод

}

private void tabPage3_MouseUp(object sender, MouseEventArgs e)

{

if (e.Button == MouseButtons.Left)

{

DrawFrame(); // Стираем старый прямоугольник

frameRect.Size = new Size(0, 0);

}

base.OnMouseUp(e); // Вызываем базовый метод

}

protected void DrawFrame()

{

if (!frameRect.Size.IsEmpty)

{

Rectangle r = RectangleToScreen(frameRect);

// Собственно, инвертируем цвета

ControlPaint.FillReversibleRectangle(r, Color.FromArgb(40, 0, 0, 160));

}

}

protected override void OnLoad(EventArgs e)

{

SetStyle(ControlStyles.UserPaint, true);

SetStyle(ControlStyles.AllPaintingInWmPaint, true);

SetStyle(ControlStyles.DoubleBuffer, true);

base.OnLoad(e);

}

 

Обратим внимание на метод OnLoad: здесь идёт включение режима двойной буферизации. Для его включения необходимо в окне, в которое производится отрисовка (например, элементе управления или форме) установить флаги UserPaint, AllPaintingInWmPaint и DoubleBuffer перечисления System.Windows.Forms.ControlStyles.

Вывод графики довольно заметно ускоряется (несмотря на необходимость дополнительного переноса на экран).

Компилируем (Debug) и запускаем. Переходим на третью вкладку и инвертируем цвета у всего, до чего дотягивается мышка...

 

7. Модификация приложения Windows Forms: вкладка «Векторные часы»

 

Данная вкладка отображает текущее время на стилизованном «аналоговом» циферблате (без использования изображений и сторонних ресурсов). В ней использован большой набор примитивов: прямоугольники, эллипсы и отрезки прямых. Для расчёта их координат используются не геометрические преобразования GDI+, а простые вычисления. Что действительно заслуживает внимания, так это отображение стрелок часов. Каждая стрелка рисуется всего одним вызовом метода DrawLine!

Для часиков создаём четвёртую вкладку tabPage4 со свойствами:


 

TabPage

Text: Векторные часы
Cursor: AppStarting

 

Добавляем на форму таймер (Timer) со свойствами:

 

Timer

(Name): timer1
Interval:  

 

Инициализируем событие первого таймера Tick:

 

private void timer1_Tick(object sender, EventArgs e)

{

tabPage4.Text = "Векторные часы: " + DateTime.Now.ToLongTimeString();

Invalidate();

}

 

Инициализируем событие Selected для главного элемента TabControl со следующим кодом:

 

private void TB_Main_Selected(object sender, TabControlEventArgs e)

{

if (e.TabPage.Name == tabPage4.Name) timer1.Enabled = true;

else timer1.Enabled = false;

}

 

Этот код нужен вот для чего: если выбираем активной вкладкой, вкладку номер четыре, то запускаем таймер.

Инициализируем событие загрузки формы Load:

private void LWP19Main_Load(object sender, EventArgs e)

{

SetStyle(ControlStyles.AllPaintingInWmPaint, true);

SetStyle(ControlStyles.DoubleBuffer, true);

SetStyle(ControlStyles.UserPaint, true);

}

 

И, наконец, инициализируем событие Paint вкладки tabPage4, а также дополнительный метод:

 

private Point RadialPoint(int radius, int seconds)

{

Point ptCenter = new Point(tabPage4.ClientRectangle.Width / 2, tabPage4.ClientRectangle.Height / 2);

double angle = -((seconds - 15) % 60) * Math.PI / 30; // Вычисляем угол на "окружности" (шаг)

Point ret = new Point(

ptCenter.X + (int)(radius * Math.Cos(angle)),

ptCenter.Y - (int)(radius * Math.Sin(angle)));

return ret;

}

private void tabPage4_Paint(object sender, PaintEventArgs e)

{

// Создаём объект часов

DateTime dt = DateTime.Now;

Graphics g = e.Graphics;

g.SmoothingMode = SmoothingMode.HighQuality;

// Вычисляем центральную точку для циферблата

Point ptCenter = new Point(tabPage4.ClientRectangle.Width / 2, tabPage4.ClientRectangle.Height / 2);

// Вычисляем радиус циферблата на основе размеров вкладки

int radius = Math.Min(tabPage4.ClientRectangle.Width, tabPage4.ClientRectangle.Height) / 2;

using (LinearGradientBrush br = new LinearGradientBrush(tabPage4.ClientRectangle, Color.White, Color.Azure, LinearGradientMode.BackwardDiagonal))

{

//br.SetSigmaBellShape(.5f, 1.0f);

// Заполняем циферблат градиентом

g.FillEllipse(br, ptCenter.X - radius, ptCenter.Y - radius, radius * 2, radius * 2);

}

// Рисуем эллипс цифербалата

using (Pen pen = new Pen(Color.Black))

g.DrawEllipse(pen, ptCenter.X - radius, ptCenter.Y - radius, radius * 2, radius * 2);

// Цикл минуты

for (int minute = 0; minute < 60; minute++)

{

Point pt = RadialPoint(radius - 10, minute);

using (SolidBrush br = new SolidBrush(Color.Black))

{

if ((minute % 5) == 0)

g.FillRectangle(br, pt.X - 3, pt.Y - 3, 6, 6);

else

g.FillRectangle(br, pt.X - 1, pt.Y - 1, 2, 2);

}

}

// Рисуем стрелку часов (от центра до позиции на циферблате)

using (Pen pen = new Pen(Color.Black, 8))

{

pen.StartCap = LineCap.Flat;

pen.EndCap = LineCap.DiamondAnchor;

float[] compVals = new float[] { 0.0f, 0.2f, 0.5f, 0.7f, 0.9f, 1.0f };

pen.CompoundArray = compVals;

g.DrawLine(pen, RadialPoint(15, 30 + dt.Hour * 5 + dt.Minute / 12), RadialPoint((int)(radius * 0.75), dt.Hour * 5 + dt.Minute / 12));

}

// Рисуем минутную стрелку

using (Pen pen = new Pen(Color.FromArgb(100, 0, 0, 0), 6))

{

pen.StartCap = LineCap.RoundAnchor;

pen.EndCap = LineCap.Round;

g.DrawLine(pen, RadialPoint(15, 30 + dt.Minute), RadialPoint((int)(radius * 0.8), dt.Minute));

}

// Русуем секундную стрелку

using (Pen pen = new Pen(Color.FromArgb(50, 150, 50, 25), 4))

{

pen.CustomEndCap = new AdjustableArrowCap(4, 6, true);

g.DrawLine(pen, RadialPoint(20, dt.Second + 30), RadialPoint(radius - 2, dt.Second));

}

using (SolidBrush br = new SolidBrush(Color.FromArgb(100, Color.Wheat)))

g.FillEllipse(br, ptCenter.X - 5, ptCenter.Y - 5, 10, 10);

}

 

Компилируем (Debug) и запускаем. Переходим на закладку «Векторные часы», чем запускаем таймер, заголовок вкладки будет отображать текущее время, а на самой вкладке видим круглые стрелочные часы собранные из примитивов. Мило!

 

8. Модификация приложения Windows Forms: вкладка «Огонь!!!»

 

На этот раз будем имитировать горение огня (стена огня). Для создания эффекта огня будем использовать генерацию случайных чисел каждую миллисекунду. Однако, отображать сам огонь будем не на вкладке TabControl, а в новой форме. Это связано с «некрасивым» обновлением вкладки при рисовании огня (возникает заметное мерцание).

Создаём новую вкладку со свойствами:

 

TabPage

Text: Огонь!!!
Cursor: No

 

Добавляем новую форму с именем LWP19Fire и не меняем какие-либо свойства формы (ну, разве что значок). Добавить форму можно так: Проект -> Добавить форму Windows... (а затем переименовать класс формы); либо использовать стандартное добавление: Проект -> Добавить новый элемент... (Ctrl + Shift + A) и далее указать Имя: LWP19Fire).

Редактируем код события Selected для TabControl. Добавляем туда строчки:

 

if (e.TabPage.Name == tabPage5.Name)

{

tabPage5.BackColor = Color.Black;

LWP19Fire Fire = new LWP19Fire();

Fire.ShowDialog();

}

Выбор данной вкладки («Огонь!!!») вызовет экземпляр модальной формы LWP19Fire.

Теперь редактируем код новой формы. Вначале добавим следующий код (в директивы using):

 

using System.Drawing.Imaging;

 

Теперь, найдём в коде формы строчки:

 

public partial class LWP19Fire: Form

{

 

Добавим после:

 

// Начало: вкладка "Огонь!!!"

private Bitmap buf; // Буфер для графики

private Random random = new Random(DateTime.Now.Millisecond); // Истоник данных (Random)

private const int width = 640;

private const int height = 320;

// Конец: вкладка "Огонь!!!"

 

Метод LWP19Fire() редактируем так:

 

public LWP19Fire()

{

InitializeComponent();

// Инициализируем источник для последующего использования

random = new Random();

// Устанавливаем начальные параметры вкладки

this.Text = "Расширенная работа с GDI+ (C#):: форма для вкладки \"Огонь!!!\"";

this.ClientSize = new Size(width, height);

this.MaximizeBox = false;

this.FormBorderStyle = FormBorderStyle.FixedDialog;

this.BackColor = Color.Black;

SetStyle(ControlStyles.Opaque, true);

// Вызываем событие рисования (для метода DoFire)

this.Paint += new PaintEventHandler(this.DoFire);

// Генерируем палитру 640x480 (на основе Bimtap с 256 цветами на пиксель)

buf = new Bitmap(width, height, PixelFormat.Format8bppIndexed);

ColorPalette pal = buf.Palette;

// Заполняем палитру, используя 64-цветные блоки:

// чёрный с красным, красный с желтым, желтый с белым, белый.

// Так как каждый диапазон составляет 64 цветов и RGB охватывает 256 значений,

// использовуем левый сдвиг (<<) по массиву

for (int i = 0; i < 64; i++)

{

pal.Entries[i] = Color.FromArgb(i << 2, 0, 0);

pal.Entries[i + 64] = Color.FromArgb(255, i << 2, 0);

pal.Entries[i + 128] = Color.FromArgb(255, 255, i << 2);

pal.Entries[i + 192] = Color.FromArgb(255, 255, 255);

}

buf.Palette = pal;

}

 

И добавим метод для переопределённого события Paint формы:

 

// Метод рисования "огня"

private void DoFire(object src, PaintEventArgs e)

{

// Блокируем растр, чтобы мы смогли записывать в него напрямую

BitmapData buflock = buf.LockBits(

new Rectangle(Point.Empty, buf.Size),

ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);

// Записываем "огонь"

// Используем указатели, потому блок "unsafe"

unsafe

{

// Извлекает указатель на верхнюю строку развёртки изображения

Byte* bufdata = (Byte*)buflock.Scan0;

Byte* bufbottom = bufdata + ((height - 1) * width);

Byte* i; int v;

// Используем случайную нижней линию в качестве источника "огня"

for (int x = 0; x < width; x++)

{

*(bufbottom + x) = (Byte)random.Next(100, 255);

}

// Для каждого пикселя в изображении,

// среднее значение пикселей, один справа, один снизу

// и один в левом нижнем углу. Достигаем порог 0,

// и пишем на текущей позиции

for (i = bufdata; i < bufbottom; i++)

{

v = *i + *(i + 1) + *(i + height) + *(i + height - 1);

v /= 4;

if (v < 0) v = 0;

*i = (Byte)v;

}

}

// Разблокируем изображение и рисуем на его на форме

buf.UnlockBits(buflock);

e.Graphics.DrawImageUnscaled(buf, 0, 0);

this.Invalidate();

}

 

Компилируем (Debug) и запускаем. Переходим на закладку «Огонь!!!», любуемся на огонь (можно любоваться вечно, не правда ли?).

 

9. Модификация приложения Windows Forms: вкладка «Дождик»

 

Последняя вкладка будет вызывать дождь. Внутри вкладки. Опять же, как и в случае с часами и огнём, не будем использовать сторонних ресурсов. Разве что можно добавить звуки дождя.

Для этого нам понадобиться новая вкладка со свойствами:

 

TabPage

Text: Дождик
Cursor: PanSouth

 

Один новый таймер со свойствами:

 

TabPage

(Name): timer2
Interval:  

 

Один PictureBox, растянутый на всю новую вкладку со свойствами:


 

PictureBox

(Name): pictureBox1
Anchor: Top, Bottom, Left, Right

 

Код для события Selected элемента TabControl:

 

if (e.TabPage.Name == tabPage6.Name) timer2.Enabled = true;

else timer2.Enabled = false;

 

После закрывающей скобки предыдущего события в файле LWP19Main.cs добавим:

 

// Начало: вкладка "Дождик"

private static Image _raindropImage;

private readonly List<Raindrop> _raindrops;

// Конец: вкладка "Дождик"

#region Класс Raindrop

private class Raindrop

{

private static readonly Random random = new Random(DateTime.Now.Millisecond);

// Удаление от экрана

private float _depth;

// Положение

private PointF _location;

// Прямоугольник для отрисовки

private Rectangle _rectangleToDraw;

// Скорость

private PointF _speed;

public Raindrop(Rectangle rectangleToDraw)

{

_rectangleToDraw = rectangleToDraw;

SetRandomLoc();

}

public void UpdatePosition()

{

// Вертикальная скрость

_location.Y += _speed.Y;

// Горизонтальная скорость

_location.X += _speed.X;

if (_location.Y > _rectangleToDraw.Height)

SetRandomLoc();

}

private void SetRandomLoc()

{

// Максимально удаление

const int MAX_DEPTH = 10;

_depth = random.Next(MAX_DEPTH) + 1;

const int MIN_SPEED = 100;

const int SPEED_DISPERSION = 20;

_speed.X = 5;

// Чем дальше капля от экрана, тем она медленнее падает

_speed.Y = (MIN_SPEED + random.Next(SPEED_DISPERSION)) / _depth;

// Необходимо расширить область, в которой появляются капли

const int DY = 200;

int locX = random.Next(_rectangleToDraw.Width + DY) - DY;

int locY = -random.Next(_rectangleToDraw.Height) - _raindropImage.Height;

// Точка возникновения капли

_location = new PointF(locX, locY);

}

public void Draw(Graphics g)

{

// Масштаб текстуры

float scale = 2 / _depth;

g.DrawImage(_raindropImage, _location.X, _location.Y, _raindropImage.Width * scale,

_raindropImage.Height * scale);

}

}

#endregion

 

Инициализируем событие второго таймера Tick:

 

private void timer2_Tick(object sender, EventArgs e)

{

// Обновляем положение капель

foreach (Raindrop raindrop in _raindrops)

raindrop.UpdatePosition();

// Перерисовываем картинку

pictureBox1.Refresh();

}

 

И событие Paint для PictureBox:

 

private void pictureBox1_Paint(object sender, PaintEventArgs e)

{

// Отрисовываем капли

foreach (Raindrop raindrop in _raindrops)

raindrop.Draw(e.Graphics);

}

 

Готово. Можно компилировать и проверять работоспособность.

 

10. Завершающая часть

 

Компилируем приложение (Release) и запускаем. После запуска на первой вкладке видим (можно менять размер формы и наблюдать за изменениями при рисовании):

 

Рис. 10.1 Модифицированное приложение Windows Forms: вкладка «Просто фон!»

 

Переходим на следующую вкладку и нажимаем на кнопки:


 

Рис. 10.2 Модифицированное приложение Windows Forms: вкладка «Объекты и градиент»

 

Переходим на следующую вкладку и жмём левую кнопку мыши на пусто месте. Ведем курсор в любой угол экрана:

 

Рис. 10.3 Модифицированное приложение Windows Forms: вкладка «Мой монитор сломался?!»

 

На следующей вкладке запускаются часы:

Рис. 10.4 Модифицированное приложение Windows Forms: вкладка «Векторные часы»

 

А на следующий горит огонь:

 

Рис. 10.5 Модифицированное приложение Windows Forms: вкладка «Огонь!!!»

 

Завершаем всю дождём:

Рис. 10.6 Модифицированное приложение Windows Forms: вкладка «Дождик»

 

Если требуется добавить звуки дождя (есть на чём услышать):

Отредактируем код события Selected для последней вкладки:

 

if (e.TabPage.Name == tabPage6.Name)

{

timer2.Enabled = true;

player.SoundLocation = "/Raindrop.wav";

player.Play();

}

else

{

timer2.Enabled = false;

player.Stop();

}

 

И добавим директиву using в начало файла с кодом:

 

using System.Media;

 

И добавим до метода Selected строчку с кодом:

 

SoundPlayer player = new SoundPlayer();

 

11. О приложении к Лабораторной работе № 19

 

Получившуюся программу (LWP19GDIPlus.exe), собранную из кусков кода приведённых в данной лабораторной работе, а также архив с фоновым изображением для первой вкладки (Background_Image.zip), использованный в данной работе, можно загрузить по ссылке в конце этого материала.

Размещено на Allbest.ru



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2020-12-05 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: