Лабораторная работа № 1 Тема: «Виртуальные графические устройства. Особенности создания графики в Microsoft Windows на языке С#» Ранее, чтобы выводить что-то на экран монитора в графическом режиме, программа должна была определить тип видеоадаптера и его возможности, переключить видеоадаптер в нужный видеорежим, а затем выполнять вывод изображения с учетом особенностей этого видеорежима. При создании ОС Microsoft Windows компания Microsoft избавила программистов от необходимости учитывать аппаратные особенности видеоадаптеров, переложив эту задачу на драйверы видеоадаптеров. Что же касается приложений, то для них в составе ОС Microsoft Windows был предусмотрен набор системных функций, реализующих интерфейс графических устройств (Graphics Device Interface, GDI). Интерфейс графических устройств GDI предназначен для взаимодействия приложений Microsoft Windows с графическими устройствами, такими как видеоадаптер, принтер или плоттер. Когда приложения обращаются к GDI для выполнения операции вывода графического изображения, они работают не с реальными (физическими) устройствами вывода, а с логическими устройствами. Приложения Microsoft Windows не определяют тип видеоадаптера (EGA, VGA, SVGA и т.п.), а работают с логическим видеоадаптером, имеющим феноменальные характеристики: способность отображать практически любой цвет, имеющим огромное разрешение и т. д. Выполняя запрос приложения, GDI обращается к драйверу соответствующего устройства вывода, работающему, в свою очередь, непосредственно с физическим устройством вывода. В процессе выполнения запроса GDI (или драйвер) учитывает ограниченные возможности видеоадаптера и его аппаратные особенности, делая необходимые приближения. Например, приложение может указать для цвета линии любой из примерно 16 млн. цветов, однако не всякое устройство обладает таким цветовым разрешением (ограничения на количество одновременно отображаемых цветов присутствуют, например, в карманных компьютерах). В зависимости от типа физического устройства, используемого для вывода, GDI может выбрать для отображения цвет, наиболее соответствующий запрошенному цвету, и допустимый для устройства. С точки зрения приложений, интерфейс GDI состоит из контекста отображения и инструментов, предназначенных для рисования. Контекст отображения можно сравнить с листом бумаги, на котором приложение рисует то или иное графическое изображение, а также пишет текст. Инструменты для рисования — это перья, кисти (а также шрифты и даже целые графические изображения), с помощью которых создается изображение. Кроме контекста отображения и инструментов для рисования, приложениям доступны десятки функций программного интерфейса GDI, предназначенные для работы с контекстом отображения и инструментами. Что же касается приложений Microsoft .NET Framework, то они реализуют возможности интерфейса GDI+ с помощью набора соответствующих классов и интерфейсов. В терминах ОС Microsoft Windows контекст отображения (display context) представляет собой структуру данных, описывающую устройство отображения. В этой структуре хранятся различные характеристики устройства и набор инструментов для рисования, выбранный по умолчанию. Приложение может выбирать в контекст отображения различные инструменты (например, перья различной толщины и цвета, с различными «наконечниками»). Поэтому если Вам надо нарисовать линию красного или зеленого цвета, перед выполнением операции следует выбрать в контекст отображения соответствующее перо. 1 Приложение может создать контекст отображения не только для окна приложения, но и для любого другого графического устройства вывода, например, для принтера. В последнем случае оно может рисовать на принтере различные изображения, используя те же функции, что и для рисования в окне приложения. Можно создать контекст отображения для метафайла. Метафайл — это обычный файл или файл в памяти, в котором хранятся последовательности команд интерфейса GDI. Приложение может выполнять графический вывод в метафайл как в обычное устройство вывода, а затем «проигрывать» метафайл на реальном устройстве вывода. Контекст устройства в терминах ОС Microsoft Windows выступает в роли связующего звена между приложением и драйвером устройства (рисунок 1) и представляет собой структуру данных размером примерно 800 байт. Эта структура данных содержит информацию о том, как нужно выполнять операции вывода на данном устройстве (цвет и толщину линии, тип системы координат и т. д.). Рисунок 1 - Вывод данных через контекст устройства Если приложение получает или создает контекст для устройства отображения, такой контекст называется контекстом отображения (display context). Поэтому когда, например, приложение получает контекст для отображения в одном из своих окон, такой контекст называется контекстом отображения. Если же ему требуется выполнять операцию вывода для устройства (для принтера или для экрана дисплея), приложение должно получить или создать контекст устройства (device context). Следует понимать, что контексты устройства и отображения содержат описания одних и тех же характеристик и имеют одинаковую структуру. Название контекста определяется только тем, относится ли контекст к окну отображения или устройству вывода. Класс Graphics Класс Graphics, реализует в себе как свойства контекста отображения, так и инструменты, предназначенные для рисования в этом контексте. Для того чтобы приложение могло что-нибудь нарисовать в окне, оно должно, прежде всего, получить или создать для этого окна объект класса Graphics. Далее, пользуясь свойствами и методами этого объекта, приложение может рисовать в окне различные фигуры или текстовые строки. Приложения Microsoft .NET Framework могут получить идентификатор формы или любого другого элемента управления при помощи свойства Handle. В частности, наше приложение получает идентификатор окна формы Form1 с помощью свойства this.Handle. Зная идентификатор окна, с помощью метода Graphics.FromHwnd нетрудно получить нужный нам объект класса Graphics: 2 Graphics g = Graphics.FromHwnd(this.Handle); Для того, чтобы рисование осуществлялось не во всем окне, а только в определенном объекте, например в объекте Panel (см. Toolbox). Graphics g = Graphics.FromHwnd(panel1.Handle); Этот метод получает контекст отображения не для всего окна формы, а только для окна панели panel1. С этой целью он передает методу Graphics.FromHwnd идентификатор окна панели, извлеченный из свойства panel1.Handle. В результате последующая операция рисования будет выполнена в системе координат, связанной с окном панели, а не с окном формы. Для того чтобы рисовать, художнику нужна кисть. Программист, создающий приложение GDI+, тоже нуждается в инструментах для рисования. Мы создадим кисть как объект класса SolidBrush: SolidBrush redBrush = new SolidBrush(Color.Red); С помощью этой кисти можно рисовать замкнутые геометрические фигуры, закрашенные заданным цветом. Через единственный параметр мы передаем конструктору класса SolidBrush цвет кисти Color.Red. Таким образом, мы будем рисовать кистью красного цвета. В классе Graphics имеется множество различных методов, предназначенных для рисования самых разных геометрических фигур, таких как линии, прямоугольники, овалы и окружности, многоугольники, кривые Безье и т.д. Но вот чего в этом классе нет, так это метода, с помощью которого можно было бы нарисовать одну единственную точку. Заметим, однако, что вместо точки мы можем нарисовать закрашенный квадрат с шириной стороны, равным 1 пикселу. Эта задача выполняется при помощи метода FillRectangle: g.FillRectangle(redBrush, e.X, e.Y, 1, 1); Обратите внимание на то, что метод FillRectangle вызывается для объекта g класса Graphics, созданного нами для окна формы Form1. Поэтому квадрат будет нарисован в окне этой формы. В качестве первого параметра методу FillRectangle передается кисть redBrush, которую нужно использовать для рисования. Кисть нужна и для других методов класса Graphics, предназначенных для рисования геометрических фигур. Второй и третий параметры метода FillRectangle задают координаты, в которых будет нарисован квадрат. Начало системы координат при этом находится в левом верхнем углу окна, для которого был получен объект Graphics. В нашем случае это левый верхний угол внутренней области окна формы Form1. Ось X в этой системе координат, принятой по умолчанию, направлена слева направо, а ось Y — сверху вниз (рис. 2). 3 Рисунок 2 - Система координат по умолчанию И, наконец, последние два параметра метода FillRectangle задают, соответственно, ширину и высоту прямоугольника. g.FillEllipse(redBrush,e.X, e.Y, 10, 10); Назначение параметров метода FillEllipse, предназначенного для рисования закрашенных эллипсов, аналогично назначению параметров метода FillRectangle. При этом два последних параметра задают, соответственно, ширину и высоту прямоугольной области, занимаемой эллипсом. Рисование в окнах приложений Microsoft Windows Известно, что приложения Microsoft Windows не могут выводить текст или графику ни с помощью стандартных функций библиотеки компилятора, например таких, как printf, cprintf или putc. Не помогут и прерывания BIOS, так как приложениям Microsoft Windows запрещено их использовать (во всяком случае, для вывода на экран). Все эти функции ориентированы на консольный вывод в одно-единственное окно, предоставленное в полное распоряжение программе MS-DOS. В ОС Microsoft Windows параллельно работающие приложения должны совместно использовать один общий экран монитора. Для этого они создают перекрывающиеся и перемещаемые окна, в которые и выполняют вывод текста или графических изображений. ОС Microsoft Windows берет на себя все проблемы, связанные с возможным перекрытием или перемещением окон, так что правильно спроектированные приложения не должны специально заботиться о восстановлении содержимого окна после его перекрытия другим окном. Способ, которым приложение Microsoft Windows выводит что-либо в свои окна, коренным образом отличается от способа, используемого в программах MS-DOS. Программа MS-DOS формирует изображение на экране «рассредоточенным» образом, то есть в любом месте программы могут вызываться функции, которые выводят что-либо на экран. Например, сразу после запуска программа может нарисовать на экране диалоговую панель, а затем в любой момент времени и, что самое главное, из любого места программы модифицировать ее. Приложения Microsoft Windows также могут выводить в созданные ими окна текст или графические изображения в любой момент времени и из любого места. 4 Сообщение WM_PAINT Прежде чем приступить к описанию способов рисования в окнах, применяемых приложениями .NET Frameworks, расскажем о том, как это делают «классические» приложения Microsoft Windows, составленные на языках программирования C или C++. ОС Microsoft Windows следит за перемещением и изменением размера окон и при необходимости извещает приложения, о том, что им следует перерисовать содержимое окна. Для извещения в очередь приложения записывается сообщение с идентификатором WM_PAINT. Получив такое сообщение, функция окна должна выполнить перерисовку всего окна или его части, в зависимости от дополнительных данных, полученных вместе с сообщением WM_PAINT. Напомним, что функция окна выполняет обработку всех сообщений, поступающих в окно приложения Microsoft Windows. Для облегчения работы по отображению содержимого окна весь вывод в окно обычно выполняют в одном месте приложения — при обработке сообщения WM_PAINT в функции окна. Приложение должно быть сделано таким образом, чтобы в любой момент времени при поступлении сообщения WM_PAINT функция окна могла перерисовать все окно или любую его часть, заданную своими координатами. Последнее нетрудно сделать, если приложение будет хранить где-нибудь в памяти свое текущее состояние, пользуясь которым функция окна сможет перерисовать окно в любой момент времени. Здесь не имеется в виду, что приложение должно хранить образ окна в виде графического изображения и восстанавливать его при необходимости, хотя это и можно сделать. Приложение должно хранить информацию, на основании которой оно может в любой момент времени перерисовать окно. Например, если приложение выводит на экран дамп оперативной памяти, оно должно хранить информацию о начальном адресе отображаемого участка памяти и размере этого участка. При получении сообщения WM_PAINT приложение должно определить, какой участок окна необходимо перерисовать и какому диапазону адресов дампа памяти этот участок соответствует. Затем приложение должно заново вывести участок дампа памяти в окно, опрашивая соответствующие адреса и выполняя преобразование байт памяти в символьные, шестнадцатеричные или другие используемые для вывода дампа числа. Сообщение WM_PAINT передается функции окна, если стала видна область окна, скрытая раньше другими окнами, если пользователь изменил размер окна или выполнил операцию прокрутки изображения в окне. Приложение может передать функции окна сообщение WM_PAINT явным образом, вызывая функции программного интерфейса Win32 API, такие как UpdateWindow, InvalidateRect или InvalidateRgn. Иногда ОС Microsoft Windows может сама восстановить содержимое окна, не посылая сообщение WM_PAINT. Например, при перемещении курсора мыши или значка свернутого приложения ОС восстанавливает содержимое окна. Если же ОС не может восстановить окно, функция окна получает от ОС сообщение WM_PAINT и перерисовывает окно самостоятельно. Перед тем как записать сообщение WM_PAINT в очередь приложения, ОС посылает функции окна сообщение WM_ERASEBKGND. Если функция окна не обрабатывает сообщение WM_ERASEBKGND, передавая его функции DefWindowProc, последняя в ответ на это сообщение закрашивает внутреннюю область окна с использованием кисти, указанной в классе окна (при регистрации класса окна). Поэтому, если функция окна нарисует что-либо в окне во время обработки других сообщений, отличных от WM_PAINT, после прихода первого же сообщения WM_PAINT нарисованное изображение будет закрашено. 5 Что же делать в том случае, когда по логике работы приложения требуется изменить содержимое окна не во время обработки сообщения WM_PAINT, а в любом другом месте приложения? В этом случае приложение должно сообщить ОС Microsoft Windows, что необходимо перерисовать часть окна или все окно. При этом в очередь приложения будет записано сообщение WM_PAINT, обработка которого приведет к нужному результату. Можно указать ОС, что был изменен прямоугольный участок окна или область произвольной формы, например эллипс или многоугольник. Для этого предназначены функции InvalidateRect и InvalidateRgn программного интерфейса Win32 API. Пример обработки события Paint Для форм класса System.Windows.Forms предусмотрен удобный объектноориентированный способ, позволяющий приложению при необходимости перерисовывать окно формы в любой момент времени. Когда вся клиентская область окна формы или часть этой области требует перерисовки, форме передается событие Paint. Все, что требуется от программиста, это создать обработчик данного события, наполнив его необходимой функциональностью. Для наглядной демонстрации методики обработки события Paint мы подготовим простейшее приложение PaintApp, рисующее в своем окне текстовую строку и геометрические фигуры. Прежде всего, создайте проект приложения PaintApp, пользуясь мастером проектов системы разработки Microsoft Visual Studio .NET. Далее выделите в окне дизайнера форму Form1 и откройте вкладку событий для этой формы, показанную на рис.3. Рисунок 3 - Добавление обработчика события Paint Отыщите на вкладке строку события Paint и щелкните ее дважды левой клавишей мыши. В результате будет создан обработчик события Form1_Paint (как это видно на рис. 6). Этот обработчик будет получать управление всякий раз, когда по тем или иным причинам возникнет необходимость в перерисовке содержимого окна нашего приложения. Вот в каком виде будет создан обработчик события Paint: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { 6 } Обработчику Form1_Paint передаются два параметра. Через первый параметр передается ссылка на объект, вызвавший событие. В нашем случае это будет ссылка на форму Form1. Что же касается второго параметра, то через него передается ссылка на объект класса PaintEventArgs. Этот объект имеет два свойства, доступных только для чтения — Graphics и ClipRectangle. Класс Graphics — он представляет собой контекст отображения, необходимый для рисования текста и геометрических фигур. Обработчик события Paint получает контекст отображения через свои параметры, поэтому программисту не нужно определять его специальным образом. Через свойство ClipRectangle передаются границы области, которую должен перерисовать обработчик события Paint. Эти границы передаются в виде объекта класса Rectangle. Свойства этого класса Left, Right, Width и Height, наряду с другими свойствами, позволяют определить расположение и размеры области. Заметим, что в простейших случаях обработчик события Paint может игнорировать свойство ClipRectangle, перерисовывая содержимое окна полностью. Однако процесс перерисовки содержимого окна можно заметно ускорить, если перерисовывать не все окно, а только область, описанную свойством ClipRectangle. Ускорение будет особенно заметным, если в окне нарисовано много текста и геометрических фигур. Итак, отредактируем исходный текст приложения PaintApp таким образом, чтобы в его окне была нарисована текстовая строка, прямоугольник и эллипс. Прежде всего, создайте в классе Form1 поле text класса string, в котором будет храниться отображаемая текстовая строка: public string text; Добавьте также в конструктор класса Form1 строку инициализации упомянутого поля text: public Form1() { // // Required for Windows Form Designer support // InitializeComponent(); // // TODO: Add any constructor code after InitializeComponent call // text = "Обработка события Paint"; } И, наконец, измените исходный текст обработчика события Form1_Paint следующим образом: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g = e.Graphics; g.Clear(Color.White); g.DrawString(text, new Font("Helvetica", 15), Brushes.Black, 0, 0); g.DrawRectangle(new Pen(Brushes.Black,2), 10, 30, 200, 100); g.DrawEllipse(new Pen(Brushes.Black,2), 150, 120, 100, 130); } 7 Здесь в теле обработчика Form1_Paint мы определили локальную переменную g класса Graphics, предназначенную для хранения контекста отображения. Эта переменная инициализируется при помощи значения, полученного из свойства Graphics первого параметра обработчика Form1_Paint: Graphics g = e.Graphics; Получив контекст отображения, наш обработчик события Paint может рисовать в соответствующем окне все, что угодно. Вначале мы закрашиваем окно белым цветом, вызывая для этого метод Clear, определенный в классе Graphics: g.Clear(Color.White); Таким способом мы можем закрасить фон, цвет которого задан для формы в свойстве BackColor. Далее мы вызываем методы DrawString, DrawRectangle и DrawEllipse, также определенные в классе Graphics: g.DrawString(text, new Font("Helvetica", 15), Brushes.Black, 0, 0); g.DrawRectangle(new Pen(Brushes.Black,2), 10, 30, 200, 100); g.DrawEllipse(new Pen(Brushes.Black,2), 150, 120, 100, 130); Первый из них рисует текстовую строку в верхней части окна, а два других — прямоугольник и эллипс, соответственно (рис. 4). Рисунок 4 - Результат выполнения приложения PaintApp Запустив приложение PaintApp, Вы сможете убедиться в том, что нарисованное изображение не пропадает при изменении размеров окна. Оно заново перерисовывается и в том случае, если окно приложения PaintApp оказывается временно перекрыто окном другого приложения. 8 Методы и свойства класса Graphics. Рисование геометрических фигур Имена большого количества методов, определенных в классе Graphics, начинается с префикса Draw* и Fill*. Первые из них предназначены для рисования текста, линий и не закрашенных фигур (таких, например, как прямоугольные рамки), а вторые — для рисования закрашенных геометрических фигур. Мы рассмотрим применение только самых важных из этих методов, а полную информацию Вы найдете в документации. Линия Метод DrawLine рисует линию, соединяющую две точки с заданными координатами. Ниже приведены прототипы различных перегруженных версий этого метода: public void DrawLine(Pen, Point, Point); public void DrawLine(Pen, PointF PointF); public void DrawLine(Pen, int, int, int, int); public void DrawLine(Pen, float, float, float, float); Первый параметр задает инструмент для рисования линии — перо. Перья создаются как объекты класса Pen, например: Pen p = new Pen(Brushes.Black,2); Здесь создали черное перо толщиной 2 пиксела. Создавая перо, можно выбрать его цвет, толщину и тип линии, а также другие атрибуты. Остальные параметры перегруженных методов DrawLine задают координаты соединяемых точек. Эти координаты могут быть заданы как объекты класса Point и PointF, а также в виде целых чисел и чисел с плавающей десятичной точкой. В классах Point и PointF определены свойства X и Y, задающие, соответственно, координаты точки по горизонтальной и вертикальной оси. При этом в классе Point эти свойства имеют целочисленные значения, а в классе PointF — значения с плавающей десятичной точкой. Третий и четвертый вариант метода DrawLine позволяет задавать координаты соединяемых точек в виде двух пар чисел. Первая пара определяет координаты первой точки по горизонтальной и вертикальной оси, а вторая — координаты второй точки по этим же осям. Разница между третьим и четвертым методом заключается в использовании координат различных типов (целочисленных int и с плавающей десятичной точкой float). Чтобы испытать метод DrawLine в работе, создайте приложение DrawLineApp (аналогично тому, как Вы создавали предыдущее приложение). В этом приложении создайте следующий обработчик события Paint: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g=e.Graphics; g.Clear(Color.White); for(int i=0; i<50; i++) { g.DrawLine(new Pen(Brushes.Black, 1), 10, 4 * i + 20, 200, 4 * i + 20); } } 9 Здесь мы вызываем метод DrawLine в цикле, рисуя 50 горизонтальных линий. Набор линий Вызвав один раз метод DrawLines, можно нарисовать сразу несколько прямых линий, соединенных между собой. Иными словами, метод DrawLines позволяет соединить между собой несколько точек. Координаты этих точек по горизонтальной и вертикальной оси передаются методу через массив класса Point или PointF: public void DrawLines(Pen, Point[]); public void DrawLines(Pen, PointF[]; Для демонстрации возможностей метода DrawLines создайте приложение DrawLinesApp. В классе Form1 этого приложения мы создаем кисть pen для рисования линий, а также массив точек points, которые нужно соединить линиями: Pen pen = new Pen(Color.Black, 2); Point[] points = new Point[50]; public Form1() { // // Required for Windows Form Designer support // InitializeComponent(); for(int i=0; i < 20; i++) { int xPos; if(i%2 == 0) { xPos=10; } else { xPos=400; } points[i] = new Point(xPos, 10 * i); } } Обратите внимание, что координаты точек по горизонтальной оси зависят от того, является ли значение переменной цикла i четным или нечетным. Рисование линий выполняется за один вызов метода DrawLines во время обработки события Paint: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g = e.Graphics; g.Clear(Color.White); g.DrawLines(pen, points); } 10 Запустив приложение вы увидите, внешний вид линий оставляет желать лучшего — вместо прямых линий мы получили ломаные линии! Для того, чтобы избежать излишней пикселизации, для этого нужно настроить один из параметров контекста отображения, а именно параметр SmoothingMode. Этот параметр задает режим сглаживания при отображении линий. Включите в исходный текст программы пространство имен System.Drawing.Drawing2D, в котором определено перечисление SmoothingMode: using System.Drawing.Drawing2D; Далее измените обработчик Form1_Paint следующим образом: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g=e.Graphics; g.Clear(Color.White); g.SmoothingMode = SmoothingMode.HighQuality; g.DrawLines(pen, points); } Константа SmoothingMode.HighQuality, определенная в перечислении SmoothingMode, задает высококачественное сглаживание при отображении линии. Прямоугольник Метод DrawRectangle позволяет рисовать прямоугольники, заданные координатой верхнего левого угла, а также шириной и высотой. В библиотеке классов .NET Frameworks имеется три перегруженных варианта этого метода: public void DrawRectangle(Pen, Rectangle); public void DrawRectangle(Pen, int, int, int, int); public void DrawRectangle(Pen, float, float, float, float); В качестве первого параметра этим методам передается перо класса Pen. Остальные параметры задают расположение и размеры прямоугольника. Класс Rectangle используется для описания расположения и размеров прямоугольной области. Свойства X и Y этого класса задают координаты верхнего левого угла прямоугольной области, соответственно, по горизонтальной и вертикальной оси координат. Свойства Width и Height, хранят, соответственно, ширину и высоту прямоугольной области. В классе Rectangle определены и другие свойства, а также методы. Подробное описание этого класса Вы найдете в документации. Варианты метода DrawRectangle с пятью параметрами позволяют задавать расположение и размеры прямоугольника в виде целых чисел, а также в виде числе с плавающей десятичной точкой. Второй и третий параметр задает расположение верхнего левого угла по горизонтальной и вертикальной оси координат, соответственно, а четвертый и пятый — ширину и высоту прямоугольника. Набор прямоугольников За один вызов метода DrawRectangles программа может нарисовать сразу несколько прямоугольников. Существует два перегруженных варианта этого метода: 11 public void DrawRectangles(Pen, Rectangle[]); public void DrawRectangles(Pen, RectangleF[]); Первый из этих методов получает в качестве второго параметра ссылку на массив объектов класса Rectangle, описывающих размеры и расположение прямоугольных областей. Второй метод использует для этого объекты класса RectangleF, свойства X, Y, Width и Height которого задают расположение и размеры прямоугольника в виде чисел с плавающей десятичной точкой. В классе Form1 этого приложения мы определили перо myPen и массив вложенных друг в друга прямоугольников myRectsArray: Pen myPen = new Pen(Color.Black, 2); Rectangle[] myRectsArray = { new Rectangle(10, 10, 200, 200), new Rectangle(20, 20, 180, 180), new Rectangle(30, 30, 160, 160), new Rectangle(40, 40, 140, 140) }; Метод DrawRectangles вызывается в теле обработчика события Paint, исходный текст которого приведен ниже: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g=e.Graphics; g.Clear(Color.White); g.DrawRectangles(myPen, myRectsArray); } Многоугольник Метод DrawPolygon позволяет нарисовать многоугольник, заданный своими вершинами. Предусмотрено два варианта этого метода: public void DrawPolygon(Pen, Point[]); public void DrawPolygon(Pen, PointF[]); В первом случае методу DrawPolygon через второй параметр передается массив точек класса Point, в котором координаты точек заданы целыми числами, а во втором — массив класса PointF, где координаты соединяемых точек задаются в виде числе с плавающей десятичной точкой. Pen myPen = new Pen(Color.Black, 2); Point[] myPoints = { new Point(10, 10), new Point(100, 40), new Point(50, 240), new Point(150, 24), new Point(100, 100), new Point(160, 40), new Point(220, 210) }; 12 Обработчик события Form1_Paint соединяет эти точки вместе, вызывая метод DrawPolygon: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g=e.Graphics; g.Clear(Color.White); g.DrawPolygon(myPen, myPoints); } Эллипс Метод DrawEllipse рисует эллипс, вписанный в прямоугольную область, расположение и размеры которой передаются ему в качестве параметров. Предусмотрено четыре перегруженных варианта метода DrawEllipse: public void DrawEllipse(Pen, Rectangle); public void DrawEllipse(Pen, RectangleF); public void DrawEllipse(Pen, int, int, int, int); public void DrawEllipse(Pen, float, float, float, float); Эти методы отличаются только способом, при помощи которого описывается расположение и размеры прямоугольной области, в которую вписан эллипс. Вы можете задавать расположение и размеры этой области в виде рассмотренных ранее объектов класса Rectangle, RectangleF, а также в виде целых чисел или числе с плавающей десятичной точкой. Сегмент эллипса При помощи метода DrawArc программа может нарисовать сегмент эллипса. Сегмент задается при помощи координат прямоугольной области, в которую вписан эллипс, а также двух углов, отсчитываемых в направлении против часовой стрелки. Первый угол Angle1 задает расположение одного конца сегмента, а второй Angle2 — расположение другого конца сегмента (рис. 10-13). Предусмотрено четыре перегруженных варианта метода DrawArc: public void DrawArc(Pen, Rectangle, float, float); public void DrawArc(Pen, RectangleF, float, float); public void DrawArc(Pen, int, int, int, int, int, int); public void DrawArc(Pen, float, float, float, float, float, float); Первый параметр метода DrawArc определяет перо, с помощью которой будет нарисован сегмент. Последние два параметра задают углы Angle1 и Angle2 в соответствии с рис. 10-13. Расположение и размеры прямоугольной области передаются методу DrawArc аналогично тому, как это делается для рассмотренного выше метода DrawEllipse. 13 Рисунок 5 -Углы и прямоугольник, задающие сегмент эллипса Например, вся работа по рисованию выполняется внутри обработчика события Form1_Paint: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Pen myPen = new Pen(Color.Black, 2); Graphics g=e.Graphics; g.Clear(Color.White); g.DrawArc(myPen, 10, 10, 200, 150, 30, 270); } Здесь использовали вариант метода DrawArc, допускающий указание расположения и размеров прямоугольной области, а также углов в виде целых чисел. Кривые Безье Сплайн представляет собой кривую линию, соединяющую между собой несколько точек. Кривая Безье, представляющая собой одну из разновидностей сплайна, задается четырьмя точками. Две из них — начальная и конечная, а две другие — управляющие. Кривая Безье проходит через начальную и конечную точки, а управляющие точки задают изгибы кривой линии. Для рисования кривых Безье имеются два перегруженных набора методов DrawBezier и DrawBeziers: public void DrawBezier(Pen, Point, Point, Point, Point); public void DrawBezier(Pen, PointF, PointF, PointF, PointF); public void DrawBezier(Pen, float, float, float, float, float, float, float, float); public void DrawBeziers(Pen, Point[]); public void DrawBeziers(Pen, PointF[]); Во всех этих методах первый параметр задает перо, которая будет использована для рисования. Остальные параметры задают координаты начальной, конечной и управляющих точек. 14 Что касается метода DrawBeziers, то он позволяет задавать координаты точек в виде массивов, что может быть удобно в некоторых случаях. Пример рисования кривой Безье в обработчике события Paint: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Pen myPen = new Pen(Color.Black, 2); PointF startPt = new PointF(40.0F, 80.0F); PointF control1Pt = new PointF(30.0F, 10.0F); PointF control2Pt = new PointF(350.0F, 250.0 F); PointF endPt = new PointF(400.0F, 100.0F); PointF[] myBezierPoints = { startPt, control1Pt, control2Pt, endPt }; Graphics g=e.Graphics; g.Clear(Color.White); g.DrawBeziers(myPen, myBezierPoints); } Здесь создаем начальную и конечную точки startPt и endPt, через которые проходит наша кривая, а также управляющие точки control1Pt и control2Pt. Координаты всех точек передаются методу DrawBeziers через массив myBezierPoints. Управляющие точки изгибают линию, как бы притягивая ее к себе. Канонические сплайны В отличие от только что рассмотренных кривых линий Безье, линии канонического или обычного сплайна (cardinal spline) проходит через все заданные точки. Для рисования обычных сплайнов предусмотрены методы DrawCurve и DrawClosedCurve. Первый из этих методов рисует незамкнутую кривую линию (открытый сплайн), а второй — замкнутую (закрытый сплайн). В простейшем случае методам передается перо и массив соединяемых точек: public void DrawCurve(Pen, Point[]); public void DrawCurve(Pen, PointF[]); public void DrawCurveClosed(Pen, Point[]); public void DrawCurveClosed(Pen, PointF[]); Существуют версии методов, позволяющие дополнительно задать так называемую жесткость (tension) сплайна. Жесткость задается в виде третьего дополнительного параметра: public void DrawCurve(Pen, Point[], float); public void DrawCurve(Pen, PointF[], float); public void DrawClosedCurve(Pen, Point[], float, FillMode); public void DrawClosedCurve(Pen, PointF[], float, FillMode); 15 По умолчанию значение жесткости равно 0,5. При увеличении этого параметра увеличиваются изгибы кривой линии. При жесткости большей 1 или меньшей 0 кривая может превратиться в петлю. Методу DrawClosedCurve дополнительно задается параметр типа FillMode. Если значение этого параметра равно FillMode.Alternate, при рисовании самопересекающихся замкнутых сплайнов будут чередоваться закрашенные и не закрашенные области. Если же значение этого параметра равно FillMode.Winding, большинство замкнутых областей будет закрашено. В приложении DrawClosedCurveApp мы рисуем обычный сплайн, используя точки с теми же координатами, что и в предыдущем примере: Замкнутый сегмент эллипса Для рисования замкнутого сегмента эллипса (pie) используется метод DrawPie. Имеется 4 перегруженных варианта этого метода: public void DrawPie(Pen, Rectangle, float, float); public void DrawPie(Pen, RectangleF, float, float); public void DrawPie(Pen, int, int, int, int, int, int); public void DrawPie(Pen, float, float, float, float, float, float); В качестве первого параметра методу нужно передать перо для рисования. Последние два параметра определяют углы, ограничивающие сегмент эллипса. Эти углы используются таким же образом, как и при рисовании незамкнутого сегмента эллипса методом DrawArc, рассмотренным выше. Остальные параметра задают расположение и размеры прямоугольника, в который вписывается сегмент эллипса. Например, private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Pen myPen = new Pen(Color.Black, 2); int xPos = 10; int yPos = 10; int width = 250; int height = 150; int startAngle = 20; int endAngle = 75; Graphics g=e.Graphics; g.Clear(Color.White); g.DrawPie(myPen, xPos, yPos, width, height, startAngle, endAngle); g.DrawRectangle(new Pen(Color.Black, 1), xPos, yPos, width, height); } 16 Здесь через переменные xPos, yPos, width и height передаются координаты левого верхнего угла и размеры прямоугольной области, в которую вписывается сегмент эллипса. Переменные startAngle и endAngle задают углы, ограничивающие сегмент. Закрашенные фигуры В классе Graphics определен ряд методов, предназначенных для рисования закрашенных фигур. Таблица 1 Методы для рисования закрашенных фигур Есть два отличия методов с префиксом Fill от одноименных методов с префиксом Draw. Прежде всего, методы с префиксом Fill рисуют закрашенные фигуры, а методы с префиксом Draw — не закрашенные. Кроме этого, в качестве первого параметра методам с префиксом Fill передается не перо класса Pen, а кисть класса Brush. Пример использования метода FillEllipse уже был приведен ранее. Вот еще один пример: SolidBrush redBrush = new SolidBrush(Color.Red); g.FillEllipse(redBrush, 50, 50, 100, 110); Здесь вначале создаем кисть красного цвета как объект класса SolidBrush. Эта кисть затем передается методу FillEllipse в качестве первого параметра. Остальные параметры метода FillEllipse задают расположение и размеры прямоугольника, в который будет вписан эллипс. Класс KeyEventArg В классе KeyEventArg предусмотрено свойство KeyCode, при помощи которого можно получить значение из одноименного перечисления KeyCode. В этом перечислении хранятся названия всех клавиш на клавиатуре. Таблица 2. Свойства класса KeyEventArgs Свойство Handled Alt, Control, Shift KeyCode Назначение Позволяет получить или установить значение, которое говорит о том, обрабатывается ли данное событие Можно получить информацию о том, нажата или нет клавиша Alt, Control, Shift Позволяет получить код клавиши при событиях KeyDown и KeyUp 17 САМОСТОЯТЕЛЬНО ВЫПОЛНИТЬ: 1. Один из указанных преподавателем варианта. 2. Создать изображение, указанное преподавателем с помощью графических примитивов. Варианты индивидуальных заданий. 1. Создать приложение, которое выводит форму, в которой выводиться текст Лабораторная работа № 1. Тема лабораторной работы, по нажатию на клавишу мыши, изменяется текст надписи и фон окна. 2. Создать приложение, которое выводит форму, в которой выводиться текст Ваше Фамилия, Имя , Отчество , в качестве заголовка окна по нажатии клавиши мыши выводиться номер группы. По нажатию на клавишу «стрелка вверх» - шрифт надписи увеличивается вдвое, «стрелка вниз» - шрифт надписи уменьшается вдвое. 3. Создать приложение, которое выводит форму, в которой выводиться текст Компьютерная графика. По нажатию на клавишу К фон меняется на красный, З – зеленый, С – синий. Если нажато сочетание клавиш ALT +K, шрифт текста меняется на красный. 4. Создать приложение, которое выводит форму, в которой выводиться текст «Нажмите клавишу мыши, чтобы увидеть текст поздравления». По нажатию на клавишу мыши, выводится поздравительный текст. 5. Создать приложение, которое выводит форму, в которой по углам формы выводится текст «Левый - верхний», «Правый - нижний» и т.д.по щелчку мыши выводиться текст – координаты мыши. 6. Создать приложение, которое выводит форму, в которой выводиться текст Фамилия, Имя, Отчество, номер группы, по нажатию на клавишу мыши текст исчезает, форма окрашивается в темно синий цвет. 18