RUS_-_Karejev_-_Razrabotka_Windows-prilozhenij
.pdf
openFileDialog1, Свойство |
Значение |
||
Title |
|
Сохранение изображения |
|
|
|
|
|
pictureBox1, Свойство |
Значение |
|
|
ContextMenu |
contextMenu1 |
|
|
Dock |
Fill |
|
|
SizeMode |
StretchImage |
|
|
Приложение будет отображать в элементе управления PictureBox загруженный рисунок. Добавляем обработчик пункта меню mnuOpen:
private void mnuOpen_Click(object sender, System.EventArgs e)
{
OpenFileDialog diag = new OpenFileDialog();
diag.Filter = "jpg files (*.jpg)|*.jpg|All files (*.*)|*.*" ; if (diag.ShowDialog() == DialogResult.OK)
{
pictureBox1.Image = new Bitmap(diag.OpenFile());
}
pictureBox1.Image= new Bitmap(diag.OpenFile());
}
Несмотря на определение формата jpeg в качестве основного, элемент PictureBox поддерживает также работу с форматами Bitmap
(.bmp), Icon (.ico), GIF (.gif), Metafile (.wmf). Сохранения файла практически не отличается от сохранения содержимого текстового поля:
private void mnuSave_Click(object sender, System.EventArgs e)
{
SaveFileDialog diag = new SaveFileDialog();
diag.Filter = "jpg files (*.jpg)|*.jpg|All files (*.*)|*.*" ; if(diag.ShowDialog() != DialogResult.OK)
return;
// Получаем адрес файла.
string filename = diag.FileName;
FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite); pictureBox1.Image.Save(fs, System.Drawing.Imaging.ImageFormat.Jpeg); fs.Close();
}
Листинг 6.6.
Обратите внимание на возможность конвертирования форматов — метод Save свойства предоставляет возможность сохранения содержимого PictureBox в различных форматах (рис. 6.9).
Рис. 6.9. Второй параметр метода Save принимает название графического формата
Мы выбрали формат Jpeg — при открытии рисунка в другом формате, например, Bmp, и последующем сохранении он будет конвертирован в этот формат.
Элемент управления PictureBox поддерживает несколько вариантов отображения загружаемого рисунка (рис. 6.10).
увеличить изображение
Рис. 6.10. Режимы отображения рисунка элементов PictureBox: подогнать размер (StretchImage), истинный размер (Normal), по центру (CenterImage), автоматический размер (AutoSize)
Мы установили свойству SizeMode значение StretchImage, и поэтому при открывании рисунка его размер автоматически подгоняется под размер PictureBox и, соответственно, формы. Истинный размер выводит рисунок в его действительных
пропорциях; при этом на экране помещается только его часть. Конечно, в этом режиме необходима прокрутка рисунка — об этом чуть позже. Режим по центру располагает рисунок в его реальных пропорциях так, чтобы его центр совпадал с центром PictureBox. Последний режим — автоматический размер — подгоняет размеры PictureBox под размер открываемого изображения.
На рис. 6.10 он визуально совпадает с режимом StretchImage — дело в том, что мы установили свойству Dock значение Fill и поэтому PictureBox уже имеет максимально большой размер.
В классе формы создадим массив ArrayMenu, в котором будут содержаться значения режимов:
private PictureBoxSizeMode[] ArrayMenu =
{
PictureBoxSizeMode.StretchImage,
PictureBoxSizeMode.Normal,
PictureBoxSizeMode.CenterImage,
PictureBoxSizeMode.AutoSize
};
Переменная selectedMode будет возвращать выбранное значение:
private int selectedMode = 0;
В обработчиках пунктов меню будем выбирать одно значение из массива:
private void mnuResize_Click(object sender, System.EventArgs e)
{
if (sender is MenuItem)
{
MenuItem menuitem = (MenuItem)sender; selectedMode = menuitem.Index; pictureBox1.SizeMode = ArrayMenu[0]; pictureBox1.Invalidate();
}
}
private void mnuActual_Click(object sender, System.EventArgs e)
{
if (sender is MenuItem)
{
MenuItem menuitem = (MenuItem)sender; selectedMode = menuitem.Index;
pictureBox1.SizeMode = ArrayMenu[1]; pictureBox1.Invalidate();
}
}
private void mnuCenterImage_Click(object sender, System.EventArgs e)
{
if (sender is MenuItem)
{
MenuItem menuitem = (MenuItem)sender; selectedMode = menuitem.Index;
pictureBox1.SizeMode = ArrayMenu[2]; pictureBox1.Invalidate();
}
}
private void mnuAutoSize_Click(object sender, System.EventArgs e)
{
if (sender is MenuItem)
{
MenuItem menuitem = (MenuItem)sender; selectedMode = menuitem.Index;
pictureBox1.SizeMode = ArrayMenu[3]; pictureBox1.Invalidate();
}
}
Листинг 6.7.
В каждом обработчике мы добавили метод Invalidate для перерисовки содержимого pictureBox1 после выбора пункта меню.
Удобно, когда выбранный пункт меню помечается галочкой (см. рис. 6.10). Переходим в режим дизайна, выделяем пункт меню Вид (View) и в окне Properties дважды щелкаем в поле события Popup:
private void mnuView_Popup(object sender, System.EventArgs e)
{
if (sender is MenuItem)
{
bool ImLoad = (pictureBox1.Image != null);
foreach (MenuItem menuitem in ((MenuItem)sender).MenuItems)
{
menuitem.Enabled = ImLoad;
menuitem.Checked = (this.selectedMode == menuitem.Index);
}
}
}
В обработчиках пунктов контекстного меню дублируем код для соответствующих пунктов главного меню:
private void cmnuResize_Click(object sender, System.EventArgs e)
{
Вставляем скопированный код для mnuResize_Click
}
private void cmnuActual_Click(object sender, System.EventArgs e)
{
Вставляем скопированный код для mnuActual _Click
}
private void cmnuCenterImage_Click(object sender, System.EventArgs e)
{
Вставляем скопированный код для mnuCenterImage _Click
}
private void cmnuAutoSize_Click(object sender, System.EventArgs e)
{
Вставляем скопированный код для mnuAutoSize _Click
}
Запускаем приложение. Оно должно походить на приложение, изображенное на рис. 6.10. Добавим теперь к нашему приложению строку состояния, на которую будет выводиться размер загруженного изображения и его адрес. Из окна ToolBox перетаскиваем на форму элемент StatusBar, щелкаем в поле его свойства Panels и в открывшемся окне StatusBarPanel Collection Editor
добавляем панели sbFile и sbSize (рис. 6.11 и рис. 6.12).
Рис. 6.11. Панель sbFile. Свойству Width панели установлено значение 500 пикселей
Рис. 6.12. Панель sbSize
Свойству ShowPanels элемента statusBar1 устанавливаем значение True. В обработчике пункта меню "Открыть" — код для вывода на панель sbFile адреса файла, а на панель sbSize — его размер:
private void mnuOpen_Click(object sender, System.EventArgs e)
{
…
//Строка для вывода во время загрузки изображения
sbFile.Text |
= "Загрузка " + diag.FileName; |
|
//Строка |
для вывода после загрузки изображения |
|
sbFile.Text |
= "Изображение " + diag.FileName; |
|
//Вывод ширины |
(Width) и высоты (Height) изображения |
|
sbSize.Text= String.Format("{0:#} x {1:#}", pictureBox1.Image.Width, pictureBox1.Image.Height);
}
Листинг 6.8.
Запускаем и тестируем приложение (рис. 6.13).
Рис. 6.13. Строка состояния приложения Picture Viewer
Печать содержимого PictureBox
Теперь займемся печатью изображений, помещенных в PictureBox. Добавляем на форму элементы PrintDocument, PageSetupDialog, PrintPreviewDialog и PrintDialog. На этот раз мы не будем настраивать визуально свойства этих элементов. Создаем обработчиков пунктов меню, в которых просто вызываем диалоговые окна:
private void mnuPageSetup_Click(object sender, System.EventArgs e)
{
PageSetupDialog diag = new PageSetupDialog(); diag.Document = printDocument1; diag.ShowDialog();
}
private void mnuPreview_Click(object sender, System.EventArgs e)
{
PrintPreviewDialog diag = new PrintPreviewDialog(); diag.Document = printDocument1;
diag.ShowDialog();
}
private void mnuPrint_Click(object sender, System.EventArgs e)
{
PrintDialog diag = new PrintDialog(); diag.Document = printDocument1;
if (diag.ShowDialog() == DialogResult.OK)
{
printDocument1.Print();
}
}
Листинг 6.9.
Обратите внимание на свойство диалоговых окон Document — мы программно устанавливаем значение printDocument1. Переключаемся в режим дизайна, выделяем объект printDocument1 и в окне Properties дважды щелкаем в поле события
PrintPage:
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
}
Дальнейший код будет относиться к этому обработчику. Проверяем наличие изображения в pictureBox1:
if (pictureBox1.Image == null)
{
e.Cancel = true; return;
}
Определяем печатную область страницы:
float leftMargin = e.MarginBounds.Left; float rightMargin = e.MarginBounds.Right; float topMargin = e.MarginBounds.Top; float bottomMargin= e.MarginBounds.Bottom;
float printableWidth = e.MarginBounds.Width; float printableHeight = e.MarginBounds.Height;
Cоздаем экземпляр graph класса Graphics:
Graphics graph = e.Graphics;
В документе, формируемом для печати, не содержится текста — ведь мы имеем дело с изображением. Но интересно будет выводить на печать строку, указывающую, к примеру, размер изображения. Для этого создадим экземпляр font класса Font:
Font font= new Font("Comic Sans MS", 16); //Определяем высоту шрифта
float fontHeight = font.GetHeight(graph); //Определяем размер пробелов
float spaceWidth = graph.MeasureString(" ", font).Width;
Определяем область, в которую будет вписываться изображение; размер наибольшей стороны изображения составляет 90% от кратчайшей стороны листа:
float imageLength;
float Xposition = leftMargin;
float Yposition = topMargin + fontHeight; if (printableWidth < printableHeight)
{
imageLength = printableWidth * 90/100; Yposition += imageLength;
}
else
{
imageLength = printableHeight * 90/100; Xposition += imageLength + spaceWidth;
}
Выводим изображение в области rectImage:
Rectangle rectImage= new Rectangle((int)leftMargin + 1, (int)topMargin + 1, (int)imageLength,(int)imageLength); graph.DrawImage(pictureBox1.Image,(int)leftMargin + 1, (int)topMargin + 1, (int)imageLength,(int)imageLength);
Определяем область rectText и выводим в нее строку с указанием размера файла (используется метод PrintTextString):
RectangleF rectText = new RectangleF(Xposition, Yposition, rightMargin - - Xposition, bottomMargin - Yposition);
PrintText (graph, font,"Размер изображения: ", Convert.ToString (pictureBox1.Image.Size), ref rectText);
}
На этом код метода printDocument1_PrintPage заканчивается. Для вывода изображения все готово. Остается только написать метод PrintText, который будет выводить текстовую строку. Привожу код этого метода с комментариями:
protected void PrintText( Graphics graph, |
Font font, |
string name, |
string text, |
ref RectangleF |
|
rectText) |
|
|
|
|
|
{ |
|
|
|
|
|
// Определяем размеры печатной области для текста: |
|
|
|
||
float leftMargin = rectText.Left; |
|
|
|
|
|
float rightMargin = rectText.Right; |
|
|
|
|
|
float topMargin = rectText.Top; |
|
|
|
|
|
float bottomMargin = rectText.Bottom; |
|
|
|
|
|
//Определяем высоту текста и координаты, где он будет выводиться: |
|
|
|||
float fontHeight = font.GetHeight(graph); |
|
|
|
|
|
float Xposition = rectText.Left; |
|
|
|
|
|
float Yposition = topMargin + fontHeight; |
|
|
|
|
|
//Определяем ширину текста и размер пробелов |
|
|
|
||
float spaceWidth = graph.MeasureString(" ", |
font).Width; |
|
|
||
float nameWidth |
= graph.MeasureString(name, font).Width; |
|
|
||
graph.DrawString(name, font,
Brushes.Black, new PointF(Xposition, Yposition)); leftMargin += nameWidth + spaceWidth;
Xposition = leftMargin;
//Формируем несколько строк для текста в случае,
//если он не будет умещаться на одной строке string[] words
=text.Split(" \r\t\n\0".ToCharArray()); foreach (string word in words)
{
float wordWidth = graph.MeasureString( word, font).Width;
if (wordWidth == 0.0) continue;
if (Xposition + wordWidth > rightMargin)
{
// Начало с новой строки
Xposition = leftMargin; Yposition += fontHeight;
if (Yposition > bottomMargin)
{
break;
}
}
graph.DrawString(word, font,Brushes.Black, new PointF(Xposition, Yposition)); Xposition += wordWidth;
}
// Исключаем область, на которую был выведен текст, из области печати //для избежания наложения текста и рисунка
rectText.Y = Yposition;
rectText.Height = bottomMargin - Yposition;
}
Листинг 6.10.
Запускаем приложение. При выборе книжной ориентации страницы рисунок вписывается с сохранением своих пропорций (рис. 6.14), затем выводится на печать (рис. 6.15).
Рис. 6.14. Предварительный просмотр приложения Picture Viewer
Рис. 6.15. Вывод рисунка в окно виртуального принтера программы pdfFactoryPro
В конце лекции приводится полный листинг приложения Picture Viewer, а исходный проект имеется на диске, прилагаемом к книге
(Code\Glava6\ Picture Viewer).
Рисование в Windows-формах
Библиотека .NET Framework предоставляет чрезвычайно большой набор классов для реализации рисования в Windows-формах. Кривые Безье, текстурные кисти и перья, трансформация изображения, всевозможные виды градиентной заливки, слои, — практически все атрибуты крупного графического пакета могут быть воссозданы на одном из языков .NET Framework. Для рассмотрения этих методов и свойств потребовалась бы отдельная книга, поэтому мы ограничимся простым приложением, позволяющим рисовать линиями различного цвета и толщины. Создайте новое приложение и назовите его EasyPaint.
Устанавливаем следующие значения свойств формы:
Form1, форма, свойство |
Значение |
BackColor |
ActiveCaptionText |
|
|
Icon |
Code\Glava6\EasyPaint\Icon\rc_bitmap.ico |
|
|
Text |
EasyPaint |
Добавляем на форму элементы управления MainMenu и ColorDialog. Создаем следующие пункты меню:
Name Text
mnuFormat &Формат
mnuColor &Цвет
mnuWidth &Толщина линии
Как обычно, рисовать мы будем при удержании и перемещении левой кнопки мыши. В классе формы добавляем объекты:
//Переменная, отвечающая за включения режима рисования. //Значение true — режим включен
bool DrawMode = false;
//Создаем массив точек, из которых будут формироваться линии
ArrayList points = new ArrayList();
Для рисования линий и кривых в библиотеки .NET Framework имеется класс Pen, экземпляр которого нам и нужно создать:
Pen mainPen;
В графических программах принято в рабочей области изменять курсор — пользователю удобнее рисовать, когда он имеет вид карандаша. Можно просто установить значение из перечисления свойства Cursor формы (рис. 6.16), но как быть, если нужен вид, не входящий в это перечисление?
Рис. 6.16. Свойство Cursor формы
Самый простой способ добавить произвольный курсор к приложению — это поместить изображение курсора (в данном случае, Pencil.cur) в папку bin\Debug приложения и в конструкторе формы указать этот курсор в качестве основного:
public Form1()
{
InitializeComponent();
this.Cursor = new Cursor("Pencil.cur"); mainPen = new Pen(this.ForeColor);
}
Во второй строчке мы также устанавливаем цвет созданного пера mainPen. Переключаемся в режим дизайна, в окне Properties в событиях формы дважды щелкаем в поле событий MouseDown, MouseMove, MouseUp:
//Событие возникает при нажатии на кнопку мыши.
private void Form1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
//Если нажата левая кнопка мыши if(e.Button==MouseButtons.Left)
{
//Включаем режим рисования
DrawMode = true;
//Удаляем все элементы из массива points points.Clear();
}
}
//Событие возникает при перемещении мыши
private void Form1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
//Если включен режим рисования if(DrawMode)
{
//Создаем экземпляр pt класса Point,
//представляющий собой точку с координатами мыши в данный момент
Point pt = new Point(e.X, e.Y); //Добавляем созданную точку в массив points points.Add(pt);
if (points.Count<=1) return;
//Создаем новый массив pts, содержащий число точек в массиве points Point [] pts = new Point [ points.Count];
//Преобразуем массив points в массив pts, начиная с первого элемента points.CopyTo(pts, 0);
//Создаем экземпляр graph класса Graphics для рисования
Graphics graph = this.CreateGraphics();
//Вызываем метод DrawCurve для рисования кривой, которому //передаем перо mainPen и массив pts graph.DrawCurve(mainPen, pts);
}
}
//Событие возникает при отпускании кнопки мыши
private void Form1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
//Отключаем режим рисования. DrawMode = false;
}
Листинг 6.11.
Логика работы предельно проста — при нажатии левой кнопки мыши включается режим рисования; при ее перемещении формируется массив точек с текущими координатами мыши, массив изображений точек pts затем передается методу DrawCurve,
который и рисует линию. При отпускании кнопки режим рисования отключается.
В обработчике пункта меню Color при выборе цвета будет создаваться новое перо:
private void mnuColor_Click(object sender, System.EventArgs e)
{
if(colorDialog1.ShowDialog()==DialogResult.OK)
{
mainPen = new Pen(colorDialog1.Color);
}
}
Запускаем приложение. Вызывая диалоговое окно выбора цвета, можно рисовать разными цветами на форме (рис. 6.17).
Рис. 6.17. Приложение EasyPaint. Курсор имеет точно такой же вид, как и в редакторе Microsoft Paint
Диалоговое окно выбора ширинылинии. Элемент управления TrackBar
Займемся теперь линией для рисования — создадим собственное диалоговое окно, позволяющее устанавливать ее ширину. В окне Solution Explorer щелкаем правой кнопкой на имени проекта и в появившемся контекстном меню выбираем Add/Add Windows Form… . Называем новую форму LineWidth.cs, из окна Toolbox перетаскиваем на нее две кнопки, надпись, панель, элементы NumericUpDown , TrackBar (рис. 6.18) и устанавливаем следующие значения свойств:
button1, свойство Значение
Name |
btnOK |
Location |
32; 224 |
Text |
&OK |
button2, свойство Значение
Name |
btnCancel |
Location |
136; 224 |
Text |
|
&Отмена |
|
|
|
|||
|
|
|
|
|
|
|
||
LineWidth, форма, свойство |
|
Значение |
||||||
|
|
|
|
|
|
|
|
|
Name |
|
|
|
|
|
LineWidth |
||
|
|
|
|
|
|
|
|
|
AcceptButton |
|
|
|
|
|
BtnOK |
||
|
|
|
|
|
|
|
|
|
CancelButton |
|
|
|
|
|
BtnCancel |
||
|
|
|
|
|
|
|
|
|
FormBorderStyle |
|
|
|
|
|
FixedDialog |
||
|
|
|
|
|
|
|
|
|
MaximizeBox |
|
|
|
|
|
False |
||
|
|
|
|
|
|
|
|
|
MinimizeBox |
|
|
|
|
|
False |
||
|
|
|
|
|
|
|
||
Size |
|
|
|
|
254; 286 |
|
||
|
|
|
|
|
|
|
|
|
Text |
|
|
|
|
|
Толщина линии |
||
|
|
|
|
|
|
|
||
label1, свойство |
Значение |
|
|
|
|
|||
Location |
8; 136 |
|
|
|
|
|
||
Text |
Толщина |
|
|
|
|
|||
|
|
|
|
|
||||
numericUpDown1, свойство |
Значение |
|
||||||
DecimalPlaces |
|
|
1 |
|
|
|||
Increment |
|
|
0,1 |
|
||||
Location |
|
|
120; 136 |
|
||||
Minimum |
|
|
1 |
|
|
|||
Value |
|
|
1 |
|
|
|||
|
|
|
||||||
trackBar1, свойство |
Значение |
|
||||||
DecimalPlaces |
|
1 |
|
|
|
|
|
|
Location |
|
16; 168 |
|
|
|
|
||
Maximum |
|
100 |
|
|
|
|
|
|
Minimum |
|
1 |
|
|
|
|
|
|
Size |
|
224; 45 |
|
|
|
|
||
TickFrequency |
|
5 |
|
|
|
|
|
|
Value |
|
5 |
|
|
|
|
|
|
Рис. 6.18. Форма LineWidth в режиме дизайна
Наша задача — сделать так, чтобы при перемещении ползунка или вводе значения на форме LineWidth выводился эскиз линии, а затем, после закрытия диалогового окна, можно было рисовать линией с выбранной толщиной. В классе формы создаем метод DrawLine, который будет рисовать на панели эскиз линии:
private void DrawLine()
{
//Создаем переменную, которая равна ординате точки, //делящий элемент panel1 по высоте пополам
int y = panel1.Bottom/2;
//Создаем точку point1 лежащей на левой границе панели, c ординатой y Point point1 = new Point(panel1.Left,y);
//Создаем точку point2 лежащей на правой границе панели, c ординатой y Point point2 = new Point(panel1.Right,y);
//Создаем экземпляр graph класса Graphics для рисования
Graphics graph = panel1.CreateGraphics();
//Определяем толщину линии, равную значению в элементе numericUpDown1 float f = Decimal.ToSingle(numericUpDown1.Value);
//Создаем экземпляр pen для рисования линии
Pen pen = new Pen (Color. Blue, f); //Соединяем точки point1 и point2 graph.DrawLine(pen, point1, point2);
}
Создаем свойство PenWidth, которое будет возвращать значение толщины для пера главной формы:
public float PenWidth
{
get
{
float f = Decimal.ToSingle(numericUpDown1.Value); return f;
