- •1. Вводная часть
- •2. Знакомство со средой разработки на примере создания простого консольного приложения
- •3. Модификация консольного приложения
- •4. Публикация
- •5. О приложении к Лабораторной работе № 1
- •Лабораторная работа № 2: Создание простого приложения Windows Forms
- •Создание простого приложения Windows Forms
- •Модификация приложения Windows Forms
- •Завершающая часть
- •2. Создание простого приложения Windows Forms
- •3. Модификация приложения Windows Forms
- •4. Завершающая часть
- •5. О приложении к Лабораторной работе № 2
- •3. Лабораторная работа № 3: Windows Forms и использование некоторых элементов управления Лабораторная работа № 3: Windows Forms и использование некоторых элементов управления
- •Вводная часть
- •О приложении к Лабораторной работе № 3
- •1. Вводная часть
- •2. Создание приложения Windows Forms
- •3. Модификация приложения Windows Forms
- •4. Модификация приложения Windows Forms: элемент управления MenuStrip
- •5. Модификация приложения Windows Forms: элемент управления Button
- •6. Модификация приложения Windows Forms: элемент управления ToolTip
- •7. Модификация приложения Windows Forms: элемент управления ComboBox
- •8. Модификация приложения Windows Forms: элемент управления WebBrowser
- •9. Модификация приложения Windows Forms: добавляем исходный код
- •10. Завершающая часть
- •11. О приложение к Лабораторной работе № 3
- •4. Лабораторная работа № 4: Windows Forms — работа с формами Лабораторная работа № 4: Windows Forms — работа с формами
- •Вводная часть
- •О приложении к Лабораторной работе № 4
- •1. Вводная часть
- •2. Создание приложения Windows Forms
- •3. Модификация приложения Windows Forms
- •4. Модификация приложения Windows Forms: добавление новой формы
- •5. Модификация приложения Windows Forms: динамическое связывание параметров двух форм и передача параметра через прямой доступ к элементу формы
- •6. Модификация приложения Windows Forms: динамическое связывание параметров двух форм и передача параметра через передачу метода в конструктор формы
- •7. Модификация приложения Windows Forms: динамическое связывание параметров двух форм и передача параметра через класс делегата
- •8. Модификация приложения Windows Forms: динамическое связывание параметров двух форм и передача параметра через свойства
- •9. Завершающая часть
- •10. О приложении к Лабораторной работе № 4
- •5. Лабораторная работа № 5: Windows Forms — элементы управления Лабораторная работа № 5: Windows Forms — элементы управления
- •Вводная часть
- •О приложении к Лабораторной работе № 5
- •1. Вводная часть
- •2. Создание приложения Windows Forms
- •3. Модификация приложения Windows Forms
- •4. Модификация приложения Windows Forms: динамическое добавление и уничтожение элемента управления
- •5. Модификация приложения Windows Forms: стандартные диалоговые окна
- •6. Модификация приложения Windows Forms: открытие файла, сохранение файла и работа с текстом
- •7. Модификация приложения Windows Forms: прочее
- •8. Завершающая часть
- •9. О приложении к Лабораторной работе № 5
- •Лабораторная работа № 6: SolidWorks — работа с SolidWorks
- •2. Создание приложения Windows Forms
- •3. Модификация приложения Windows Forms: eDrawings 2012
- •4. Модификация приложения Windows Forms: организация работы с SolidWorks через приложение
- •5. Завершающая часть
- •6. О приложении к Лабораторной работе № 6
- •Лабораторная работа № 7: SolidWorks — использование SwCSharpAddin, работа с макросом и шаблоном детали
- •2. Создание приложения SwCSharpAddin
- •3. Модификация приложения SwCSharpAddin: макрос отрисовки болта
- •4. Модификация приложения SwCSharpAddin: построение через шаблон детали
- •5. Завершающая часть
- •6. О приложении к Лабораторной работе № 7
- •8. Лабораторная работа № 8: Создание простого приложения Windows Foundation Presentation Лабораторная работа № 8: Создание простого приложения Windows Foundation Presentation
- •Вводная часть
- •О приложении к Лабораторной работе № 8
- •1. Вводная часть
- •2. Создание приложения Windows Foundation Presentation
- •3. Модификация приложения Windows Foundation Presentation
- •4. Модификация приложения Windows Foundation Presentation: добавление нового элемента из библиотеки компонентов wpf
- •5. Модификация приложения Windows Forms: расширение функциональности приложения и работа с оформление
- •6. Модификация приложения Windows Foundation Presentation: различные возможности wpf
- •7. Модификация приложения Windows Foundation Presentation: немного о стилях и шаблонах
- •8. Завершающая часть
- •9. О приложении к Лабораторной работе № 8
- •Лабораторная работа № 9: Создание приложения-презентации Windows Foundation Presentation
- •2. Создание приложения Windows Foundation Presentation
- •3. Модификация приложения Windows Foundation Presentation
- •4. Модификация приложения Windows Foundation Presentation: работа с решением в Expression Blend
- •5. Завершающая часть
- •6. О приложении к Лабораторной работе № 9
- •Лабораторная работа № 10: Работа с базами данных — xml и ole
- •2. Создание приложения Windows Forms
- •3. Модификация приложения Windows Forms: odbc
- •4. Модификация приложения Windows Forms: ole
- •5. Модификация приложения Windows Forms: xml
- •6. Завершающая часть
- •7. О приложении к Лабораторной работе № 10
- •Лабораторная работа № 11: Динамическая связь приложений через библиотеку классов
- •2. Удалённый объект: создание библиотеки классов удалённого объекта
- •3. Клиент: создание приложения Windows Forms
- •4. Сервер: создание консольного приложения
- •5. Завершающая часть
- •6. О приложении к Лабораторной работе № 11
- •Лабораторная работа № 12: Автоматизация Microsoft Office Word
- •2. Создание приложения Windows Forms
- •3. Модификация приложения Windows Forms: создание и заполнение нового документа Microsoft Word
- •4. Модификация приложения Windows Forms: работа с шаблонами
- •5. Завершающая часть
- •6. О приложении к Лабораторной работе № 12
- •Лабораторная работа № 13: Автоматизация Microsoft Office Excel
- •2. Создание приложения Windows Forms
- •3. Модификация приложения Windows Forms: создание и заполнение нового документа Microsoft Excel
- •4. Модификация приложения Windows Forms: чтение данных с документа Microsoft Excel
- •5. Модификация приложения Windows Forms: прочие возможности Microsoft Excel
- •6. Завершающая часть
- •7. О приложении к Лабораторной работе № 13
- •Лабораторная работа № 14: Простой растровый редактор
- •2. Создание приложения Windows Forms
- •3. Модификация приложения Windows Forms: подготовка интерфейса редактора
- •4. Модификация приложения Windows Forms: функциональность растрового редактора
- •5. Завершающая часть
- •6. О приложении к Лабораторной работе № 14
- •15. Лабораторная работа № 15: Векторный редактор изображений Лабораторная работа № 15: Векторный редактор изображений
- •Вводная часть
- •О приложении к Лабораторной работе № 15
- •1. Вводная часть
- •2. Создание решения, приложения Windows Forms и библиотеки классов
- •3. Модификация приложения Windows Forms: подготовка интерфейса редактора и добавление файлов ресурсов
- •4. О будущей функциональности векторного редактора изображений
- •5. Модификация приложения Windows Forms: подготовка библиотеки классов
- •6. Модификация приложения Windows Forms: функциональность векторного редактора
- •7. Завершающая часть
- •8. О приложении к Лабораторной работе № 15
- •Лабораторная работа № 16: Windows Communication Foundation
- •2. Создание приложения wcf:
- •3. Модификация приложения wcf: приложение-клиент для wcf-службы
- •4. Создание приложения Windows Forms: сервер чата на wcf
- •5. Создание приложения Windows Forms: клиент чата на wcf
- •6. Завершающая часть
- •7. О приложении к Лабораторной работе № 16
- •Лабораторная работа № 17: Знакомство с Silverlight
- •2. Создание приложения Silverlight:
- •3. Модификация приложения Silverlight: первые шаги и полноэкранный режим
- •4. Модификация приложения Silverlight: простой проигрыватель mp3-файлов
- •5. Модификация приложения Silverlight: работа с анимацией
- •6. Завершающая часть
- •7. О приложении к Лабораторной работе № 17
- •Лабораторная работа № 18: Знакомство с asp.Net
- •2. Создание веб-приложения asp.Net
- •3. Модификация веб-приложения asp.Net: реализация различной функциональности
- •4. Модификация веб-приложения asp.Net: ajax
- •5. Завершающая часть
- •6. О приложении к Лабораторной работе № 18
- •Вводная часть
- •О приложении к Лабораторной работе № 19
- •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
- •Лабораторная работа № 20: Inventor — работа с Inventor
- •Вводная часть
- •О приложении к Лабораторной работе № 20
- •1. Вводная часть
- •2. Создание приложения Windows Forms
- •3. Модификация приложения Windows Forms: запуск и завершение приложения Inventor
- •4. Модификация приложения Windows Forms: создание новых документов
- •5. Модификация приложения Windows Forms: доступ к элементам документов Inventor
- •6. Завершающая часть
- •7. О приложении к Лабораторной работе № 20
- •Лабораторная работа № 21: Знакомство с языком f# (Эф-шарп) на примере простого приложения для командной строки
- •Вводная часть
- •О приложении к Лабораторной работе № 21
- •1. Вводная часть
- •2. Основные положения при работе с f#
- •3. Создание приложения «Учебник по f#»
- •4. Создание консольного приложения
- •5. О приложении к Лабораторной работе № 21
- •Лабораторная работа № 22: Различные примеры на f#
- •2. Создание приложения «Приложение f#»
- •3. Модификация приложения f#: match
- •4. Модификация приложения f#: создание форм и рисование объектов на форме
- •5. Модификация приложения f#: работа с базой данных Microsoft Access
- •6. О приложении к Лабораторной работе № 22
- •23. Дополнительная лабораторная работа № 1
- •24. Дополнительная лабораторная работа № 2
- •25. Дополнительная лабораторная работа № 3
- •Вводная часть
- •1. Вводная часть
- •2. Создание приложения Windows Foundation Presentation
- •3. Модификация приложения Windows Foundation Presentation: добавление ресурсов
- •4. Модификация приложения Windows Foundation Presentation: добавление исходного кода
- •5. Модификация приложения Windows Foundation Presentation: оформление элемента ListBox
- •6. Завершающая часть
- •7. О приложении к данной работе
4. Модификация приложения Windows Forms: функциональность растрового редактора
Есть два основных способа работы с изображением и последующим его сохранением. Работа с растровым изображением и работа с векторным изображением. Не путать с типами изображений.
Начнём с того, что любое растровое изображение является простым набором сведений о каждом пикселе.
Растровое изображение — представляющее собой сетку пикселей или цветных точек (обычно прямоугольную) на компьютерном мониторе, бумаге и других отображающих устройствах и материалах (растр).
Важными характеристиками изображения являются:
1. Количество пикселей — количество цветов. Может указываться отдельно количество пикселей по ширине и высоте (1024×768, 640×480, …) или же, редко, общее количество пикселей (часто измеряется в мегапикселях).
2. Количество используемых цветов или глубина цвета (эти характеристики имеют следующую зависимость: N = 2k, где N — количество цветов, а k — глубина цвета);
3. Цветовое пространство (цветовая модель) RGB, CMYK, XYZ, YCbCr и др.
4. Разрешение — справочная величина, говорящая о рекомендуемом размере пикселя изображения.
Растровую графику редактируют с помощью растровых графических редакторов. Создаётся растровая графика фотоаппаратами, сканерами, непосредственно в растровом редакторе, также путём экспорта из векторного редактора или в виде снимка экрана.
Типичный растровый редактор: Paint. Фактически редактор оперирует только точками. Нарисованную линию нельзя передвинуть или растянуть. А всё рисование состоит только из заполнения массива координат и пикселей. Именно так и будет работать наше приложение. Второй же способ, это на этапе рисования (до сохранения в обычные графические форматы типа *.bmp) представлять каждый объект рисунка как самостоятельный объект. Для линии это координаты положения, толщина, цвет и что самое главное — аналоги имени. Таким образом, такую линию можно переместить, растянуть, удалить как объект (ближайший аналог: символ в Word’е). Для приложения такой рисунок предстаёт как векторное изображение, которое можно экспортировать в растровое (данные о примитивах удаляются, остаётся лишь слитое воедино изображение).
Теперь о нашем приложении:
Первая кнопка загружает сторонний рисунок с жёсткого диска через диалог открытия файла. После загрузки рисунок отображается в качестве растрового изображения в PictureBox и этот рисунок становится доступен для редактирования.
Событие Click для элемента меню (Выбрать рисунок) можно создать просто дважды кликнув на элементе меню. Также можно выбрать этот элемент меню, далее на панели Свойства справа перейти на вкладку События и далее ввести имя метода для события Click, либо просто дважды клинуть по Click (Действия):
Рис. 3. 5. Свойства: Событие Click элемента меню Выбрать рисунок
Код события Click для кнопки Выбрать рисунок:
private void выбратьРисунокToolStripMenuItem_Click(object sender, EventArgs e)
{
// Если рисунок был выбран
if (OFD_Picture.ShowDialog() == DialogResult.OK)
{
// Получаем изображения по указанному в диалоге OpenFileDialog пути (OFD_Picture.FileName)
image = Image.FromFile(OFD_Picture.FileName);
int width = image.Width; // Получаем ширину изображения
int height = image.Height; // Получаем высоту изображения
// Устанавливаем размеры изображения в PictureBox
PB_Bitmap.Width = width;
PB_Bitmap.Height = height;
// Создаём Bitmap на основе изображения
bitmap = new Bitmap(Image.FromFile(OFD_Picture.FileName), width, height);
PB_Bitmap.Image = bitmap; // Отправляем Bitmap в PictureBox
StatusLabel.Text = "Изображение " + Path.GetFileName(OFD_Picture.FileName) + " успешно загружено! Полный путь: " + OFD_Picture.FileName;
}
}
Кнопка Сохранить как... сохраняет всё изображение в рабочей области в выбранном формате (BMP-, JPEG-, GIF, TIF- или PGN-файл) и выводит результат в качестве текста в строке состояния.
Событие Click кнопки Сохранить как...:
private void сохранитьКакToolStripMenuItem_Click(object sender, EventArgs e)
{
// Если выбрали, сохраняем
if (SFD_Picture.ShowDialog() == DialogResult.OK)
{
// Получаем имя файла из диалога
string fileName = SFD_Picture.FileName;
// Получаем расширения файла из диалога
string strFilExtn = fileName.Remove(0, fileName.Length - 3);
// Сохраняем файл в выбранном расширении
switch (strFilExtn)
{
case "bmp":
bitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Bmp);
break;
case "jpg":
bitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Jpeg);
break;
case "gif":
bitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Gif);
break;
case "tif":
bitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Tiff);
break;
case "png":
bitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Png);
break;
default:
break;
}
StatusLabel.Text = "Изображение " + Path.GetFileName(SFD_Picture.FileName) + " успешно сохранено! Полный путь: " + SFD_Picture.FileName;
}
}
Событие Click кнопки Выход:
private void выходToolStripMenuItem_Click(object sender, EventArgs e)
{
Close();
}
Кнопка Очистить, полностью очищает рабочую область и Bitmap в которой сохраняются изменения.
Событие Click кнопки Очистить:
private void очиститьToolStripMenuItem_Click(object sender, EventArgs e)
{
// Очищаем рабочее поле
// Создаём пустой Bitmap на основе размеров PictureBox
bitmap = new Bitmap(PB_Bitmap.Size.Width, PB_Bitmap.Size.Height);
// Инициализируем фон для поля рисования
graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White); // Задаём белый цвет фона, иначе фон будет прозрачным
//graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; // Выключаем сглаживание
PB_Bitmap.Image = bitmap; // Отправляем Bitmap в PictureBox
StatusLabel.Text = "Рабочее поле очищено";
}
Кнопка Линия активирует режим в котором в PictureBox можно рисовать линии. Код события содержит лишь действия по управлению элементами меню и выводом текстового сообщения в строку состояния. При нажатии, снимается выделение (галочка слева от надписи) с кнопок меню «Рисование»: «Прямоугольник» или «Окружность», если до этого они были активированы. Событие Click:
private void линияToolStripMenuItem_Click(object sender, EventArgs e)
{
if (!DrawLine)
{
DrawLine = true;
линияToolStripMenuItem.Checked = true;
DrawRect = false;
прямоугольникToolStripMenuItem.Checked = false;
DrawCirc = false;
окружностьToolStripMenuItem.Checked = false;
StatusLabel.Text = "Включен режим рисования линии";
}
}
Событие Click кнопки Прямоугольник:
private void прямоугольникToolStripMenuItem_Click(object sender, EventArgs e)
{
if (!DrawRect)
{
DrawLine = false;
линияToolStripMenuItem.Checked = false;
DrawRect = true;
прямоугольникToolStripMenuItem.Checked = true;
DrawCirc = false;
окружностьToolStripMenuItem.Checked = false;
StatusLabel.Text = "Включен режим рисования прямоугольника";
}
}
Событие Click кнопки Окружность:
private void окружностьToolStripMenuItem_Click(object sender, EventArgs e)
{
if (!DrawCirc)
{
DrawLine = false;
линияToolStripMenuItem.Checked = false;
DrawRect = false;
прямоугольникToolStripMenuItem.Checked = false;
DrawCirc = true;
окружностьToolStripMenuItem.Checked = true;
StatusLabel.Text = "Включен режим рисования окружности";
}
}
Кнопка «Непрерывная» включает режим рисования линий при котором предыдущая линия становится началом следующей. Получается ломаная линия. Событие Click этой кнопки выглядит так:
private void непрерывнаяToolStripMenuItem_Click(object sender, EventArgs e)
{
if (!CurveLine)
{
CurveLine = true;
непрерывнаяToolStripMenuItem.Checked = true;
StatusLabel.Text = "Включен режим непрерывной линии";
}
else
{
CurveLine = false;
непрерывнаяToolStripMenuItem.Checked = false;
StatusLabel.Text = "Выключен режим непрерывной линии";
}
}
Кнопка Цвет пера вызывает стандартный диалог Windows по выбору цвета и задаёт цвет главного пера, которым рисуется линия или контуры фигур. Если цвет не выбран, значение устанавливается по умолчанию как чёрный. Событие Click кнопки:
private void цветПераToolStripMenuItem_Click(object sender, EventArgs e)
{
if (CD_Pen.ShowDialog() == DialogResult.OK)
{
Color_Pen = CD_Pen.Color;
Main_Pen = new Pen(Color_Pen, Convert.ToInt16(X));
Main_Pen.EndCap = System.Drawing.Drawing2D.LineCap.NoAnchor;
}
else
{
Main_Pen = new Pen(Color.Black, Convert.ToInt16(X));
Main_Pen.EndCap = System.Drawing.Drawing2D.LineCap.NoAnchor;
}
}
Кнопка Цвет заливки устанавливает цвет заполнения SolidBrush-объекта для прямоугольника или окружности. Если цвет не выбран, значение сбрасывается на цвет с именем Color.Violet, который выбран в качестве цвета для сбора заливки (заливка не осуществляется и фигура не заполняется цветом.
private void цветЗаливкиToolStripMenuItem_Click(object sender, EventArgs e)
{
if (CD_Fill.ShowDialog() == DialogResult.OK)
{
Color_Fill = CD_Fill.Color;
Fill = new SolidBrush(Color_Fill);
}
else
{
Color_Fill = Color.Violet; // Цвет сбора заливки
Fill = new SolidBrush(Color_Fill);
}
}
Для элемента TextBox (толщинаПераToolStripMenuItem) определены четыре события. Основное событие TextChanged вызывается всякий раз когда будет меняться текст внутри поля. Само поле предназначено для ввода числа отвечающего за толщину пера (положительное значение от 0 и до примерно 1000 пикселей0; после 1000 значение не нельзя будет заменить разницы):
private void толщинаПераToolStripMenuItem_TextChanged(object sender, EventArgs e)
{
try
{
X = Convert.ToDouble(толщинаПераToolStripMenuItem.Text);
Main_Pen = new Pen(Color_Pen, Convert.ToInt16(X));
}
catch { }
}
Событие KeyDown выполняющее операцию сохранения числа в текстовом поле (аналогично предыдущему событию), но после нажатия клавиши Enter:
private void толщинаПераToolStripMenuItem_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter) // Если нажат Enter
{
X = Convert.ToDouble(толщинаПераToolStripMenuItem.Text);
Main_Pen = new Pen(Color_Pen, Convert.ToInt16(X));
}
}
События MouseEnter и MouseLeave реализуют визуальное «оформление» элемента. Наводим курсор и получаем значение 1. «1» остаётся, если ничего не вводить в поле или оставить поле пустым:
private void толщинаПераtoolStripMenuItem_MouseEnter(object sender, EventArgs e)
{
if (толщинаПераToolStripMenuItem.Text == "Толщина пера")
{
толщинаПераToolStripMenuItem.Text = "1";
}
}
private void толщинаПераtoolStripMenuItem_MouseLeave(object sender, EventArgs e)
{
if (толщинаПераToolStripMenuItem.Text == "")
{
толщинаПераToolStripMenuItem.Text = "1";
}
}
События кнопок ToolStrip-меню похожи и выполняют роль переопределения вызовов некоторых событий нажатия кнопок верхнего меню. Все события Click по порядку следования кнопок на рисунке 3. 4.:
private void выбратьРисунокToolStripButton_Click(object sender, EventArgs e)
{
выбратьРисунокToolStripMenuItem.PerformClick(); // Вызываем нажатие аналогичной кнопки в MenuStrip
}
private void сохранитьКакToolStripButton_Click(object sender, EventArgs e)
{
сохранитьКакToolStripMenuItem.PerformClick();
}
private void выходToolStripButton_Click(object sender, EventArgs e)
{
выходToolStripMenuItem.PerformClick();
}
private void очиститьToolStripButton_Click(object sender, EventArgs e)
{
очиститьToolStripMenuItem.PerformClick();
}
private void линияToolStripButton_Click(object sender, EventArgs e)
{
линияToolStripMenuItem.PerformClick();
линияToolStripButton.Checked = true;
прямоугольникToolStripButton.Checked = false;
окружностьToolStripButton.Checked = false;
}
private void прямоугольникToolStripButton_Click(object sender, EventArgs e)
{
прямоугольникToolStripMenuItem.PerformClick();
линияToolStripButton.Checked = false;
прямоугольникToolStripButton.Checked = true;
окружностьToolStripButton.Checked = false;
}
private void окружностьToolStripButton_Click(object sender, EventArgs e)
{
окружностьToolStripMenuItem.PerformClick();
линияToolStripButton.Checked = false;
прямоугольникToolStripButton.Checked = false;
окружностьToolStripButton.Checked = true;
}
private void цветПераToolStripButton_Click(object sender, EventArgs e)
{
цветПераToolStripMenuItem.PerformClick();
}
private void цветЗаливкиToolStripButton_Click(object sender, EventArgs e)
{
цветЗаливкиToolStripMenuItem.PerformClick();
}
Теперь перейдём к редактированию непосредственно кода и основным события формы. Для начала откроем код файла LWP14Main.cs (правая кнопка мыши на значке формы, далее Перейти к коду или нажмём на клавишу F7). В самом начале файла добавим ссылку:
using System.IO; // Для работы класса Path
Найдём:
public partial class LWP14Main : Form
{
Добавим после:
// Диалоги
OpenFileDialog OFD_Picture;
SaveFileDialog SFD_Picture;
ColorDialog CD_Pen;
ColorDialog CD_Fill;
// Рабочее поле
Image image;
Graphics graphics;
Bitmap bitmap;
// Инструменты рисования
Pen Main_Pen;
Color Color_Pen;
Color Color_Fill;
// Объекты рисования
Point FirstPoint = new Point();
Point ToPoint = new Point();
Point LastPoint = new Point();
Rectangle SelectRect = new Rectangle();
Rectangle CircleRect = new Rectangle();
Rectangle LightRect = new Rectangle();
SolidBrush Fill;
// Вспомогательные переменные
Boolean DrawLine;
Boolean CurveLine;
Boolean DrawRect;
Boolean DrawCirc;
Double X;
Код метода LWP14Main() изменим следующим образом:
public LWP14Main()
{
InitializeComponent();
// Инициализируем диалоги
OFD_Picture = new OpenFileDialog();
OFD_Picture.Filter = "Файлы изображений (*.bmp, *.jpg, *.gif, *.tif, *.png, *.ico, *.emf, *.wmf)|*.bmp;*.jpg;*.gif; *.tif; *.png; *.ico; *.emf; *.wmf";
SFD_Picture = new SaveFileDialog();
SFD_Picture.Title = "Сохранить как";
SFD_Picture.OverwritePrompt = true;
SFD_Picture.CheckPathExists = true;
SFD_Picture.Filter = "Изображение в формате PNG|*.png|" + "Изображение в формате JPEG|*.jpg|" + "Изображение в формате BMP|*.bmp|" + "Изображение в формате GIF|*.gif|" + "Изображение в формате TIF|*.tif";
CD_Pen = new ColorDialog();
CD_Fill = new ColorDialog();
// Инициализируем рабочее поле
// Создаём пустой Bitmap на основе размеров PictureBox
bitmap = new Bitmap(PB_Bitmap.Size.Width, PB_Bitmap.Size.Height);
// Инициализируем фон для поля рисования
graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White); // Задаём белый цвет фона, иначе фон будет прозрачным
//graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; // Выключаем сглаживание
PB_Bitmap.Image = bitmap; // Отправляем Bitmap в PictureBox
// Инициализируем инструменты рисования по умолчанию
Main_Pen = new Pen(Color.Black, 1);
Main_Pen.EndCap = System.Drawing.Drawing2D.LineCap.NoAnchor; // Не задаём маркер на конце пера
//graphics.DrawLine(Main_Pen, 10, 10, 11, 10); // Рисует точку в один пиксель, при отсутствии маркера на Pen и толщине в 1
Color_Pen = Color.Black;
Color_Fill = Color.Violet;
Fill = new SolidBrush(Color_Fill);
// Инициализируем объекты рисования...
// ...нету их :)
// Инициализируем вспомогательные переменные по умолчанию
DrawLine = true;
линияToolStripMenuItem.Checked = true;
линияToolStripButton.Checked = true;
CurveLine = false;
DrawRect = false;
прямоугольникToolStripButton.Checked = false;
DrawCirc = false;
окружностьToolStripButton.Checked = false;
X = 1;
// Инициализируем прочее
StatusLabel.Text = "Приложение готово к работе";
}
Код выше помимо подготовки диалогов сохранения и открытия изображений, также инициализируем поле для рисования во всём PictureBox. Заполняем при старте PictureBox объектом Bitmap, очищает и задаёт белый фон. Важно понимать, что сохранение происходит не из самого PictureBox, а из объекта растрового изображения, загруженного в качестве текущего PictureBox.Image. Без задания фона пустого Bitmap’а, сохранённое изображение будет иметь чёрный или прозрачный фон, в зависимости от типа сохраняемого изображения (поддержки альфа-канала).
Рисование любого объекта в приложении проходит в три этапа. Первый этап это получение координат начала (начальной точки), затем промежуточный второй этап (движение мыши по рабочей зоне) и третий этап получения координат окончания (конечной точки) и сам процесс отрисовки объекта.
Первый этап реализован в методе PictureBox: MouseDown. Нажатие ЛКМ в любом месте рабочей зоны приводит к сохранению координат (так устроено само событие и его аргументы). В зависимости от выбранного режима эти координаты используются по разному. Для отрисовки линии координаты нажатия отправляются в значения X и Y для объекта Point,а для фигур формируется начальная точка объекта Rectangle. Событие MouseDown для элемента PictureBox (PB_Bitmap) выглядит так:
private void PB_Bitmap_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right) // Если нажата правая кнопка мыши
{
LightRect.Width = 0;
LightRect.Height = 0;
LightRect.X = e.X;
LightRect.Y = e.Y;
}
if (e.Button == MouseButtons.Left) // Если нажата левая кнопка мыши
{
// Если рисуем линию
if (DrawLine)
{
// Получаем координаты нажатия левой кнопки мыши в PictureBox
FirstPoint.X = e.X;
FirstPoint.Y = e.Y;
ToPoint = FirstPoint;
if (CurveLine)
{
FirstPoint = LastPoint;
}
}
// Если рисуем прямоугольник
if (DrawRect)
{
SelectRect.Width = 0;
SelectRect.Height = 0;
SelectRect.X = e.X;
SelectRect.Y = e.Y;
}
// Если рисуем окружность
if (DrawCirc)
{
CircleRect.Height = 0;
CircleRect.Width = 0;
CircleRect.X = e.X;
CircleRect.Y = e.Y;
}
}
}
Промежуточный этап для наглядности «усовершенствован». Если зажать ЛКМ во время рисования линии, то за курсором от начальной точки будет тянуться чёрная тонкая линия. В случае если будет включен режим рисования прямоугольника или окружности, то вместо линии будет пунктирный прямоугольник. Эти действия реализованы в событии MouseMove для PictureBox:
private void PB_Bitmap_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
LightRect.Width = e.X - LightRect.X;
LightRect.Height = e.Y - LightRect.Y;
}
if (e.Button == MouseButtons.Left)
{
// Если рисуем линию, отображаем заготовку линии
if (DrawLine)
{
// Отображаем заготовку линии до тех пор пока не отпустим левую кнопку мыши
ControlPaint.DrawReversibleLine(PB_Bitmap.PointToScreen(FirstPoint), PB_Bitmap.PointToScreen(ToPoint), Color.Black);
ToPoint = new Point(e.X, e.Y);
ControlPaint.DrawReversibleLine(PB_Bitmap.PointToScreen(FirstPoint), PB_Bitmap.PointToScreen(ToPoint), Color.Black);
if (CurveLine) // Убираем возможные "артефакты", возникающие при отрисовки непрерывной линии
PB_Bitmap.Refresh();
}
// Если рисуем прямоугольник, отображаем заготовку прямоугольника пунктирными линиями
if (DrawRect)
{
ControlPaint.DrawReversibleFrame(PB_Bitmap.RectangleToScreen(SelectRect), Color.Black, FrameStyle.Dashed);
SelectRect.Width = e.X - SelectRect.X; // Получаем значение ширины прямоугольника
SelectRect.Height = e.Y - SelectRect.Y; // Получаем значение высоты прямоугольника
ControlPaint.DrawReversibleFrame(PB_Bitmap.RectangleToScreen(SelectRect), Color.Black, FrameStyle.Dashed);
//PB_Bitmap.Refresh();
}
// Если рисуем окружность, отображаем заготовку окружности пунктирными линиями
if (DrawCirc)
{
ControlPaint.DrawReversibleFrame(PB_Bitmap.RectangleToScreen(CircleRect), Color.Black, FrameStyle.Dashed);
CircleRect.Width = e.X - CircleRect.X;
CircleRect.Height = e.Y - CircleRect.Y;
ControlPaint.DrawReversibleFrame(PB_Bitmap.RectangleToScreen(CircleRect), Color.Black, FrameStyle.Dashed);
//PB_Bitmap.Refresh();
}
}
}
Третий этап собственно реализует рисование объекта. После «отжатия» левой кнопки мыши будут получены координаты конечной точки и отрисован объект. Это реализует событий MouseUp для PictureBox:
private void PB_Bitmap_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
graphics = Graphics.FromImage(PB_Bitmap.Image);
graphics.DrawRectangle(Main_Pen, LightRect); // Нарисуем прямоугольник-контур для осветлённой области
graphics.Dispose();
PB_Bitmap.Invalidate();
}
if (e.Button == MouseButtons.Left) // Если нажата левая кнопка мыши
{
// Рисуем в PictureBox (только в загруженном в элементе изображении)
graphics = Graphics.FromImage(PB_Bitmap.Image);
if (DrawLine)
{
LastPoint.X = e.X;
LastPoint.Y = e.Y;
graphics.DrawLine(Main_Pen, FirstPoint, LastPoint);
}
if (DrawRect)
{
if (Color_Fill != Color.Violet) { graphics.FillRectangle(Fill, SelectRect); } // Заполнение цветом прямоугольной области ограниченной SelectRect
graphics.DrawRectangle(Main_Pen, SelectRect);
}
if (DrawCirc)
{
if (Color_Fill != Color.Violet) { graphics.FillEllipse(Fill, CircleRect); } // Заполнение цветом эллептической области ограниченной CircleRect
graphics.DrawEllipse(Main_Pen, CircleRect);
}
graphics.Dispose();
PB_Bitmap.Invalidate(); // Обновляем PictureBox
}
}
Реализуем ещё одну интересную функцию, которую «повесим» на событие нажатия правой кнопки мыши в области PictureBox. Код события MouseClick:
private void PB_Bitmap_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
StatusLabel.Text = "Произведено осветление зоны";
int newRed, newGreen, newBlue;
Color pixel;
// Операция увеличения яркости может занять много времени, пользователя предупредят, если выбран большой участок
if ((LightRect.Width > 500) || (LightRect.Height > 500))
{
DialogResult result = MessageBox.Show("Выделенная область велика! " + "Изменение яркости может требовать значительного времени!", "Простой растровый редактор (C#) :: Изменения яркости", MessageBoxButtons.OKCancel);
// При нажатии кнопки "Отмена" выходим из метода
// и возвращаемся к месту его вызова
if (result == DialogResult.Cancel) return;
}
/* Перебираем последовательно все пиксели данного участка и удваиваем значение яркости компонент RGB пикселей */
// Перебор по горизонтали слева направо...
for (int x = LightRect.X; x < LightRect.X + LightRect.Width; x++)
{
// и по вертикали сверху вниз...
for (int y = LightRect.Y; y < (LightRect.Y + LightRect.Height); y++)
{
// Считываем текущий пиксель
pixel = bitmap.GetPixel(x, y);
// Увеличиваем яркость цветовых компонент пикселя
newRed = (int)Math.Round(pixel.R * 2.0, 0);
if (newRed > 255) newRed = 255;
newGreen = (int)Math.Round(pixel.G * 2.0, 0);
if (newGreen > 255) newGreen = 255;
newBlue = (int)Math.Round(pixel.B * 2.0, 0);
if (newBlue > 255) newBlue = 255;
// Присваиваем пикселю новые цветовые значения
bitmap.SetPixel(x, y, Color.FromArgb((byte)newRed, (byte)newGreen, (byte)newBlue));
}
}
}
}
Код выше «осветляет» прямоугольную область при зажатой правой кнопке мыши. Вокруг осветлённой области также рисуется прямоугольник толщина пера и цвет общий для остальных фигур.
Наконец, сделаем ластик. Работать он будет при зажатой клавиши Shift (+ нажатие ПКМ в области, которую нужно стереть). Сделаем ластик также ещё и карандашом. Его цвет будет завесить от цвета заливки, а толщина поля «стирания» от толщины пера.
Найдём:
Boolean DrawCirc;
Double X;
Добавим после:
// Ластик
Pen Pen_Erase;
Boolean DrawErase;
SolidBrush Erase;
Rectangle EraseRect = new Rectangle();
Найдём:
// Инициализируем прочее
StatusLabel.Text = "Приложение готово к работе";
Добавим после:
// Ластик
Pen_Erase = new Pen(Color.White, 1);
DrawErase = false;
Erase = new SolidBrush(Color.White);
Найдём:
private void PB_Bitmap_MouseMove(object sender, MouseEventArgs e)
{
Добавим после:
// Ластик
if (e.Button == MouseButtons.Right && DrawErase == true)
{
EraseRect.X = e.X - Convert.ToInt32(X);
EraseRect.Y = e.Y - Convert.ToInt32(X);
EraseRect.Width = Convert.ToInt32(X) * 2;
EraseRect.Height = Convert.ToInt32(X) * 2;
graphics = Graphics.FromImage(PB_Bitmap.Image);
graphics.FillEllipse(Erase, EraseRect);
graphics.Dispose();
PB_Bitmap.Invalidate();
StatusLabel.Text = "Режим ластика/карандаша";
}
Перепишем код события нажатия на кнопку «Цвет заливки»:
private void цветЗаливкиToolStripMenuItem_Click(object sender, EventArgs e)
{
if (CD_Fill.ShowDialog() == DialogResult.OK)
{
Color_Fill = CD_Fill.Color;
Fill = new SolidBrush(Color_Fill);
Erase = new SolidBrush(Color_Fill);
}
else
{
Color_Fill = Color.Violet; // Цвет сбора заливки
Fill = new SolidBrush(Color_Fill);
Erase = new SolidBrush(Color.White);
}
}
А также инициализируем два события главной формы (отлов нажатия и отжатия клавиш левый и правый Shift): KeyDown и KeyUp:
private void LWP14Main_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey) // Если нажат Shift
{
DrawErase = true;
}
}
private void LWP14Main_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey) // Если нажат Shift
{
DrawErase = false;
}
}
Готово. Можно компилировать и проверять работоспособность.