Файлы к лабораторной работе Вы можете скачать здесь.
Компьютерная графика делится на два вида:
Ниже мы познакомимся с векторной графикой в том виде, в каком
она реализована в и . Все рассматриваемые
здесь функции рисования являются методами класса и
начинаются с префикса Draw (рисуют линии) или (выполняют
заливку в границах).
Для рисования нужен объект , который определяет контекст
устройства. Класс System.Drawing. объявлен как sealed (не
поддерживает наследование) и его конструктор не является открытым,
поэтому напрямую создать объект нельзя
Для создания объекта
OnPaint() или создании
обработчика события Paint в любом классе, производном от System.Windows.Forms.Control (например, Form) мы получаем
объект Graphics как свойство Graphics класса PaintEventArgsclass MyGraphics : Form
{
public MyGraphics()
{
this.Paint +=new PaintEventHandler(MyGraphics_Paint);
}
private void MyGraphics_Paint(object sender, PaintEventArgs e)
{
Graphics graphics = e.Graphics; // Это верно!
}
}
Control ) не только в
OnPaint() или обработчике события Paint ,
но и в других местах программы, можно вызывать метод
Часто классы вызывают метод CreateGraphics() в своих конструкторах,
чтобы получить нужные данные о или выполнить инициализацию.
Элементы управления или формы могут также рисовать во время
событий мыши, клавиатуры или (контекст устройства). Главное,
чтобы объект создавался только на период обработки
события, и ни в коем случае не хранился в
System.Drawing.Printing.PrintPageEventArgs,
который в свойстве содержит подходящий для данного принтера
объект Graphics , установив обработчик события PrintPage при
печати.OwnerDraw, позволяющее программе
динамически рисовать на этом элементе MeasureItem и DrawItem передают своим обработчикам в аргументах
объекты типа MeasureItemEventArgs и DrawItemEventArgs, у
которых в свойствах имеется объект Graphics ,
связанного с принтером, не печатая никаких данных, можно
использовать метод
Win32 получить объект Graphics позволяют статические методы уровня класса Graphics .FromHdc()
и Graphics .FromHwnd() 
Прежде всего мы создадим главную форму, позволяющую управлять вызовом дочерних форм для каждого упражнения.
Перейдите
в каталог для размещения работы и создайте новое решение
с именем Petzold5

Оболочка создаст новый Petzold5 в выбранном каталоге
размещения работы.
Щелкните
правой кнопкой мыши на узле решения в панели Solution
Explorer и добавьте к решению новый пустой проект
с именем Test
В панели Solution Explorer щелкните правой кнопкой мыши на узле References (ссылки)
проекта и выполните команду Add Reference контекстного
меню
Добавьте
ссылки на нужные нам библиотеки DLL как показано на снимке
Щелкните
по узлу Test панели Solution Explorer и
добавьте в проект новый класс с именем Start.cs
Мастер оболочки создаст заготовку, которую мы и будем использовать
для
using System;
namespace Test
{
///
/// Summary description for Start.
///
public class Start
{
public Start()
{
//
// TODO: Add constructor logic here
//
}
}
}
Добавьте
в начало файла Start.cs нужные пространства
имен, соответствующие добавленным ссылкам на библиотеки DLL.
Сделайте наш класс наследником библиотечного класса Form.
Удалите сгенерированные оболочкой комментарии и должен получиться
такой кодusing System;
using System.Windows.Forms;
using System.Drawing;
namespace Test
{
public class Start : Form
{
public Start()
{
// В этом конструкторе будем размещать
// настройки формы
}
}
}
Отметьте, что как только мы добавили к нашему классу базовый
класс Form, в панели Solution Explorer пиктограмма
класса заменилась на значек формы.
Добавьте
к проекту новый пустой файл с именем AppClass.cs,
в котором мы разместим точку входа приложения
Удалите
из кода файла AppClass.cs лишние комментарии
и конструктор. Окончательно содержащий точку входа класс
должен иметь такой кодnamespace Test
{
public class AppClass
{
// Точка входа приложения
static void Main()
{
System.Windows.Forms.Application.Run(new Start());
}
}
}
Здесь мы из точки входа, расположенной в классе AppClass,
создаем экземпляр главной формы вызовом Start.
Выполните
команду меню оболочки Project/Test Properties и
в поле Output Type вкладки Common
Properties/General установите значение Windows
Application, чтобы отключить окно командной
строки приложения
В этом же окне свойств проекта установите параметр в значение Test.AppClass

Задайте
значение строки заголовка главной формы, разместив в конструкторе
класса Start следующий кодusing System;
using System.Windows.Forms;
using System.Drawing;
namespace Test
{
public class Start : Form
{
public Start()
{
this.Text = "Главная форма Lab36B";
}
}
}
Постройте
проект и убедитесь, что пустая форма создается
Теперь мы поместим на главную форму элемент управления ListBox и
заполним в нем строки заголовков упражнений, которые нам предстоит
выполнить в этой лабораторной работе. Затем мы создадим обработчики
событий выбора любого из упражнений, внутри которых будем запускать
дочерние формы, соответствующие каждому отдельному упражнению.
Упражнения будут демонстрировать возможности классов библиотеки
.NET Framework и языка C#.
Откройте
форму Start.cs в режиме View Designer и
поместите на ней из панели ListBox.
Выделите элемент управления ListBox и через
панель Properties задайте значения его свойств
в соответствии с таблицей| Свойство | Значение |
|---|---|
Name |
listBox |
|
|
Items |
DrawLines() (DrawHouse)DrawLines() (SineCurve)DrawLines() (PolyEllipse)DrawEllipse() (ClientEllipse)DrawLines() (Spiral)DrawArc() (DashedEllipse) |
Растяните
форму Start.cs так, чтобы все строки списка
были полностью в ней видны
Запустите
приложение.Мы не увидели тех изменений, которые внесли в форму и ожидали увидеть. Как и прежде, она оказалось пустой. Исправим это.
Щелкните
на форме правой кнопкой мыши и выполните команду View
CodeМы видим, что оболочка сгенерировала соответствующий код,
когда мы создавали пользовательский интерфейс. Весь сгенерированный
код разместился в функции InitializeComponent(),
но вызова этой функции в коде нет.
Поместите
в Start код вызова функции InitializeComponent()public Start()
{
this.Text = "Главная форма Lab36B";
InitializeComponent();
}
Постройте
приложение - должен получиться такой результат
Теперь нам нужно создать обработчик, который бы при выборе любой строки списка запускал соответствующую форму.
Перейдите
в режим View Designer формы Start и
выделите компонент listBox. Установите в
панели Properties режим Events и
для события SelectedIndexChanged двойным
щелчком на его поле значений создайте обработчик, который
заполните такprivate void listBox_SelectedIndexChanged(object sender, System.EventArgs e)
{
switch(listBox.SelectedIndex + 1)
{
case 1:
XMarksTheSpot frm1 = new XMarksTheSpot();
frm1.Show();
break;
case 2:
HelloPrinter frm2 = new HelloPrinter();
frm2.Show();
break;
case 3:
PrintableForm frm3 = new PrintableForm();
frm3.Show();
break;
case 4:
BoxingTheClient frm4 = new BoxingTheClient();
frm4.Show();
break;
case 5:
DrawHouse frm5 = new DrawHouse();
frm5.Show();
break;
case 6:
SineCurve frm6 = new SineCurve();
frm6.Show();
break;
case 7:
PolyEllipse frm7 = new PolyEllipse();
frm7.Show();
break;
case 8:
ClientEllipse frm8 = new ClientEllipse();
frm8.Show();
break;
case 9:
Spiral frm9 = new Spiral();
frm9.Show();
break;
case 10:
OutlineClientRectangle frm10 = new OutlineClientRectangle();
frm10.Show();
break;
case 11:
DashedEllipse frm11 = new DashedEllipse();
frm11.Show();
break;
case 12:
RoundRect frm12 = new RoundRect();
frm12.Show();
break;
case 13:
PieChart frm13 = new PieChart();
frm13.Show();
break;
case 14:
BetterPieChart frm14 = new BetterPieChart();
frm14.Show();
break;
case 15:
FillModesClassical frm15 = new FillModesClassical();
frm15.Show();
break;
}
Теперь давайте создадим заготовки из 15 форм, соответствующие
упражнениям, рассматриваемым далее. Вначале создадим одну шаблонную
форму, заполнив в ней необходимые подключаемые пространства
имен. Затем будем эту форму копировать в требуемые, задавая
соответствующие имена файлов и классов. Затем, после физического
(
Добавьте
в проект новый класс с именем Template командой Project/Add
Class
Отредактируйте
код файла Template .cs такusing System;
using System.Windows.Forms;
using System.Drawing;
namespace Test
{
public class Template : Form
{
public Template()
{
}
}
}
Сохраните
изменения файла Template .cs на диске, выполнив
команду File/Save All
В панели Solution
Explorer выделите файл Template .cs,
затем через меню Edit оболочки выполните
команду Copy, а затем 15
раз подряд команду Paste или Ctrl-V . Таким
образом мы получим 15 копий файла
Выделите
файл Template .cs и выполните команду Edit/Delete,
чтобы удалить файл физически.
В
панели Solution
Explorer переименуйте полученные копии файлов
в соответствии со следующим спискомXMarksTheSpot.csHelloPrinter.csPrintableForm.csBoxingTheClient.csDrawHouse.csSineCurve.csPolyEllipse.csClientEllipse.csSpiral.csOutlineClientRectangle.csDashedEllipse.csRoundRect.csPieChart.csBetterPieChart.csFillModesClassical.cs
Откройте
каждый из только что using System;
using System.Windows.Forms;
using System.Drawing;
namespace Test
{
public class XMarksTheSpot : Form
{
public XMarksTheSpot()
{
}
}
}
Постройте
приложение и проверьте, что при щелчке на каждой строке
списка создается соответствующая ей новая форма (пока
еще пустая).После таких грандиозных подготовительных операций перейдем
к выполнению самих упражнений. В каждом упражнении мы будем
ставить задачу и рассматривать ее решение с использованием
Чтобы нарисовать линию, нужно определить ее толщину, цвет,
стиль и другие параметры. Этим ведает класс
Создать простое приро можно так
Pen pen = new Pen(color);
где color - экземпляр структуры System.Drawing.Color.
Можно, также, использовать готовые статические неизменяемые
Pen pen = Pens.Red;
У класса System.Drawing.SystemPens 15 общих (статических,
уровня класса) неизменяемых (только для чтения) свойств, которые
возвращают объекты
Pen pen = new Pen(ForeColor);

Для создания пера с нужной толщиной линии используется перегруженные

Объекты глобальных координатах и
подвержена влиянию преобразований. Можно создать перья, толщина
которых будет всегда равна 1 пикселу, независимо от преобразований.
Для этого при создании пера следует указать в конструкторе
толщину 0 или установить свойство пера Width равным 0, например
Pen pen = new Pen(color, 0); Pen pen1 = new Pen(color); pen1.Width = 0;
Для рисования одиночных прямых служит метод DrawLine(), а
для последовательности прямых - DrawLines() класса

Метод DrawLine() рисует прямую от начальной точки до конечной,
включая в линию сами точки. Например, вызов рисует два пиксела между точками
(2,2) и (3,3), а вызов ничего не рисует.
Размер клиентской области формы определяется ее свойством
ClientSize, где ширина
ClientSize.Width и высота ClientSize.Height измеряются в пикселах,
Ниже
приведен пример программы, рисующей крестик в клиентской
областиusing System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
public class XMarksTheSpot : Form
{
public XMarksTheSpot()
{
this.Text = "Крестик в клиентской области";
this.Width += 50;
this.Height /= 2;
this.BackColor = System.Drawing.SystemColors.Window;
this.ForeColor = System.Drawing.SystemColors.WindowText;
this.ResizeRedraw = true;
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics graphics = e.Graphics;
Pen pen = new Pen(this.ForeColor);
graphics.DrawLine(pen, // Перо
0, 0, // Верхний левый угол клиентской области
this.ClientSize.Width - 1, this.ClientSize.Height - 1);// Правый нижний
graphics.DrawLine(pen,
this.ClientSize.Width - 1, 0, // Правый верхний
0, this.ClientSize.Height - 1);// Левый нижний
base.OnPaint (e);
}
}
}
Результат работы кода такой

Поставим простейшую задачу распечатать клиентскую область формы на стандартно настроенный принтер. Чтобы не отвлекаться на создание пользовательского интерфейса для управления печатью (пункт меню, кнопка и т.д.) перегрузим метод OnClick() класса Control, который наследуется формой. Этот метод инициируется всякий раз при щелчке на форме любой кнопкой мыши.
Чтобы печатать на принтере по умолчанию, нужно создать объект типа System.Drawing.Printing.PrintDocument
PrintDocument prndoc = new PrintDocument();
Это класс содержит много методов, свойств и событий

На данном этапе нам понадобятся только одно свойство, один метод и одно событие, а именно
DocumentName (public string DocumentName [ get,
set ]) - строковое свойство, в которой устанавливается идентификатор
задания в очереди печати. Обычно программа, работающая с
документом, заносит в это свойство свое имя (текст заголовка
окна программы).PrintPage - событие, в обработчике которого будем использовать
вызовы на вывод графической информации.Print() - метод начала печати. Этот метод возвращает
управление не сразу - некоторое время отображается информационное
окно с именем документа и кнопкой, позволяющей отменить задание
печати.В результате исполнения метода Print() также вызывается обработчик
события PrintPage с передаваемыми ему параметрами. Значением
первого параметра Object является ранее созданный объект prndoc
класса PrintDocument. Второй параметр имеет тип PrintPageEventArgs,
свойства которого предоставляют информацию о принтере, в том
числе свойство
Если нужно напечатать несколько страниц, следует установить
свойство HasMorePages переданного в обработчик объекта класса PrintPageEventArgs как true. Но в нашем примере будет печататься
одна страница, поэтому оставим значение свойства HasMorePages
по умолчанию (false) и обработчик события PrintPage сразу вернет
управление.
Свойство ClientSize формы предоставляет размеры своей клиентской
области в пикселах. Этого недостаточно для страницы принтера. Страница
принтера определяется тремя разными областями:
Об этом содержатся
сведения в свойстве PageBounds переданного в обработчик объекта
класса PrintPageEventArgs. Его значением является структура , чьи свойства X и Y равны нулю, а свойства Width
и Height предаставляют размеры бумаги по умолчанию, выраженные
в сотых долях дюйма. Например, для листа размером 8,5x11
дюймов значения свойств Width
и Height объекта PageBounds равно 850x1100 единиц (сотых
долей дюйма). Если в принтере задана альбомная, а не книжная
Размер этой области близок к полному размеру страницы, за
исключением полей, недоступных VisibleClipBounds объекта класса
Свойства X и Y этой структуры устанавливаются в 0, а ее свойства Width и Height равны горизонтальному и вертикальному размерам установленной области печати страницы, выраженных в тех же единицах, что будут использованы и для рисования на странице принтера.
Эта область рассчитывается с учетом 1-дюймового отступа по
периметру страницы и представляет собой границы, в пределах
которых может печатать пользователь. Эта информация предоставляется
свойством MarginBounds объекта PrintPageEventArgs в виде структуры
Цвета, видимые на экране, большей частью для принтера непригодны.
Лучшим цветом для принтера является Color.
Вот
программа, которая выводит в клиентскую область приглашение "Щелкни
на форме для печати!" и
начинает печатать на принтере после щелчка на клиентской
области формы текст.using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Printing;
namespace Test
{
public class HelloPrinter : Form
{
public HelloPrinter()
{
this.Text = "Простая программа печати";
this.Width += 50;
this.Height /= 2;
this.BackColor = System.Drawing.SystemColors.Window;
this.ForeColor = System.Drawing.SystemColors.WindowText;
this.ResizeRedraw = true;
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics graphics = e.Graphics;
System.Drawing.StringFormat strfmt = new StringFormat();
strfmt.Alignment = StringAlignment.Center;
strfmt.LineAlignment = StringAlignment.Center;
graphics.DrawString("Щелкни на форме для печати!",
Font,
new SolidBrush(ForeColor),
RectangleF.FromLTRB(// Преобразовать из Left, Top, Right, Bottom
this.ClientRectangle.Left,
this.ClientRectangle.Top,
this.ClientRectangle.Right,
this.ClientRectangle.Bottom),
strfmt);
base.OnPaint (e);
}
protected override void OnClick(EventArgs e)
{
PrintDocument prndoc = new PrintDocument();
prndoc.DocumentName = this.Text;
// Делегат события печати
// Создать вручную
prndoc.PrintPage +=new PrintPageEventHandler(prndoc_PrintPage);
prndoc.Print(); // Печать документа (инициирует событие PrintPage)
base.OnClick (e);
}
private void prndoc_PrintPage(object sender, PrintPageEventArgs e)
{
Graphics printgr = e.Graphics;
string message = "Привет студентам от Снеткова В.М.!";
// Печатаем сообщение
printgr.DrawString(message,
this.Font, Brushes.Black, 0, 0);
// Измеряем текстовый блок с сообщением для начала линии
SizeF szf = printgr.MeasureString(message, this.Font);
// Проводим линию из нижнего правого угла текстового блока
// до правого нижнего угла видимой области печати
printgr.DrawLine(Pens.Black, szf.ToPointF(),
printgr.VisibleClipBounds.Size.ToPointF());
}
}
}
Программа выводит текст в верхнем левом углу области печати страницы, а затем рисует одну диагональную линию от правого нижнего угла текстового блока выводимой строки до правого нижнего угла видимой области печати.
Усовершенствуем нашу последнюю программу так, чтобы сделать ее более универсальной, разделив код вывода на экран и печать. На данном этапе программа будет рисовать крестик, но в дальнейшем разработанный класс можно будет наследовать и переопределять в наследнике функции рисования.
Вот
как будет выглядеть усовершенствованный классusing System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Printing;
namespace Test
{
public class PrintableForm : Form
{
public PrintableForm()
{
this.Text = "Форма, готовая наследоваться";
this.BackColor = System.Drawing.SystemColors.Window;
this.ForeColor = System.Drawing.SystemColors.WindowText;
this.ResizeRedraw = true;
}
protected override void OnPaint(PaintEventArgs e)
{
// Вывод на экран
// Наша функция, см. ниже (зри в корень!)
DoPage(e.Graphics, ForeColor, ClientSize.Width, ClientSize.Height);
base.OnPaint (e);
}
protected override void OnClick(EventArgs e)
{
PrintDocument prndoc = new PrintDocument();
prndoc.DocumentName = this.Text;
// Делегат события печати
prndoc.PrintPage +=new PrintPageEventHandler(prndoc_PrintPage);
prndoc.Print(); // Печать документа (инициирует событие PrintPage)
base.OnClick (e);
}
private void prndoc_PrintPage(object sender, PrintPageEventArgs e)
{
// Получаем контекст устройства принтера
Graphics printgr = e.Graphics;
// Измеряем область печати
SizeF szf = printgr.VisibleClipBounds.Size;
// Вывод на принтер
DoPage(printgr, Color.Black, (int)szf.Width, (int)szf.Height);
}
// Создадим виртуальный метод с возможностью замещения в наследнике
protected virtual void DoPage(Graphics graphics, Color color,
int cx, int cy)
{
Pen pen = new Pen(color);
// В этом варианте замещаемой функции рисуем крестик
graphics.DrawLine(pen, 0, 0, cx - 1, cy - 1);
graphics.DrawLine(pen, cx - 1, 0, 0, cy - 1);
}
}
}
Здесь мы часть кода выделили в отдельную функцию DoPage(),
которая выводит графическую информацию и на форму, и на принтер.
Ей передают объект , цвет и размеры области вывода
функции OnPaint() - вывод на форму, и prndoc_PrintPage() -
вывод на принтер. Каждая вызывающая функция передает свои значения
аргументов. Функция OnClick() инициирует срабатывание обработчика
печати на принтер. Функцию DoPage() мы сделали защищенной (доступной
только наследникам) и виртуальной (с возможностью замещения
в наследниках), чтобы ее можно было заместить в наследующих
классах для рисования других фигур.
Теперь в следующих программах этот класс можно использовать как базовый вместо класса Form, поскольку он является расширением Form, удобным для нас.
Постройте
форму и проверьте функциональность. Экранная часть должна
выглядеть примерно так
Если использовать функцию рисования прямой, например, для изображения прямоугольника по краю клиентской области формы, то потребуется четыре ее вызова
graphics.DrawLine(pen, 0, 0, cx - 1, 0); graphics.DrawLine(pen, cx - 1, 0, cx - 1, cy - 1); graphics.DrawLine(pen, cx - 1, cy - 1, 0, cy - 1); graphics.DrawLine(pen, 0, cy - 1, 0, 0);
Здесь конечная точка текущего вызова должна повторяться как начальная точка следующего.
Для экономии кода в класс DrawLines()
рисования нескольких соединенных линий, в совокупности называемых
ломаной. Приведем пример программы, очерчивающей границу клиентской
области. В ней мы будем наследовать разработанное нами расширение PrintableForm вместо
класса Form. Обратите внимание, что в новой форме мы не устанавливаем
все ее свойства заново - они унаследованы из класса PrintableForm
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
public class BoxingTheClient : PrintableForm
{
public BoxingTheClient()
{
this.Text = "Очерчивание границы клиентской области";
this.Width += 80;
}
// Перегруженная функция базового класса PrintableForm
protected override void DoPage(Graphics graphics, Color color, int cx, int cy)
{
Point[] apoint = {
new Point(0, 0),
new Point(cx - 1, 0),
new Point(cx - 1, cy - 1),
new Point(0, cy - 1),
new Point(0, 0)
};
graphics.DrawLines(new Pen(color, 10), apoint);
}
}
}
Результатом работы кода будет

Для практики приведем еще один пример кода рисования ломаных.
На этот раз нарисуем домик. Как и в предыдущем коде, воспользуемся
собственным базовым классом PrintableForm.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
public class DrawHouse : PrintableForm
{
public DrawHouse()
{
this.Text = "Рисование домика функцией DrawLines()";
}
// Перегруженная функция базового класса PrintableForm
protected override void DoPage(Graphics graphics, Color color, int cx, int cy)
{
Point[] apoint = {
new Point(cx / 4, 3 * cy / 4),
new Point(cx / 4, cy / 2),
new Point(cx / 2, cy / 4),
new Point(3 * cx / 4, cy / 2),
new Point(3 * cx / 4, 3 * cy / 4),
new Point(cx / 4, cy / 2),
new Point(3 * cx / 4, cy / 2),
new Point(cx / 4, 3 * cy / 4),
new Point(3 * cx / 4, 3 * cy / 4),
};
graphics.DrawLines(new Pen(color), apoint);
}
}
}
Результатом работы кода будет

Метод DrawLines() позволяет настраивать тип пера так, чтобы
для толстых перьев получать нужные формы линий в местах соединения
ломаных (закругленные, угловые и т.д.). Эти элементы линий
называются концами или соединениями.
Чтобы придать концам правильный вид, DrawLines() вместо DrawLine().
Метод DrawLines() имеет очень большую производительность.
Он создан не для рисования прямых, а для DrawLines() миллион
структур
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
public class SineCurve : PrintableForm
{
public SineCurve()
{
this.Text = "Рисование синусоиды функцией DrawLines()";
}
// Перегруженная функция базового класса PrintableForm
protected override void DoPage(Graphics graphics, Color color, int cx, int cy)
{
PointF[] apointf = new PointF[cx];
for(int i = 0; i < cx; i++)
{
apointf[i].X = i;
apointf[i].Y = ((float)(cy - 1) / 2) * (float)
(1 - Math.Sin(i * 2 * Math.PI / (cx - 1)));
}
graphics.DrawLines(new Pen(color), apointf);
}
}
}
Здесь мы используем в качестве базового наш класс PrintableForm,
и переопределяем в нем DoPage(), задающий
суть выводимой информации. Для вычисления синуса используется
класс System.mscorlib.lib.
Большинство методов класса mscorlib.lib ).
Отметим, что преобразование целого к вещественному является
безопасным, поскольку не приведет к потере значений целого,
поэтому можно преобразовать неявно и компилятор это воспринимает
нормально.
Аргумент функции задается в радианах. Полный круг
( 360o ) соответствует 2*Pi радиан.
Здесь у нас cx=ClientSize.Width, cy=ClientSize.Height. Таким
образом, аргумент может принимать значения от 0 (когда i=0)
до 2*Pi (когда i=cx). Значение метода
Экран с результатом выполнения выглядит так

Метод DrawPolygon() класса DrawLines()
тем, что автоматически замыкает первую и последнюю точки рисования.
Если для практического рисования сложных кривых использовать
их обычные уравнения, то возникнут большие проблемы. Например,
r описывается уравнением
Если представить это уравнение в удобном для построения графика виде, то получим
$$y=\pm \sqrt{r^2-x^2}$$которым пользоваться нельзя.
Можно применить другой способ - параметрические уравнения,
в которых координаты вычисляются на основе третьей переменной,
которую удобно обозначить как t и
назвать временем. Тогда получим для
x(t) = r*cos(t); y(t) = r*sin(t);
Для t, изменяющегося от 0 до Pi, получим график
x(t) = Rx*cos(t); y(t) = Ry*sin(t);
Вот программа рисования
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
public class PolyEllipse : PrintableForm
{
public PolyEllipse()
{
this.Text = "Рисование эллипса функцией DrawLines()";
this.Width += 100;
}
// Перегруженная функция базового класса PrintableForm
protected override void DoPage(Graphics graphics, Color color, int cx, int cy)
{
int iNum = 2 * (cx + cy);
PointF[] apointf = new PointF[iNum + 1];
for(int i = 0; i <= iNum; i++)
{
double rAng = i * 2 * Math.PI / iNum;
apointf[i].X = (float)((cx - 1) / 2 * (1 + Math.Cos(rAng)));
apointf[i].Y = (float)((cy - 1) / 2 * (1 + Math.Sin(rAng)));
}
graphics.DrawLines(new Pen(color), apointf);
}
}
}
Изображение экрана для этой программы будет таким

Этот пример мы выполнили для упражнения, поскольку класс DrawEllipse().
Приведем код, который в клиентской области формы рисует вписанный эллипс специализированным методом
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
public class ClientEllipse : PrintableForm
{
public ClientEllipse()
{
this.Text = "Рисование эллипса функцией DrawEllipse()";
}
// Перегруженная функция базового класса PrintableForm
protected override void DoPage(Graphics graphics, Color color, int cx, int cy)
{
graphics.DrawEllipse(Pens.Red, 0, 0, cx - 1, cy - 1);
}
}
}
Выход программы следующий

Используя метод DrawLines() рисования линий
по их параметрическим уравнениям, приведем еще один код,
рисующий спираль.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
public class Spiral : PrintableForm
{
public Spiral()
{
this.Text = "Рисование спирали функцией DrawLines()";
this.Width += 100;
}
// Перегруженная функция базового класса PrintableForm
protected override void DoPage(Graphics graphics, Color color, int cx, int cy)
{
const int iNumRevs = 10;
int iNumPoints = iNumRevs * 2 * (cx + cy);
PointF[] apointf = new PointF[iNumPoints];
double rAngle, rScale;
for(int i = 0; i < iNumPoints; i++)
{
rAngle = iNumRevs * i * 2 * Math.PI / iNumPoints;
// Когда хотя-бы один операнд вещественный, то автоматически преобразуется
rScale = 1 - i / Convert.ToDouble(iNumPoints);
apointf[i].X = (float)(Convert.ToDouble(cx) / 2 * (1 + rScale * Math.Cos(rAngle)));
apointf[i].Y = (float)(Convert.ToDouble(cy) / 2 * (1 + rScale * Math.Sin(rAngle)));
}
graphics.DrawLines(new Pen(color), apointf);
}
}
}
Экран программы будет таким

Обратите внимание, что когда мы вычисляем масштабный коэффициент
rScale, то в операции деления один из операндов приводим к
вещественному типу. В этом случае перед делением процессор
автоматически приводит к вещественному и второй
Прямоугольник можно нарисовать при помощи функций DrawLine() и DrawLines(),
что мы и делали ранее, вписывая его в клиентскую область формы.
Но проще сделать это методом DrawRectangle() объекта .
Имеются три перегруженных версии метода DrawRectangle() и две DrawRectangles():

В каждой из версий прямоугольник определяется точкой левого
верхнего угла, шириной и высотой. Аналогично определяется структура в одной из версий DrawRectangle(). Ширина и высота
прямоугольника должны быть положительными. Прямоугольник с
отрицательными шириной и высотой не вызовут исключения, но
и не будут нарисованы.
При рисовании прямоугольников часто возникает ошибка смещения
на 1 пиксел. При использовании свойства пера по умолчанию методу DrawRectangle() передаются значения ширины и высоты, равные
3. Если задать ширине и высоте значение 2, будет нарисован
прямоугольник размером 3x3 пиксела, внутренняя область которого
состоит лишь из одного пиксела.
Вот программа, которая рисует прямоугольник по краю клиентской
области методом DrawRectangle()
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
public class OutlineClientRectangle : PrintableForm
{
public OutlineClientRectangle()
{
this.Text = "Рисование прямоугольника DrawRectangle()";
}
// Перегруженная функция базового класса PrintableForm
protected override void DoPage(Graphics graphics, Color color, int cx, int cy)
{
graphics.DrawRectangle(new Pen(Color.Red, 20), 0, 0, cx - 1, cy - 1);
}
}
}
Результатом работы будет экран

DrawArc() класса

Два последних аргумента задают углы в
градусах. Предпоследний
аргумент определяет начало

Приведем код, рисующий эллипс с пунктирным 10o,
а промежутков между штрихами 15o - 10o =
5o
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
public class DashedEllipse : PrintableForm
{
public DashedEllipse()
{
this.Text = "Рисование эллипса функцией DrawArc()";
}
// Перегруженная функция базового класса PrintableForm
protected override void DoPage(Graphics graphics, Color color, int cx, int cy)
{
int width = 30;
Rectangle rect = new Rectangle(width / 2, width / 2,
cx - 1 - width, cy - 1 - width);
Pen pen = new Pen(Color.Red, width);
for(int iAngle = 0; iAngle < 360; iAngle += 15)
graphics.DrawArc(pen, rect, iAngle, 10);
}
}
}
Выходной экран имеет вид

В есть функция RoundRect(), рисующая прямоугольник
с закругленными углами. Эта функция принимает 4 аргумента,
указывающих координаты левого верхнего и правого нижнего
углов прямоугольника, плюс еще два, определяющие ширину и
высоту
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
public class RoundRect : PrintableForm
{
public RoundRect()
{
this.Text = "Рисование прямоугольника со скругленными углами (RoundRect)";
}
// Перегруженная функция базового класса PrintableForm
protected override void DoPage(Graphics graphics, Color color, int cx, int cy)
{
RoundedRectangle(graphics, Pens.Red,
new Rectangle(0, 0, cx - 1, cy - 1),
new Size(cx / 5, cy / 5));
}
private void RoundedRectangle(Graphics gr, Pen pn, Rectangle rect, Size sz)
{
gr.DrawLine(pn, rect.Left + sz.Width / 2, rect.Top,
rect.Right - sz.Width / 2, rect.Top);
gr.DrawArc(pn, rect.Right - sz.Width, rect.Top,
sz.Width, sz.Height, 270, 90);
gr.DrawLine(pn, rect.Right, rect.Top + sz.Height / 2,
rect.Right, rect.Bottom - sz.Height / 2);
gr.DrawArc(pn, rect.Right - sz.Width, rect.Bottom - sz.Height,
sz.Width, sz.Height, 0, 90);
gr.DrawLine(pn, rect.Right - sz.Width / 2, rect.Bottom,
rect.Left + sz.Width / 2, rect.Bottom);
gr.DrawArc(pn, rect.Left, rect.Bottom - sz.Height,
sz.Width, sz.Height, 90, 90);
gr.DrawLine(pn, rect.Left, rect.Bottom - sz.Height / 2,
rect.Left, rect.Top + sz.Height / 2);
gr.DrawArc(pn, rect.Left, rect.Top,
sz.Width, sz.Height, 180, 90);
}
}
}
Вывод на экран будет таким

Нарисованная фигура состоит из восьми частей, нарисованных
отдельно. Это значит, у каждой из восьми частей
Рисование секторов выполняется функцией DrawPie() класса . От метода DrawArc() эта функция отличается только
тем, что соединяет концы

Приведем пример, строящий круговую диаграмму на основе массива
значений, хранимых как
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
public class PieChart : PrintableForm
{
private int[] aiValues = {50, 100, 25, 150, 100, 75};
public PieChart()
{
this.Text = "Рисование круговой диаграммы (PieChart)";
}
// Перегруженная функция базового класса PrintableForm
protected override void DoPage(Graphics graphics, Color color, int cx, int cy)
{
Rectangle rect = new Rectangle(50, 50, 200, 200);
Pen pen = new Pen(color);
int iTotal = 0;
float fAngle = 0;
Single fSweep; // Структура Single равноценна типу float
foreach(int iValue in aiValues)
iTotal += iValue;
foreach(int iValue in aiValues)
{
fSweep = 360.0F * iValue / (Single)iTotal;
DrawPieSlice(graphics, pen, rect, fAngle, fSweep);
fAngle += fSweep;
}
}
// Создание замещаемого метода
protected virtual void DrawPieSlice(Graphics gr, Pen pn, Rectangle rect,
Single fAngle, Single fSweep)
{
gr.DrawPie(pn, rect, fAngle, fSweep);
}
}
}
Выход на экран будет таким

Метод DoPage() суммирует значения массива и рассчитывает
угол каждого сектора, деля значение для этого сектора на
сумму значений всех секторов и умножая частное на 360o.
Мы специально определили метод DrawPieSlice() виртуальным,
чтобы можно было просто написать улучшенный вариант программы,
используя класс PieChart как базовый
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
public class BetterPieChart : PieChart
{
public BetterPieChart()
{
this.Text = "Рисование круговой диаграммы (BetterPieChart)";
this.Height += 20;
}
// Создание замещаемого метода
protected override void DrawPieSlice(Graphics gr, Pen pn,
Rectangle rect, Single fAngle, Single fSweep)
{
Single fSlice = Convert.ToSingle(2 * Math.PI / 360 * (fAngle + fSweep / 2));
rect.Offset(Convert.ToInt32((double)rect.Width / 10 * Math.Cos(fSlice)),
Convert.ToInt32((double)rect.Height / 10 * Math.Sin(fSlice)));
base.DrawPieSlice (gr, pn, rect, fAngle, fSweep);
}
}
}
Экранный вывод будет таким

Для некоторых функций рисования содержит аналогичные методы, которые вместо префикса Draw
начинаются с

Есть еще один метод, который похож на метод DrawPolygon(),
но имеет один дополнительный параметр FillMode.

FillMode - это статическое перечисление, определяемое в
пространстве имен System.Drawing.Drawing2D. У него всего
два возможных константных значения
| Член | Значение | Пояснение |
|---|---|---|
| System.Drawing.Drawing2D.FillMode. |
0 | Задано по умолчанию, чередует закрашенные и незакрашенные области фигуры |
| System.Drawing.Drawing2D.FillMode.Winding | 1 | Закрашивает максимально возможное число внутренних областей фигуры |
Режим заливки имеет значение лишь тогда, когда линии, определяющие
многоугольник, пересекаются. Он определяет, какие из замкнутых
областей будут закрашены, а какие - нет. Если режим заливки
в методе FillPolygon() не задан, по умолчанию используется
режим . При этом замкнутая область заполняется,
если она отделена от бесконечности нечетным числом границ.
Классический пример - пятиконечная
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace Test
{
public class FillModesClassical : PrintableForm
{
public FillModesClassical()
{
this.Text = "Заливка сложной фигуры (FillModesClassical)";
this.ClientSize = new Size(2 * this.ClientSize.Height,
this.ClientSize.Height);
}
// Перегруженная функция базового класса PrintableForm
protected override void DoPage(Graphics graphics, Color color, int cx, int cy)
{
SolidBrush brush = new SolidBrush(color);
Point[] apoint = new Point[5];
int iMax = apoint.GetUpperBound(0); // Верхнее значение индекса-ключа
// Первая звезда
for(int i = 0; i <= iMax; i++)
{
Double rAngle = (i * 0.8 - 0.5) * Math.PI;
apoint[i].X = Convert.ToInt32(cx * (0.25 + 0.24 * Math.Cos(rAngle)));
apoint[i].Y = Convert.ToInt32(cy * (0.5 + 0.48 * Math.Sin(rAngle)));
}
graphics.FillPolygon(brush, apoint, FillMode.Alternate);
// Вторая звезда
for(int i = 0; i <= iMax; i++)
apoint[i].X += cx / 2;
graphics.FillPolygon(brush, apoint, FillMode.Winding);
}
}
}
Экранный результат имеет вид

Откомпилируйте
проект в режиме Release и проверьте его функциональность во всех построенных
нами режимах
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.