Лекция № 8

Тема: «Работа с графической информацией»

 

План

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)

 

2. Компонент ImageList

Компонент 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];

 

 

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. Опишите назначение таймера и принцип работы с ним.