Тема: «Работа с графической информацией»
План
1. Компонент для работы с графикой
2. Компонент ImageList
3. Работа с таймером
4. Элементы анимации
1. Компонент для работы с графикой
Нередко возникает потребность украсить свое приложение изображениями. Для отображения графики на форме используется компонент PictureBox.
У компонента есть еще ряд свойств.
|
Свойство |
Описание |
|
Name |
Имя компонента для обращения к нему в коде |
|
ErrorImage |
Изображение, отображаемое при невозможности загрузки у казанного изображения |
|
Image |
Изображение, отображаемое в компоненте |
|
InitialImage |
Изображение, отображаемое во время загрузки основного изображения. Если основное изображение грузится долго. |
|
SizeMode |
Задает тип масштабирования изображения в компоненте: Normal - не масштабировать, StretchImage - растянуть изображение до размеров компонента, AutoSize - растянуть компонент до размеров изображения, CenterImage - отцентрировать изображение в компоненте не изменяя размер, Zoom - вписать изображение в компонент с сохранением пропорций. |
|
WaitOnLoad |
Включает режим приостановки работы приложения до окончания полной загрузки изображения в компонент. По умолчанию режим выключен. |
Основным свойством является свойство Image, которое может задаваться как при конструировании формы ( в этом случае нужно указать имя графического файла на диске), так и программно при выполнении кода.
Создание форм с фоновым рисунком
Одним из способов использования графики в приложении является задание фонового изображения для формы. Для этого необходимо выполнить ряд действий:
- в свойстве BackGroundImage формы укажите путь к нужному графическому файлу;
- в свойстве BackgroundImageLayout укажите тип масштабирования фонового изображения. Чаще всего указывают значение "Stretch".
Рассмотренные свойства также имеет большое число компонентов. Следовательно, фоновое изображение можно задать и для них.
Работа с классами Color и SystemColors
При работе с цветом в программах необходимо указывать значение нужных цветов. При этом часто цвет задается его именем или числовым обозначением. Для задания нужного цвета с C# имеется встроенный статический класс Color, который включает в себя большое количество свойств, обозначающих разные оттенки цвета. Если при написании сода ввести имя класса Color и поставить точку, то в качестве подсказки откроется список с огромным список свойств-цветов.
Например, присвоим форме красный цвет фона:
BackColor = Color.Red;
Присвоим текстовому полю светло-зеленый цвет фона:
textBox1.BackColor = Color.LightGreen;
Класс Color также имеет ряд методов, позволяющих работать с цветами:
Color.FromArgb(int a, Color clr) задает прозрачность заданного цвета. При a=0 цвет не видимый, при a=255 - не прозрачный. Обратите внимание, что прозрачные цвета поддерживает только компонент PictureBox.
Например, присвоим компоненту PictureBox красный цвет фона прозрачностью 100:
pictureBox1.BackColor = Color.FromArgb(100, Color.Red);
Color.FromArgb(int r, int g, int b) определяет цвет путем задания значений для составляющих его цветов (red, green, blue). Удобно использовать, если разработчик подобрал в графическом редакторе нужный оттенок и хочет применить его в приложении.
Например, присвоим форме цвет по значениям его составляющих:
BackColor = Color.FromArgb(70,120,50);
Color.FromArgb(int a, int r, int g, int b) позволяет задать цвет путем задания значений для его составляющих (red, green, blue), но в качестве первого параметра нужно указать прозрачность полученного оттенка. Напоминаем, что прозрачные цвета поддерживает только компонент PictureBox.
Например, присвоим компоненту PictureBox цвет по значениям его составляющих и прозрачностью 100:
pictureBox1.BackColor = Color.FromArgb(100,70,120,50);
Color.FromName(string name) - получает цвет по его имени. В качеcтве имени можно использовать любое имя из доступных в классе Color. Регистр символов в имени не важен
Например:
Color.FromName("red")
Color.FromName("lightgreen")
Каждое свойство, в котором хранится цвет, позволят выполнить обратное преобразование: по цвету получить его название или числовое значение. Для это используют два метода:
ToArgb() - возвращает числовое значение цвета
ToKnowColor() - возвращает название цвета
Например:
BackColor.ToArgb() - вернет числовое значение цвета фона формы
textBox1.BackColor.ToKnowColor() - вернет название цвета фона текстового поля
Класс SystemColors
Данный класс позволяет работать не со стандартными оттенками цветов, а с системными цветами - цветами темы оформления Windows. Как известно, пользователь может задавать произвольные темы оформления на своем рабочем месте. Если в коде компонента цвет задается с помощью конкретного оттенка, то независимо от выбранной темы, компонент будет всегда отображаться в заданном оттенке.
Класс SystemColors позволяет задавать не оттенки цветов, а оттенки элементов интерфейса операционной системы: цвет фона окна, цвет заголовка окна, цвет активного элемента в окне, цвет пункта меню и т.п. Все эти оттенки определяются темой оформления. Если Вы будете использовать именно их, то цветовое оформление приложения всегда будет адаптироваться под тему оформления ОС.
Если Вы задаете цвет в режиме конструирования формы, то в выпадающем списке выбирайте цвета на закладке "Система".
Если Вы задаете цвет в коде, то Color.Цвет используйте SystemColors.ЦветКомпонента.
Например:
BackColor=SystemColors.Control; //фону форму присвоили стандартный цвет компонентов в ОС
textBox1.Color=SystemColors.Window; //фону поля присвоили цвет фона оконных элементов в ОС
Открытие и сохранение графических файлов
Для работы с графическими файлами будем использовать стандартные диалоги OpenFileDialog и SaveFileDialog. При этом в свойстве Filter данных диалогов нужно будет указать список доступных форматов графических файлов. В языке автоматически поддерживаются следующие форматы графики:
- bmp;
- jpeg;
- gif;
- png;
- tiff;
- wmf;
- emg;
- icon.
Например, пусть приложение поддерживает 4 первых формата. В диалогах в свойстве Filter можно ввести следующее определение:
Файлы BMP|*.bmp|Файлы JPG, JPEG|*.jpg;*.jpeg|Файлы GIF|*.gif|Файлы PNG|*.png
Для загрузки графического файла в компонент PictureBox можно использовать команду:
if (openFileDialog1.ShowDialog() == DialogResult.OK)
pictureBox1.Image=Image.FromFile(openFileDialog1.FileName);
Сохранение графики в различные форматы
При сохранении графической информации пользователь может в диалоге указать нужный формат (bmp, jpeg, gif, png и т.п.). В результате программа должна выполнить конвертирование графики в заданный формат и сохранить ее на диске в виде файла.
Для этого используется метод вида:
pictureBox1.Image.Save(имя_файла, формат);
где формат задается, как одно из значений типа System.Drawing.Imaging.ImageFormat.
Пример. Пусть на форме имеется диалог SavePictureDialog, для которого задали свойство Flter, как указано выше (4 формата). Напишем код сохранения графики.
//в диалоге сохранения очищаем имя файла
saveFileDialog1.FileName = "";
//если в диалоге сохранения не указано имя файла, то завершавем работу
if (saveFileDialog1.ShowDialog()!=DialogResult.OK) return;
//проверяем номер указанного типа файла из фильтра (нумерация с 1)
switch (saveFileDialog1.FilterIndex)
{
//если указан первый тип (bmp)
case 1:
//задаем расширение по умолчанию
saveFileDialog1.DefaultExt = "bmp";
//сохраняем содержимое PictureBox в формат BMP
pictureBox1.Image.Save(saveFileDialog1.FileName,System.Drawing.Imaging.ImageFormat.Bmp);
break;
//аналогично, если выбран 2 тип (jpg)
case 2:
saveFileDialog1.DefaultExt = "jpg";
pictureBox1.Image.Save(saveFileDialog1.FileName,System.Drawing.Imaging.ImageFormat.Jpeg);
break;
//аналогично, если выбран 3 тип (gif)
case 3:
saveFileDialog1.DefaultExt = "gif";
pictureBox1.Image.Save(saveFileDialog1.FileName,System.Drawing.Imaging.ImageFormat.Gif);
break;
//аналогично, если выбран 4 тип (png)
case 4:
saveFileDialog1.DefaultExt = "png";
pictureBox1.Image.Save(saveFileDialog1.FileName,System.Drawing.Imaging.ImageFormat.Png);
break;
}
Изменение размера изображения
Для изменения размера изображения создают новый экземпляр класса Bitmap с помощью конструктора вида:
Bitmap temp = new Bitmap (изображение, новая_ширина, новая_высота);
Например, пусть в компоненте PictureBox находится изображение. Необходимо изменить его размер на 800Х600 и снова отобразить в этом же компоненте.
//создаем экземпляр класса с измененным размером исходного изображения
Bitmap temp = new Bitmap (PictureBox1.Image, 800, 600);
//отображаем результат в компоненте
PictureBox1.Image=temp;
Пример. В рассмотренном коде изображение меняется под конкретно заданные значения. Пусть пользователь задает произвольные значения ширины и высоты в текстовых полях (textBox1 - ширина, textBox2 - высота)ю
//создаем экземпляр класса с измененным размером исходного изображения
Bitmap temp = new Bitmap (pictureBox1.Image, Convert.ToInt32(textBox1.Text), Convert.ToInt32(textBox2.Text));
//отображаем результат в компоненте
pictureBox1.Image=temp;
Рассмотренные примеры требует указать ширину и высоту. Если указывать эти две величины, то может нарушиться коэффициент соотношения сторон рисунка. Поэтому целесообразнее поступать так:
- рассчитать коэффициент пропорции по формуле ширина / высота;
- указывайте одно из значений: если указана ширина, то высота рассчитывается как ширина / коэффициент; если указана высота, то ширина рассчитывается как высота * коэффициент.
Пример. Пусть в рассмотренных выше примере на форме есть одно текстовое поле для указания новой ширины рисунка. Тогда в код нужно внести изменения:
//рассчитываем коэффициент пропорции исходного изображения (ширина / высота)
double p=Convert.ToDouble(pictureBox1.Image.Width) / Convert.ToDouble(pictureBox1.Image.Height);
//создаем экземпляр класса с измененным размером исходного изображения
//ширину берем из поля
//высота рассчитывается как ширина / коэффициент p и конвертированная в целое значение
Bitmap temp = new Bitmap (pictureBox1.Image, Convert.ToInt32(textBox1.Text), Convert.ToInt32(Convert.ToInt32(textBox1.Text)/p));
//отображаем результат в компоненте
pictureBox1.Image=temp;
Пример. Пусть в рассмотренных выше примере на форме есть одно текстовое поле для указания новой высоты рисунка. Тогда в код нужно внести изменения:
//рассчитываем коэффициент пропорции исходного изображения (ширина / высота)
double p=Convert.ToDouble(pictureBox1.Image.Width) / Convert.ToDouble(pictureBox1.Image.Height);
//создаем экземпляр класса с измененным размером исходного изображения
//ширина рассчитывается как высота * коэффициент p и конвертированная в целое значение
//высоту берем из поля
Bitmap temp = new Bitmap (pictureBox1.Image, Convert.ToInt32(Convert.ToInt32(textBox1.Text)*p), Convert.ToInt32(textBox1.Text));
//отображаем результат в компоненте
pictureBox1.Image=temp;
Работа с буфером обмена
Существует возможность обмениваться графической информацией через буфер обмена. Для этого можно использовать специальный класс Clipboard, который имеет методы копирования и вставки.
Для копирования содержимого компонента PictureBox в буфер обмена можно использовать код:
Clipboard.SetImage(pictureBox1.Image);
Для вырезания содержимого компонента PictureBox в буфер обмена можно использовать код:
Clipboard.SetImage(pictureBox1.Image);
//очищаем компонент pictureBox1
pictureBox1.Image=null;
Для вставки содержимого буфера обмена в компонент PictureBox используют метод:
pictureBox1.Image = (Image)Clipboard.GetImage();
Вставка содержимого буфера обмена в компонент PictureBox связана с проблемой формата данных. Если в буфере находится не графика, то попытка вставки приведет к ошибке. Поэтому нужно проверять формат информации в буфере перед вставкой. Это можно сделать с помощью кода:
// если в буфере обмена графическая информация
if (Clipboard.GetImage()!=null)
// вставляем содержание буфера обмена
pictureBox1.Image = (Image)Clipboard.GetImage();
Настройка контекстного меню
Часто команды работы с буфером обмена помещают в контекстное меню. команды "Вырезать", "Копировать", "Вставить" могут быть активны и не активны в зависимости от состояния информации в компоненте PictureBox.
Например, если в компоненте PictureBox нет данных, то команды "Вырезать" и "Копировать" нужно отключить. Если в буфере обмена не графическая информация, то команду "Вставить" нужно отключить.
Рассмотрим работу с контекстным меню на примере.
Пусть на форме имеется компонент ContextStripMenu с командами "Вырезать" (toolStripMenuItem1), "Копировать" (toolStripMenuItem2), "Вставить" (toolStripMenuItem3). Необходимо блокировать или разблокировать эти команды в зависимости от состояния работы программы.
В событии Opening компонента ContextMenuStrip введите код:
//если изображения в PictureBox нет
if (pictureBox1.Image == null)
{
//блокируем команды "Вырезать" и "Копировать"
toolStripMenuItem1.Enabled =
toolStripMenuItem2.Enabled = false;
}
//в противном случае разблокируем эти команды
else
{
toolStripMenuItem1.Enabled =
toolStripMenuItem2.Enabled = true;
}
//если в буфере обмена не картинка
if (Clipboard.GetImage()==null)
//блокируем команду "Вставить"
toolStripMenuItem3.Enabled = false;
//иначе разблокируем команду "Вставить"
else
toolStripMenuItem3.Enabled = true;
Рассмотренный пример можно записать короче.
//блокируем разблокирует команды "Вырезать" и "Копировать"
toolStripMenuItem1.Enabled =
toolStripMenuItem2.Enabled = (pictureBox1.Image != null);
//блокируем разблокирует команду "Вставить"
toolStripMenuItem3.Enabled = (Clipboard.GetImage()!=null)
Компонент ImageList уже рассматривался ранее как хранилище изображений для панелей инструментов, меню и других компонентов.
Основные свойства компонента:
|
Свойство |
Описание |
|
Name |
Имя компонента для обращения к нему в коде |
|
Images |
Коллекция изображений в компоненте. якаждое изображение имеет порядковый номер. Нумерация с 0 |
|
ImageSize |
Размер изображений в коллекции. При добавлении в коллекцию все изображения будут преобразованы к заданному размеру |
|
TransparentColor |
Цвет, который будет прозрачным на изображениях в коллекции. По умолчанию - белый. |
Компонент ImageList может не только отображать свое содержимое на кнопках, вкладках и в меню. В нем можно прочитать любой рисунок из коллекции и отобразить его на форме в компоненте PictureBox или присвоить экземпляру класса Image для дальнейшей обработки.
Для этого к нужному рисунку в коллекции обращаются как к элементу массива:
imageList1.Images[i] - нумерация с 0
Например:
//отобразим второй рисунок из коллекции ImageList в компоненте на форме
pictureBox1.Image=imageList1.Images[1];
//прочитаем четвертое изображение из коллекции в экземпляр класса Image
//описываем переменную и считываем в нее изображение
Image pict = imageList1.Images[3];
Timer - это специальный компонент, позволяющий выполнить определенное действие через заданный промежуток времени без участия пользователя: скрыть окно формы-заставки, организовать анимационное движение фигур и т.п. Компонент является невидимым и может располагаться в любом месте формы.
Компонент имеет следующие свойства:
|
Свойство |
Описание |
|
Name |
Имя компонента для обращения к нему в коде |
|
Enabled |
Включает/выключает таймер. По умолчанию выключен |
|
Interval |
Промежуток времени, через который срабатывает таймер. Задается в мс. 1000мс = 1 с. |
Пусть имеется компонент ImageList с 5 картинками. Написать код, отображающий эти картинки в компоненте PictureBox поочередно через определенный интервал времени.
Опишите глобальную переменную - счетчик картинок:
int k=0;
В событии таймера Tick напишите код:
//отображаем изображение с номером k
pictureBox1.Image=imageList1.Images[k];
//если k
не последнее (не 4)
if (k<4)
//переходим к
следующему изображению
k++;
// если к последнее
else
// переходим к
первому изображению
k=0;
4. Элементы анимации
Анимация предусматривает эффект движения компонентов по форме. Для создание эффекта анимации у каждого компонента используют свойства:
- Left - задает отступ компонента от левого края формы в пикселях. При увеличении свойства компонент движется вправо, при уменьшении - влево;
- Top - задает отступ компонента от верхнего края формы в пикселях. При увеличении свойства компонент движется вниз, при увеличении - вверх.
Движение можно выполнять автоматически с помощью таймера или при нажатии на клавиши (например, стрелок управления курсором).
При движении компонентов по форме иногда нужно определить факт достижения компонентом края формы. В этом случае у формы используются свойства, которые сохраняют реальный размер формы без ее границ и строки заголовка. Эти свойства имеют имена ClientSize.Width (ширина) и ClientSize.Height (высота).
Пусть имеется форма вида:

На форме есть компонент TextBox (MultiLine = true). С помощью таймера организовать движение компонента по форме с эффектом скролинга в зависимости от выбранного направления с помощью переключателей RadioButton
Для таймера напишем код:
private void timer1_Tick(object sender, EventArgs e)
{
//если выбран первый переключатель
if (radioButton1.Checked)
if (textBox1.Left <= ClientSize.Width-textBox1.Width)
textBox1.Left++;
else
textBox1.Left=-textBox1.Width;
//если выбран второй переключатель
else if (radioButton2.Checked)
if (textBox1.Left>= -textBox1.Width)
textBox1.Left--;
else
textBox1.Left = ClientSize.Width;
//если выбран третий переключатель
else if (radioButton3.Checked)
if (textBox1.Top <= ClientSize.Height-textBox1.Height)
textBox1.Top++;
else
textBox1.Top = -textBox1.Height;
//если выбран четвертый переключатель
else if (radioButton4.Checked)
if (textBox1.Top>=-textBox1.Height)
textBox1.Top--;
else
textBox1.Top=ClientSize.Height;
}
Перепишем пример, создав анимацию с эффектом отскока от краев формы. Пусть имеется форма вида:

Для организации отскока на форме нужно разместить два компонента Timer. Timer1 будет перемещать фигуру по горизонтали, а Timer2 - по вертикали.
На форме по умолчанию выбран первый переключатель, поэтому для компонента Timer2 установим свойство Enabled = False.
Опишем две глобальные переменные для задания направления движения по горизонтали (h) и вертикали (v). Если переменные равны -1, то двигаемся влево (h=-1) или вверх (v=-1). Если переменные равны 1, то двигаемся вправо (h=1) или вниз (v=1).
int h=-1, v=-1;
Для таймера Timer1 напишем код:
private void timer1_Tick(object sender, EventArgs e)
{
//если фигура коснулась левого края формы
if (textBox1.Left <= 0)
//меняем направление движения
h = 1;
//если фигура коснулась правого края формы
else if (textBox1.Left>= ClientSize.Width-textBox1.Width)
//меняем направление движения
h = - 1;
//смещаем фигуру по форме
textBox1.Left = textBox1.Left + h;
}
Для компонента Timer2 напишем код:
private void timer2_Tick(object sender, EventArgs e)
{
// если фигура коснулась верхнего края формы
if (textBox1.Top <= 0)
// меняем направление движения
v = 1;
// если фигура коснулась нижнего края формы
else if (textBox1.Top>= ClientSize.Height-textBox1.Height)
// меняем направление движения
v = - 1;
// смещаем фигуру по форме
textBox1.Top=textBox1.Top+v;
}
При выборе направления движения мы будет активировать нужный таймер для движения фигуры.
Для первого переключателя напишем код:
private void radioButton1_CheckedChanged(object sender, EventArgs e)
{
timer1.Enabled = true;
timer2.Enabled = false;
}
Для второго переключателя напишем код:
private void radioButton2_CheckedChanged(object sender, EventArgs e)
{
timer1.Enabled = false;
timer2.Enabled = true;
}
Для третьего переключателя напишем код:
private void radioButton3_CheckedChanged(object sender, EventArgs e)
{
timer1.Enabled = true;
timer2.Enabled = true;
}
Вопросы для самоконтроля
1. Опишите компонент, предназначенный для отображения графической информации на форме.
2. Опишите принципы работы с классами Color и SystemColors.
3. Какие диалоги для работы с графическими файлами вы знаете? Приведите примеры их использования.
4. Опишите принципы работы с графикой через буфер обмена. Примеры.
5. Опишите компонент ImageList. Как выполнить чтение изображения из этого компонента? Приведите пример.
6. Опишите назначение таймера и принцип работы с ним.