Будь умным!


У вас вопросы?
У нас ответы:) SamZan.net

Нажата

Работа добавлена на сайт samzan.net: 2016-03-13

Поможем написать учебную работу

Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.

Предоплата всего

от 25%

Подписываем

договор

Выберите тип работы:

Скидка 25% при заказе до 8.5.2024

PAGE  8

1-я вершина фигуры

2-я вершина фигуры

3-я вершина фигуры

Текущее положение 4-й вершины фигуры

Предыдущее положение 4-й вершины фигуры

ет

Да

- Логический блок

- Операционный блок

Условие перехода

Перечисление  выполняемых операций

Завершение работы

функции

(Выход)

Укурс = Укурс + 5;

Укурс = Укурс - 5;

Хкурс = Хкурс - 5;

Хкурс = Хкурс + 5;

Да

Да

Да

Да

Нет

Нет

Нет

Нет

Нажата ""?

Нажата ""?

Нажата ""?

Нажата ""?

Нажата клавиша на клавиатуре

(Начало)

Министерство общего и профессионального образования

Российской Федерации

Тверской государственный технический университет

Кафедра электронных вычислительных машин

Программирование в среде Microsoft Visual Studio.

Графические возможности интегрированной среды Visual Studio и основные приёмы разработки программ под Windows.

 

Методические указания к лабораторным работам

по курсу "Технология программирования"

для студентов 2-го курса специальности ВМКСС

Лабораторная работа № 2

Тверь 2011

Цель лабораторной работы заключается в изучении графических возможностей языка С# при формировании изображений геометрических фигур в  интегрированной среде разработки программных приложений Microsoft Visual Studio.

Основными задачами, решаемыми в процессе выполнения лабораторной работы, являются:

  •  Ознакомление с возможностями, предоставляемыми контекстом графического устройства и его составными частями для работы с графикой.
  •  Изучение основных особенностей языка и интегрированной среды при формировании линий, замкнутых фигур и областей.
  •  Приобретение практических навыков работы с графикой в процессе разработки простейших программных приложении в среде Microsoft Visual Studio.

 

Методическое указание обсуждено на заседании кафедры ЭВМ (протокол № 4 от 22 февраля 2000 года) и рекомендовано к печати.

Составитель: проф. кафедры ЭВМ ТГТУ, д.т.н., Веселов А.А.

Содержание:

№ п/п

Раздел

Стр.

1

Теоретическая часть

4

1.1

Контекст графического устройства

4

1.1.1

Основные принципы рисования. GDI и GDI+.

4

1.1.2

Контексты устройств и графический объект

5

1.1.3

Пример рисования форм.

6

1.2

Обзор пространств имен GDI+

7

1.3

Обзор пространства имен System.Drawing

8

1.3.1

Служебные типы System.Drawing

9

1.2.2

Тип Point (PointF)

9

1.2.3

Тип Rectangle (RectangleF)

10

1.2.4

Класс Region

10

1.4

Класс Graphics

11

1.5

Сеансы Paint

12

1.6

Объявление клиентской области формы недействительной

13

1.7

Получение объекта Graphics вне обработчика события Paint

14

1.8

Удаление объекта Graphics

15

1.9

Блок-схемы алгоритмов функционирования

16

2

Формирование контура фигур

17

2.1

Возможные способы формирования линий

17

2.1.1

Первый способ (по-точкам)

17

2.1.2

Второй способ ("резинка")

17

3

Разработка приложения

18

3.1

Конструирование главной формы

18

3.2

Реализация первого способа рисования

22

3.3

Реализация второго способа

25

4

Задание на лабораторную работу

27

5

Содержание отчета по лабораторной работе

27

1. Теоретическая часть.

В предыдущей лабораторной работе вы ознакомились с процессом построения настольных приложений с графическим интерфейсом пользователя на основе System.Windows.Forms. В процессе выполнения этой лабораторной работы мы изучим уже детали визуализации простых графических объектов на поверхности Form. Для этого мы начнем с описания контекста графического устройства и общего обзора многочисленных пространств имен, связанных с рисованием, рассмотрим роль события Paint и могущественного объекта Graphics.

1.1. Контекст графического устройства

Часто для создаваемых приложений пользователю необходимо создавать свой собственный интерфейс. Например, ему нужно нарисовать текст заданным шрифтом и в строго определенном месте, или необходимо вывести на экран нужное изображение или просто нарисовать простые графические формы и другую графику.

Обычно программные тексты для рисования на экране довольно просты и основаны на использовании технологии, известной под названием GDI+. Эта технология состоит из множества .NET классов, предназначенных для осуществления процесса рисования на экране. Эти классы обеспечивают множество операций, напрямую взаимодействующих с драйверами устройств, гарантирующих корректный вывод в нужное место на экран монитора или устройство печати. Точно также как и .NET классы, GDI+ классы располагают интуитивно понятными и простыми в использовании объектными моделями. Несмотря на сложность GDI+ объектной модели, пользователю все же требуется хорошее понимание происходящих внутри них процессов.

1.1.1. Основные принципы рисования. GDI и GDI+.

В действительности, преимущество Windows как современной операционной системы (ОС) заключается в способности разработчика абстрагироваться от деталей и особенностей конкретных устройств. Например, не требуется понимание того, что происходит в драйвере вашего диска при чтении или записи информации в файл. Достаточно просто вызвать соответствующие методы соответствующего .NET класса или эквивалентные им Windows API функции. Этот принцип справедлив и при рисовании. Когда компьютер что-то рисует на экране, то он делает это путем посылки инструкций в видеокарту, сообщая ей что нужно нарисовать и где. Проблема состоит только в том, что существуют сотни различных типов видеокарт, многие из которых созданы разными производителями и имеют разные системы команд и функциональных возможностей.  Учитывая это, практически становиться невозможным написать одну программу для всех видеодрайверов, которые что-то рисовали бы на экране. Именно по этой причине самые ранние версии Windows неизбежно включали в свой состав Интерфейс Графических Устройств (Windows Graphical Device Interface (GDI)).

Когда, при выполнении определенной задачи, происходит вызов Windows API функции, то GDI скрывает различие между разными видеокартами. Это позволяет пользователю одинаковым образом взаимодействовать с видеокартами разных типов. Но GDI также делает еще и нечто другое. Дело в том, что современные компьютеры располагают более одного устройства, на которые можно выводить графическую информацию. Естественно, что это монитор (дисплей) к которому осуществляется доступ через видеокарту и принтер. Некоторые компьютеры имеют несколько установленных видеокарт или могут иметь несколько принтеров. GDI делает для вашего приложения принтер таким же устройством, как и экран дисплея. Если вместо вывода на экран нужно что то распечатать, то достаточно просто информировать систему о том, что требуется вывести информацию не на экран, а на принтер и вызвать соответствующую API функцию. Таким образом, GDI абстрагируется от характеристик электронного устройства, предоставляя их на более высоком уровне API.

Однако, несмотря на это, Windows API, разработанная на языке Си не позволяет использовать эти достаточно простыми способами, как этого хотелось бы. Для устранения этого недостатка был введен механизм GDI+, который можно рассматривать как некоторую прослойку между GDI и приложением и, тем самым, обеспечивая наличие более интуитивно понятной объектно-ориентированной модели. Несмотря на то, что GDI+ представляется в виде "обертки" вокруг GDI, но фирме Microsoft это позволило обеспечить новые характеристики и возможности.

1.1.2. Контексты устройств и графический объект

В GDI способ, с помощью которого определяется тип устройства для вывода информации, основан на использовании объекта, известного как контекст устройства (device context (DC)). Контекст устройства хранит информацию о конкретном устройстве и способен пееревести вызовы GDI API функций в инструкции, отсылаемые на это устройство. Можно запросить у контекста устройства информацию о том, какими возможностяхми обладает соответствующее устройство (например, возможно цветное распечатывание или только черно-белое) и установить нужные характеристики. Если запросить усстройство сделать что-либо, чего оно не может выполнить, то контекст устройства обнаружит это и, в зависимости от ситуации, предпримет соответствуюшие действия, выбрасывая исключение или изменяя сам запрос на наиболее ближайшее по смыслу действие.

Однако, контекст устройства связан не только с аппаратными средствами. Он работает еще и как мост с ОС Windows и, следовательно, способен учитывать любые требования или ограничения, которые случаютсяя при вырисовывании графики. Например, если Windows знает, что требуется обновить только часть окна приложения, то контекст устройства способен перехватить и исключит попытки рисовать вне этой области. Благодаря взаимодействию с Windows, работа с контекстом устройства поможет упростить ваши программы и в других ситуациях. Например, техническим устройствам нужно сообщить: где рисовать и, обычно, это требует указания координат относительно левого-верхнего угла экрана (или другого устройства вывода). Однако, как правило, ваше приложение вырисовывает что-либо в определенном месте внутри своей клиентной области. (Кстати, клиентная область Windows представляет собой только часть окна, используемую для рисования, и в большинстве случаев она имеет белый цвет фона). Но, поскольку это окно может располагаться, в общем случае, в любом месте экрана, то его произвольное перемещениие представляет собой довольно трудную задачу, поскольку связано с преобразованием старых координат в новые. Однако, контекст устройства всегда знает – где находится ваше окно и способен выполнить такое преобразование автоматически. Это означает, что можно в любое время запросить  у контекста устройства положение рисуемого объекта внутри вашего окна не беспокоясь о том, где в настоящий момент находится на экране окно вашего приложения.

Можно легко заметить, что контекст устройства представляет собой очень мощное средство, и не удивительно, что при использовании GDI все рисование должно осуществляться с помощью контекста устройства. Иногда контекст устройства используют даже для выполнения таких операций, которые не связаны с рисованием на экране или другом устройстве вывода. Например, если в вашем распоряженнии имеется битовая память в которой необходимо выполнить какие-то преобразования (масштабирование, вращение), то с помощью контекста устройства это можно сделать гораздо эффективнее потому, что контекст устройства полностью использует все преимущества конкретного устройства вывода на вашем компьютере и чтобы выполнить нужные операции максимально быстро.

В GDI+ продолжается использование контекста устройства, но теперь оно приобретает более дружественное название. Оно заимствовано из базовых .NET классов и известно как Graphics. Можно легко увидеть, что основная доля рисования осуществляется с помощью объекта Graphics. На самом деле, поскольку класс System.Drawing.Graphics - это класс, предназначенный для управления большинством операций, связанных с рисованием. Понимание того, как манипулировать этим объектом представляется ключевым моментом в понимании характерных особенностей рисования с помощью GDI+.

1.1.3. Пример рисования форм

Начнем с небольшого примера, который иллюстрирует процесс рисования в главном окне приложения. Пример создан в среде Visual Studio .NET на языке C# как C# Windows приложение. В этом типе приложения  автоматически создается экземпляр класса Form1, порожденный от класса System.Windows.Form, представляющий главное окно приложения. Поэтому весь дополнительный программный текст будет добавляться в описание именно этого класса.

В технологии .NET обычно говорят о приложениях, которые отображают разные элементы управления, и представляют собой произвольно размещаемую окно, представленное в виде прямоугольного объекта, занимающего некоторую область экрана. В этом контексте термин окна для рисования является наиболее уместным.

В данном примере просто создадим форму (окно) рисование в которой осуществляется в методе InitializeComponent(). Это не совсем удачный объект для рисования и в этом можно быстро убедиться, но он ввполне подходит для первоначального знакомства.

Для начала модифицируем текст программы в методе InitializeComponent() следующим образом:

private void InitializeComponent()

{

this.components = new System.ComponentModel.Container();

this.Size = new System.Drawing.Size(300,300);

this.Text = "Display At Startup"

 this.BackColor = Color.White;

}

И добавим следующий текст в конструктор формы:

public Form1()

{

    InitializeComponent();

    Graphics dc = this.CreateGraphics();

    this.Show();

    Pen BluePen = new Pen(Color.Blue, 3);

    dc.DrawRectangle(BluePen, 0,0,50,50);

    Pen RedPen = new Pen(Color.Red, 2);

    dc.DrawEllipse(RedPen, 0, 50, 80, 60);

}

Разберем написанное. Вначале мы установили цвет фона – в белый, чтобы соответствующее окно выглядело бы как обычное окно, на котором мы собираемся выводить графику. Мы поместили эту строку в метод InitializeComponent() для того, чтобы Visual Studio .NET установил цвет сразу же после его появления. Такого же эффекта можно бы добиться, если бы в режиме конструирования окна в окне свойств установить белый цвет фона. Метод же InitializeComponent() специально используется Visual Studio .NET для настройки внешнего вида формы. Если явно не установить белого цвета фона формы, то он останется равным значению, принятому по умолчанию и равным цвету диалоговых окон (этот цвет определяется в настройках Windows).

Следующее, что сделано в нашем примере, это создание объекта Graphics с помощью метода формы CreateGraphics(). Этот объект Graphics содержит контекст устройства OC Windows, необходимый для рисования. Созданный контекст устройства связан с дисплейным устройством и с нашей формой. Для его обозначения мы используем имя переменной с названием dc, отражая тот факт, что она в действительности отображает контекст графического устройства.

После этого мы вызываем метод Show() для того, чтобы отобразить окно на экране. Вообще говоря, это необходимо только для того, чтобы ускорить его отображение, поскольку невозможно никакого рисования до тех пор, пока окно не визуализировано (т.е. нет того, на чем надо рисовать).

И, накнец, мы рисуем прямоугольник с координатами (0, 0) и размерами 50 х 50 и эллипс с координатами (0, 50) и размерами 80 х 50. Координаты (0, 0) означают Х пикселей вправо и Y пикселей вниз от левого-верхнего угла клиентной области окна формы. Запись (х, у) является стандартной формой математического представления положения и очень подходит для нашего сслучая. Следует отметить, что мы используем методы DrawRectangle() и DrawEllipse() принимающие 5 аргументов. Первый параметр каждого из них – это экземпляр класса System.Drawing.Pen. Перо является одним из основных инструментальных средств рисования и содержит информацию о том, как будут вырисовываться линии на экране.  В первом случае, параметры пера говорят, что линии должны быть синими с толщиной в три пикселя.  Следующие четыре аргумента – это координаты. Для прямоугольника они представляют (х, у) координаты левого-верхнего угла прямоугольника и его ширину и высоту (все координаты выражаются в пикселях). Для эллипса эти числа означают то же самое кроме того, что рассматривается гепотетический прямоугольник, в котором размещается эллипс.

1.2. Обзор пространств имен GDI+

Платформа .NET предоставляет ряд пространств имен, посвященных визуализации двухмерной графики. В дополнение к базовой функциональности вы найдете также набор инструментов графики (цвета, шрифты, перья, кисти и т.п.), а также типы, выполняющие геометрические трансформации, сглаживание, смешивание палитр и поддержку печати документов. Все вместе эти пространства имен образуют средство .NET под названием GDI+, которое представляет собой управляемую альтернативу программному интерфейсу Win32 Graphical Device Interface (GDI). В табл.1 приведен общий обзор основных пространств имен GDI+.

Таблица 3.1. Основные пространства имен GDI+

Пространство имен

Назначение

System.Drawing

Это центральное пространство имен GDI+, определяющее многочисленные типы для базовой визуализации (шрифты, перья, базовые кисти и т.п.), а также мощный тип Graphics.

System.Drawing.Drawing2D

Это пространство имен представляет типы, используемые  для более развитой функциональности двухмерной векторной графики (например, градиентные кисти, концы перьев, геометрические трансформации и т.п.).

System.Drawing.Imaging

Это пространство имен определяет типы, позволяющие манипулировать графическими изображениями (изменять палитру, извлекать метаданные изображения, манипулировать мета файлами и т.п.).

System.Drawing.Printing

Это пространство имен определяет типы, позволяющие визуализировать изображения для печатных страниц, взаимодействовать с принтером, форматировать общее представление конкретного задания печати.

System.Drawing.Text

Это пространство имен позволяет манипулировать коллекциями шрифтов.

Замечание! Все пространства имен GDI+ определены в сборке System.Drawing.dll. Хотя многие типы проектов Visual Studio автоматически устанавливают ссылки на эту библиотеку кода, при необходимости можно манипулировать ссылкой на System.Drawing.dll, используя диалоговое окно Add References (Добавление ссылок).

1.3. Обзор пространства имен System.Drawing

Подавляющее большинство типов, которые вы будете использовать при программировании приложений GDI+, находятся внутри пространства имен System.Drawing. Как и можно было ожидать, здесь есть классы, представляющие изображения, кисти, перья и шрифты. Более того, System.Drawing определяет множество связанных служебных типов, таких как Color.Point и Rectangle. В табл.2 перечислены некоторые основные типы.

Таблица 2. Основные типы пространства имен System.Drawing

Тип

Назначение

Bitmap

Тип инкапсулирует данные изображения (*.bmp и т.п.).

Brush

Brushes

SolidBrush

SystemBrushes

TextureBrush

Объекты кистей используются для заполнения внутренних поверхностей графических фигур, таких как прямоугольники, эллипсы и многоугольники.

BufferedGraphics

Этот тип представляет собой графический буфер для двойной буферизации, который используется для сокращения мерцания, вызванного перерисовкой поверхности отображения.

Color

SystemColors

Типы Color и SystemColors определяют множество статических,

доступных только для чтения свойств, используемых для получения определенных цветов для конструирования различных перьев и кистей.

Font

FontFamily

Тип Font инкапсулирует характеристики шрифта (название, жирность, курсив, размер и т.п.).

FontFamily предоставляет абстракцию для группы шрифтов, имеющих сходный дизайн, но различия в стиле.

Graphics

Этот центральный класс представляет действительную поверхность рисования, а также множество методов для визуализации текста, изображений и геометрических шаблонов.

Icon

SystemIcons

Эти классы представляют специальные пиктограммы, а также набор стандартных системных пиктограмм.

Image

ImageAnimator

Image — абстрактный базовый класс, предоставляющий функциональность для типов Bitmap, Icon и Cursor. Класс ImageAnimator предлагает способ итерации по множеству производных от Image типов в течение заданного интервала времени.

Pen

Pens

SystemPens

Перья — это объекты, используемые для рисования прямых и кривых линий. Тип Pen определяет ряд статических свойств, возвращающих Pen заданного цвета.

Point

PointF

Эти структуры представляют координаты (x, y), отображающиеся на целые числа или числа с плавающей точкой, соответственно.

Rectangle

RectangleF

Эти структуры представляют прямоугольные области (отображающиеся на целые числа или числа с плавающей точкой).

Size

SizeF

Эти структуры представляют ширину/высоту (отображающиеся на целые числа или числа с плавающей точкой). Каждый из этих типов определяет свойства Height и Width, а также несколько перегруженных операций.

StringFormat

Этот тип используется для инкапсуляции различных средств текстовой компоновки (например, выравнивание, межстрочный интервал и т.п.).

Region

Этот тип описывает внутреннюю часть геометрического образа, состоящего из прямоугольников и путей.

1.3.1. Служебные типы System.Drawing

Многие из методов рисования, определенных в объекте System.Drawing.Graphics, требуют спецификации позиции или области, в которой вы хотите визуализировать данный элемент. Например, метод DrawString() требует указания места для визуализации текстовой строки на поверхности объекта типа-наследника Control. Учитывая, что DrawString() многократно перегружен, этот позиционный параметр может быть специфицирован с использованием координат (x, y) или границ “рамки”, в пределах которой нужно рисовать. Другие методы типа GDI+ могут потребовать указания ширины и высоты заданного элемента либо внутренних границ геометрического изображения.

Чтобы специфицировать такую информацию, в пространстве имен System.Drawing определены типы Point, Rectangle, Region и Size. Очевидно, что Point представляет точку с координатами (x, y). Тип Rectangle охватывает пару точек, представляющих верхний левый и нижний правый границы прямоугольной области. Тип Size подобен Rectangle, но эта структура представляет размеры, заданные длиной и шириной.

И, наконец, Region обеспечивает способ представления и квалификации непрямоугольных поверхностей.

Переменные-члены, используемые типами Point, Rectangle и Size, внутренне представлены целочисленным типом данных. Если необходим более высокий уровень точности, можете использовать соответствующие типы PointF, RectangleF и SizeF, которые (как несложно догадаться), используют координаты с плавающей точкой. Но независимо от внутреннего представления данных, каждый тип имеет идентичный набор членов, включая ряд перегруженных операций.

1.3.2. Тип Point (PointF)

Первый служебный тип, о котором вам следует знать — это System.Drawing.Point (PointF). В отличие от иллюстративных типов Point, которые создавались в начальных главах, тип GDI+ Point (PointF) поддерживает ряд полезных членов, включая:

  •  +, -, ==, != — тип Point перегружает различные операции C#;
  •  X, Y — эти члены предоставляют доступ к лежащим в основе Point значениям (x, y);
  •  IsEmpty — этот член возвращает true, если x и y равны 0.

Для иллюстрации работы со служебными типами GDI+ рассмотрим пример консольного приложения (по имени UtilTupes), которое использует тип System.Drawing.Point (не забудьте ссылку на System.Drawing.dll):

using System;

using System.Drawing;

namespace UtilTypes

{

public class Program

{

static void Main(string[] args)

{

// Создание и смещение точки.

Point pt = new Point(100, 72);

Console.WriteLine(pt);

pt.Offset(20, 20);

Console.WriteLine(pt);

// Перегруженные операции Point.

Point pt2 = pt;

if(pt == pt2)

WriteLine("Points are the same");

else

WriteLine("Different points"); 

// Изменить в pt2 значение X.

pt2.X = 4000;

// Теперь показать каждое значение X.

Console.WriteLine("First point: {0} ", pt);

Console.WriteLine("Second point: {0} ", pt2);

Console.ReadLine();

}

}

}

1.3.3. Тип Rectangle (RectangleF)

Тип Rectangle, как и Point, полезен во многих приложениях (как на основе GDI, так и нет). Один из наиболее полезных методов типа Rectangle — это Contains(). Этот метод позволяет определять, находится ли данный экземпляр Point или Rectangle в пределах текущих границ другого объекта. Далее в этой главе вы увидите применение этого метода для выполнения проверки попадания на изображения GDI+. А пока рассмотрим простой пример:

static void Main(string[] args)

{

...

// Point изначально вне границ прямоугольника.

Rectangle r1 = new Rectangle(0, 0, 100, 100);

Point pt3 = new Point(101, 101);

if(r1.Contains(pt3))

Console.WriteLine("Point is within the rect!");

else

Console.WriteLine("Point is not within the rect!");

// Теперь поместим точку внутрь прямоугольника.

pt3.X = 50;

pt3.Y = 30;

if(r1.Contains(pt3))

Console.WriteLine("Point is within the rect!"); // внутри

else

Console.WriteLine("Point is not within the rect!"); // не внутри

Console.ReadLine();

}

1.3.4. Класс Region

Тип Region представляет внутреннюю часть геометрической фигуры. Исходя из этого определения, имеет смысл, что конструкторы класса Region требуют передачи им некоторого существующего геометрического шаблона. Например, предположим, что создан прямоугольник размером 100х100 пикселей. Если вы хотите получить доступ к внутренней области этого прямоугольника, то можете написать нечто вроде такого:

// Получить внутреннюю часть прямоугольника.

Rectangle r = new Rectangle(0, 0, 100, 100);

Region rgn = new Region(r);

Имея внутренние размеры заданной фигуры, можно манипулировать ею, используя различные члены этого класса, включая перечисленные ниже.

  •  Complement(). Обновляет Region частью специфицированного графического объекта, который не пересекается с данным Region.
  •  Exclude(). Обновляет этот Region во внутренней части, которая не пересекается с указанным графическим объектом.
  •  GetBounds(). Возвращает Rectangle(F), представляющий прямоугольную область, охватывающую данный Region.
  •  Intersect(). Обновляет этот Region пересечением его самого с указанным объектом графики.
  •  Transform(). Трансформирует Region указанным объектом Matrix.
  •  Union(). Обновляет этот Region объединением его самого с указанным объектом графики.
  •  Translate(). Смещает координаты указанного Region на заданную величину.

Перечисленый состав функций делает понятной основную идею, положенную в основу этих координатных примитивов. Но, если нужны дополнительные детали, следует обратиться к документации по .NET Framework 2.0 SDK.

1.4. Класс Graphics

Класс System.Drawing.Graphics — это ворота в функциональность визуализации GDI+. Этот класс не только представляет поверхность рисования (такую, как поверхность формы, поверхность элемента управления или область в памяти), но также определяет десятки членов, которые позволяют визуализировать текст, изображения (пиктограммы, битовые карты и т.п.), и многочисленные геометрические шаблоны. В табл.3 приведен неполный список членов этого класса.

Таблица 3. Члены класса Graphics

Метод

Назначение

FromHdc()

FromHwnd()

FromImage()

Эти статические методы предоставляют возможность получения корректного объекта Graphics от заданного изображения (пиктограммы, битовой карты и т.п.) или виджета GUI.

Clear()

Этот метод заполняет объект Graphics указанным цветом, стирая в процессе текущую поверхность рисования.

DrawArc()

DrawBeziers()

DrawCurve()

DrawEllipse()

DrawIcon()

DrawLine()

DrawLines()

DrawPie()

DrawPath()

DrawRectangle()

DrawRectangles()

DrawString()

Эти методы используются для визуализации заданного образа или геометрического шаблона. Все методы DrawXXX() требуют использования объектов GDI+ Pen.

FillEllipse()

FillPie()

FillPolygon()

FillRectangle()

FillPath()

Эти методы используются для заполнения внутренности заданной геометрической фигуры. Все методы FillXXX() требуют применения объектов GDI+ Brush.

Кроме представленного множества методов для визуализации, класс Graphics определяет дополнительные члены, позволяющие конфигурировать “состояние” объекта Graphics. Присваивая значения свойствам, перечисленным в табл. 4, можно изменять текущие операции визуализации.

Замечание. Что касается ASP.NET, то System.Drawing предоставляет тип BufferedGraphics, который позволяет визуализировать графику с использованием системы двойной буферизации

для минимизации или исключения мерцания, которое может возникнуть во время операций визуализации. Подробности можно найти в документации по .NET Framework 2.0 SDK.

Таблица 3.4. Свойства состояния класса Graphics

Свойства

Назначение

Clip

ClipBounds

VisibleClipBounds

IsClipEmpty

IsVisibleClipEmpty

Эти свойства позволяют устанавливать опции отсечения, используемые с текущим объектом Graphics.

Transform

Это свойство позволяет трансформировать “мировые координаты” (подробнее об этом речь пойдет ниже).

PageUnit

PageScale

DpiX

DpiY

Эти свойства позволяют конфигурировать точку начала координат операций визуализации, а также единицу измерения.

SmoothingMode

PixelOffsetMode

TextRenderingHint

Эти свойства позволяют конфигурировать гладкость геометрических объектов и текста.

CompositingMode

CompositingQuality

Свойство CompositingMode определяет, будет ли рисование перекрывать фон или смешиваться с ним.

InterpolationMode

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

Вопреки предположениям, класс Graphics невозможно создать через ключевое слово new, поскольку в нем нет общедоступно объявленных конструкторов. Как же тогда получить действительный объект Graphics? Давайте посмотрим.

1.5. Сеансы Paint

Наиболее распространенный способ получения объекта Graphics состоит в обработке события Paint. Вспомните из предыдущей главы, что класс Control определяет виртуальный метод по имени OnPaint(). Когда вы хотите, чтобы Form визуализировал графические данные на своей поверхности, можете переопределить этот метод и извлечь объект Graphics из входного параметра PaintEventArgs. Для иллюстрации создайте новое приложение Windows Forms по имени BasicPaintForm и обновите класс-наследник Form следующим образом:

public partial class MainForm : Form

{

public MainForm()

{

InitializeComponent();

CenterToScreen();

this.Text = "Basic Paint Form";

}

protected override void OnPaint(PaintEventArgs e)

{

// Переопределяя OnPaint(), не забудьте вызвать реализацию базового класса.

base.OnPaint(e);

// Получить объект Graphics из входящего PaintEventArgs.

Graphics g = e.Graphics;

// Визуализировать текстовое сообщение в заданном шрифте и цвете.

g.DrawString("Hello GDI+", new Font("Times New Roman", 20), Brushes.Green, 0, 0);

}

}

Хотя переопределение OnPaint() допустимо, более распространенный способ обработки события Paint предусматривает использование ассоциированного делегата PaintEventHandler (фактически это поведение по умолчанию, принятое в Visual Studio при обработке событий через окно Properties). Этот делегат может указывать на любой метод, принимающий в первом параметре System.Object, а во втором — PaintEventArgs. Если имеется обработанное событие Paint (через дизайнер Visual Studio, или вручную в коде), то можно извлечь объект Graphics из входящего параметра PaintEventArgs. Вот необходимое изменение:

public partial class MainForm : Form

{

public MainForm()

{

InitializeComponent();

CenterToScreen();

this.Text = "Basic Paint Form";

// Visual Studio помещает этот код внутрь InitializeComponent().

this.Paint += new PaintEventHandler(MainForm_Paint);

}

private void MainForm_Paint(object sender, PaintEventArgs e)

{

Graphics g = e.Graphics;

g.DrawString("Hello GDI+", new Font("Times New Roman", 20),

Brushes.Green, 0, 0);

}

}

Независимо от того, как вы реагируете на событие Paint, имейте в виду, что всякий раз, когда окно становится “грязным”, инициируется событие Paint. И как вам должно быть известно, окно считается “грязным” всякий раз, когда изменяется его размер, оно перекрывается другим окном (полностью или частично) и потом открывается, либо сворачивается и затем восстанавливается. Во всех этих случаях .NET гарантирует, что если форма нуждается в перерисовке, обработчик события Paint (или переопределенный метод OnPaint()) вызывается автоматически.

1.6. Объявление клиентской области формы недействительной

Во время работы приложения Windows Forms может понадобиться явно инициировать событие Paint в коде, вместо того, чтобы ожидать, пока окно станет “естественно грязным” в результате действий конечного пользователя. Например, можно построить программу, которая позволяет пользователю выбирать из множества предопределенных образов, используя специальное диалоговое окно. После закрытия диалогового окна нужно нарисовать выбранное изображение в клиентской области формы. Очевидно, если вы станете ждать, пока окно станет “естественно грязным”, пользователь не увидит изменений до тех пор, пока не изменит размер окна или не перекроет его другим окном. Чтобы программно вынудить окно перерисовать себя, просто вызовите унаследованный метод Invalidate():

public partial class MainForm: Form

{

...

private void MainForm_Paint(object sender, PaintEventArgs e)

{

Graphics g = e.Graphics;

// Здесь визуализировать корректное изображение.

}

private void GetImageFromDialog()

{

// Показать диалоговое окно и получить новое изображение.

// Перерисовать всю клиентскую область.

Invalidate();

}

}

Метод Invalidate() перегружен несколько раз, чтобы позволить специфицировать определенную прямоугольную область для перерисовки вместо перерисовки всей клиентской области окна (что принято по умолчанию). Если вы хотите только обновить самый верхний левый прямоугольник клиентской области, можете написать следующее:

// Перерисовать заданную прямоугольную область Form.

private void UpdateUpperArea()

{

Rectangle myRect = new Rectangle(0, 0, 75, 150);

Invalidate(myRect);

}

1.7. Получение объекта Graphics вне обработчика события Paint

В некоторых редких случаях может понадобиться доступ к объекту Graphics за пределами контекста обработчика события Paint. Например, предположим, что вы хотите нарисовать маленький кружок в позиции (x, y), где был выполнен щелчок кнопкой мыши. Один подход к получению действительного объекта Graphics из контекста обработчика события MouseDown состоит в вызове статического метода Graphics.FromHwnd(). Из опыта работы с Win32 вам должно быть известно, что HWND — это структура данных, представляющая некоторое окно Win32. На платформе .NET унаследованное свойство Handle извлекает лежащий в основе HWND, который может применяться в качестве параметра для Graphics.FromHwnd():

private void MainForm_MouseDown(object sender, MouseEventArgs e)

{

// Получить объект Graphics через Hwnd.

Graphics g = Graphics.FromHwnd(this.Handle);

// Теперь нарисовать кружок 10*10 в месте щелчка кнопкой мыши.

g.FillEllipse(Brushes.Firebrick, e.X, e.Y, 10, 10);

// Освободить все объекты Graphics, созданные непосредственно.

g.Dispose();

}

Хотя эта логика визуализирует кружок вне обработчика события OnPaint(), очень  важно понимать, что когда форма делается недействительной (и потому должна быть перерисована), каждый из кружков будет стерт! Это имеет смысл, учитывая, что визуализация случается только в контексте события MouseDown. Намного лучший подход состоит в том, чтобы позволить обработчику MouseDown создать новый экземпляр Point, который затем добавляется к внутренней коллекции (вроде List<T>), с последующим вызовом Invalidate(). В этот момент обработчик событий Paint может просто выполнить итерацию по коллекции и нарисовать каждый Point:

public partial class MainForm : Form

{

// Используется для запоминания всех точек.

private List<Point> myPts = new List<Point>();

public MainForm()

{

...

this.MouseDown += new MouseEventHandler(MainForm_MouseDown);

}

private void MainForm_MouseDown(object sender, MouseEventArgs e)

{

// Добавить в коллекцию точек.

myPts.Add(new Point(e.X, e.Y));

Invalidate();

}

private void MainForm_Paint(object sender, PaintEventArgs e)

{

Graphics g = e.Graphics;

g.DrawString("Hello GDI+", new Font("Times New Roman", 20),

new SolidBrush(Color.Black), 0, 0);

foreach(Point p in myPts)

g.FillEllipse(Brushes.Firebrick, p.X, p.Y, 10, 10);

}

}

При таком подходе визуализированные кружки навсегда останутся на поверхности окна, поскольку вся графическая визуализация происходит в событии Paint. На рис.1 показан результат тестового запуска этого начального приложения GDI+.

Рис.1. Простое приложение рисования

1.8. Удаление объекта Graphics

В содержимом последних нескольких страниц можно было заметить, что в некоторых из приведенных примеров непосредственно вызывался метод Dispose() объекта Graphics, в то время как в других это не делалось. Учитывая, что тип Graphics манипулирует различными неуправляемыми ресурсами, имело бы смысл, чтобы он освобождал ресурсы через вызов Dispose() как можно скорее (вместо того, чтобы дожидаться, пока это сделает сборщик мусора в процессе финализации). То же самое можно сказать о любом типе, поддерживающем интерфейс IDisposable. При работе с объектами GDI+ Graphics следует помнить о следующих правилах:

  •  если вы напрямую создали объект Graphics, вызывайте Dispose(), когда надобность в нем отпадет;
  •  если вы ссылаетесь на существующий объект Graphics, не вызывайте Dispose().

Чтобы прояснить мысль, рассмотрим следующий обработчик события Paint:

private void MainForm_Paint(object sender, PaintEventArgs e)

{

// Загрузить локальный файл *.jpg.

Image myImageFile = Image.FromFile("landscape.jpg");

// Создать объект Graphics на основе изображения.

Graphics imgGraphics = Graphics.FromImage(myImageFile);

// Визуализировать новые данные в изображение.

imgGraphics.FillEllipse(Brushes.DarkOrange, 50, 50, 150, 150);

// Нарисовать изображение в Form.

Graphics g = e.Graphics;

g.DrawImage(myImageFile, new PointF(0.0F, 0.0F));

// Освободить созданный нами объект Graphics.

imgGraphics.Dispose();

}

Пусть пока вас не волнует, что некоторая логика GDI+ выглядит несколько загадочно. Однако следует заметить, что вы получили объект Graphics из файла *.jpg, загруженного из локального каталога (статическим методом Graphics.FromImage()). Поскольку вы явно создали этот объект Graphics, практический опыт требует вызова метода Dispose() на этом объекте по завершении его использования, чтобы освободить внутренние ресурсы для использования другими частями системы.

Обратите внимание, что вы не вызываете Dispose() на объекте Graphics, который получили из входящего PaintEventArgs. Это объясняется тем фактом, что вы не создавали этот объект напрямую, и не можете быть уверены, что он не понадобится другим частям программы. Ясно, что освобождение объекта, используемого потом где-то еще, создало бы проблему!

Кстати, если вы забудете вызвать Dispose() на объекте, реализующем IDisposable, внутренние ресурсы будут в конечном итоге освобождены, когда позднее до них доберется сборщик мусора. В этом свете ручное освобождение объекта imgGraphics технически не обязательно. Хотя явное освобождение объектов GDI+, созданных напрямую — разумная мера, для простоты в примерах кода это делаться с каждым типом GDI+ не будет.

1.9. Блок-схемы алгоритмов функционирования.

Прежде чем приступать к разработке программы необходимо представить и формально описать ее алгоритм функционирования. Понятно, что на начальном этапе достаточно трудно представить себе алгоритм функционирования будущего приложения во всех деталях и с учетом особенностей его реализации.  Скорее всего, этот алгоритм будет описывать поведение разрабатываемого приложения или функционального блока только на самом общем уровне. Однако, по мере продвижения процесса разработки он будет постепенно детализироваться с учетом особенностей его реализации. Таким образом, на начальном этапе разработки приложения его алгоритм функционирования представляет собой описание основных (главных) особенностей поведения. Ошибки в разработке алгоритма функционирования на этом этапе ведут к большим и непроизводительным затратам труда программиста на последующих этапах разработки приложений. А часто и вообще к его повторной разработке с "чистого" листа. Именно поэтому программисты уделяют особое внимание алгоритмам функционирования разрабатываемых программных приложений.

Обычно алгоритма изображаются в виде соотвествующих блок-схем, которые в самом простом варианте представляют собой набор условных графических изображений соединенных между собой логических и операционных блоков (Рис.2).

Рис.2. Основные элементы блок-схем алгоритмов.

Использование этих основных элементов позволяет составлять достаточно понятное представление о функциональных возможностях и особенностей поведения разрабатываемых приложений или их составных частей. На рис.3 представлен пример фрагмента алгоритма функционального блока, описывающего процесс управления курсором при его перемещении по экрану монитора.

Рис.3. Блок-схема алгоритма управления курсором

2. Формирование контура фигур

2.1. Возможные способы формирования линий.

При формировании линий в окне приложения обычно применяют один из двух способов:

2.1.1. Первый способ заключается в том, что пользователь (с помощью курсора) указывает место, где должна находиться очередная вершина многоугольника, и, нажимая на клавишу (например, на левую клавишу мыши), сообщает приложению, что фиксируется очередная вершина многоугольника. После чего вырисовывается линия, соединяющая предпоследнюю и последнюю вершины многоугольника. Процесс продолжается то тех пор, пока пользователь не сообщит программе о том, что он завершает формирование фигуры. Для этой цели можно воспользоваться резервированием и последующим использованием специальной клавиши (например, правой клавиши мыши) или использовать некоторый особый признак, интерпретируемый как окончание процесса формирования фигуры.

Таким образом, достаточно, чтобы приложение соответствующим образом реагировало бы только на одно событие, поступающее от манипулятора типа "мышь" при нажатии (OnMouseDown), например, на ее левую клавишу. Условно этот способ можно назвать "поточечным".

2.1.2. Второй способ заключается в том, чтобы формировать новые вершины таким образом, чтобы можно было непосредственно наблюдать, как будет выглядеть изображение последней грани контура многоугольной фигуры, если его последняя вершина находилась бы на том месте, где в настоящий момент находится курсор. Такой подход предполагает, формирование линии, изображающей формируемую грань многоугольника, с координатами в точках, где была зафиксирована его последняя вершина и текущего положения курсора. При изменении положения курсора изменяет своё положение и соответствующая грань многоугольника. В этом случае сама линия, изображающая эту последнюю грань, выглядит как резиновая, надёжно закреплённая на одном конце и свободно перемещаясь вслед за курсором на её втором конце. Такой режим рисования получил название "Резинка".

Рис.3. Рисование контура фигуры в режиме "Резинка".

В данном случае приложение должно быть способно реагировать уже на два события, поступающее от манипулятора типа "мышь" и возникающее при нажатии на ее левую клавишу (MouseDown) и при ее перемещении (MouseMove).

Вполне очевидно, что для реализации обоих способов вырисовывания фигур в объявление класса формы, требуется ввести некоторый объект, предназначенный для временного хранения координат уже зафиксированных вершин многоугольника. В качестве такого хранилища можно использовать обычный список. Для этого в классе формы достаточно объявить переменную типа List. Кроме этого нам, очевидно, потребуется вспомогательная переменная, хранящая последнее положение курсора и координаты последней вершины формируемого многоугольника.

3. Разработка приложения.

3.1. Конструирование главной формы

Для того, чтобы создать приложение, с помощью которого можно рисовать на экране монитора контуры многоугольных фигур, прежде всего, необходимо создать главную форму этого приложения. С этой целью нужно запустить интегрированную среду Visual Studio и с помощью выбора пунктов меню: Файл-Создать-Проект создать Windows приложение так, как это делалось в лабораторной работе №1 (рис.4).

Рис.4. Внешний вид окна для создания проекта приложения.

При этом следует не забыть указать имя решения, проекта и папки, в которой будут храниться результаты работы над Вашим проектом. В результате этих действий, интегрированная среда примет вид, представленный на рис.5.

Рис.5. Внешний вид главного окна приложения в среде Visual Studio.

Теперь, если выбрать страницу с названием Form1.cs, то программисту открывается окно с программным кодом для главной формы приложения (рис.6). Такой же результат можно получить путем нажатия на правую клавишу "мыши", находящейся в клиентной области окна конструктора формы Form1.сs[Конструктор].

Рис.6. Внешний вид окна для редактирования программного кода.

Как уже говорилось, для построения приложения для создания контурного изображения многоугольных фигур в поточечном режиме рисования и в режиме "резинка, необходимо внести в класс главной формы переменную типа списка и переменную для хранения предыдущего положения курсора. На рис. 6 они представлены соответственно переменными PointList и oldCursor (строка 15 и 16). И еще нам понадобится одна переменная, для обозначения режима рисования – drMode, тип которой описан в строке 12. Строки 22, 23 и  24 необходимы для инициализации объявленных переменных.

Для полноценного приложения в главном его окне необходимо создать меню и панель управления. Для этого перетащим на форму из панели компонентов - компоненту Меню (MenuStrip). Кликните мышкой по компоненте Меню на форме и с помощью редактора создайте пункт "Файл" и внутри него подпункт "Выход"(рис.7, слева). Добавьте еще один пункт меню, назовите его "Режим", а внутри него создайте три подпункта с названиями "Не рисовать", "По-точкам" и "Резинка" (рис.7, в центре).

Рис.7. Формирование меню главной формы приложения

В результате внешний вид главного окна приложения примет вид, представленный на рис.7. После чего необходимо создать обработчики событий, возникающих при нажатии на эти клавиши. Делается это путем двойного клика на соответствующем пункте меню. В результате в окне редактирования программного текста автоматически появляются пустые тела этих функций. Наполним их следующим содержимым:

//Функция-обработчик пункта меню "Файл" - "Выход"

private void выходToolStripMenuItem_Click(object sender, EventArgs e)

{

Close();//Закрытие главного окна приложения

}

//Функция-обработчик пункта меню "Режим рисования" - "Не рисовать"

private void неРисоватьToolStripMenuItem_Click(object sender, EventArgs e)

{

drMode = DrawMode.None; //Установка режима – "Не рисовать"

}

//Функция-обработчик пункта меню "Режим рисования" - "По-точкам"

private void поточкамToolStripMenuItem_Click(object sender, EventArgs e)

{

drMode = DrawMode.ByPoint; ; //Установка режима – "Рисовать по-точкам"

}

//Функция-обработчик пункта меню "Режим рисования" - "Резинка"

private void резинкойToolStripMenuItem_Click(object sender, EventArgs e)

{

drMode = DrawMode.Ribbon; //Установка режима – "Рисовать резинкой"

}

В дополнение к сделанным изменениям во внешнем виде приложения, необходимо создать еще и панель управления с клавишами управления, дублирующих соответствующие пункты меню. Для этого необходимо поместить на форму компоненту инструментальной панели управления ToolStrip и выровнять ее по верхней границе клиентной области формы. Затем в ее состав нужно ввести несколько клавиш с названиями: "Выход", "Не рисовать", "Рисовать по-точкам" и "Рисовать резинкой". В результате этих манипуляций главное окно приложения примет вид, представленный на рис.8.

Рис.8. Внешний вид приложения с меню и панелью управления

Теперь для того, чтобы эта панель стала по-настоящему рабочим инструментом, необходимо создать функции-обработчики события нажатия на размещенные на ней управляющие клавиши. Для этого, необходимо выбрать курсором нужную клавишу (например, с надписью "Выход) и на странице событий в окне инспектора свойств выбрать нужное событие. В нашем случае это будет событие Click. Справа от имени события расположен раскрывающийся список функций, которые уже используются приложением. Если раскрыть список, то можно увидеть все его содержимое (рис.9).

Рис.9. Выбор функции отклика на событие Click

 В нашем случае список будет содержать перечень функций-обработчиков событий, связанных с выбором пунктов меню. Среди этих функций можно увидеть функцию "неРисоватьToolStripMenuItem_Click", реагирующую на событие меню "Файл" "Выход". Поскольку нажатие на выбранную клавишу, для которой мы создаем функцию-обработчик доле быть таким же как и в результате выбора этого пункта меню, то и выбираем именно эту функцию. Таким образом, одна и та же функция будет вызываться в ответ на два события: на выбор пункта меню и на нажатие на клавишу, соответствующую этому пункту меню.  

3.2. Реализация первого способа рисования.

Для реализации первого (поточечного) способа формирования фигур необходимо предусмотреть соответствующие реакции формы на события, связанные с изменением состояния мыши (в частности, нажатие на левую клавишу). Для этого, с помощью курсора мыши активизируем форму Form1 и обратимся к окну Инспектора Свойств Объектов. Выберем в нём страницу для настройки событий (Events) и способом, описанным выше, активизируем создание пустого шаблона функции отклика на событие MouseDown (рис.10).  

Рис.10. Выбор события в окне инспектора свойств формы.

Поскольку нам требуется, чтобы приложение реагировало на нажатие на клавиши "мыши", то выбираем событие MouseDown и путем двойного клика на пустом поле справа от данного события, создаем пока "пустую" функцию, которая будет вызываться каждый раз при наступлении выбранного. В результате этих действий, в окне программного текста данной формы "Form1.cs" автоматически появляется прототип функции-обработчика выбранного события под названием FormMouseDown с пустым телом:

void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,  TshiftState

                                                                     Shift, int X, int Y)

{

}

В соответствии с описанием рассматриваемого способа формирования фигуры многоугольника, приведенного в разделе 2.1.1, в тело этой функции необходимо вставить программный текст, необходимый для выполнения следующих действий:

  •  После выбора места очередной вершины многоугольника и нажатии на клавишу (например, на левую) мыши необходимо выполнить следующую последовательность действий:
  •  Если количество вершин более одной, то нарисовать грань многоугольника в виде линии, соединяющей последнюю вершину с точкой размещения курсора мыши.
  •  создать новую переменную для сохранения новой вершины с координатами курсора мыши и добавить её в список вершин ListPoints.
  •  При необходимости завершить процесс формирования фигуры, необходимо:
  •  отрисовать последнюю грань многоугольника путём рисования линии, соединяющей её последнюю и начальную вершины.
  •  Удалить все элементы из списка, в которых хранятся вершины многоугольника, тем самым подготовив его для формирования следующей фигуры.

Один из возможных вариантов реализации функции FormMouseDown() представлен в Листинге 1.

 Листинг 1.

private void Form1_MouseDown(object sender, MouseEventArgs e)

{

Graphics g = CreateGraphics();   //Создаем объект Graphics

Pen pen=new Pen(Color.Black, 1); //Создаем объект "пера"

Point pnt; //Объявляем локальную переменную для хранения координат вершины

if (e.Button == MouseButtons.Left)//Если нажата левая клавиша мыши, то…

{

if (PointsList.Count > 0) //Проверяем: если в списке есть хотя бы одна вершина

{

//то соединяем последнюю вершину с координатами курсора

g.DrawLine(pen, PointsList[PointsList.Count - 1], e.Location);

}

pnt = new Point(e.X, e.Y);// Создаем объект вершины

PointsList.Add(pnt);         //Добавляем ее в список вершин

}

else  //Если же нажата правая клавиша мыши, то…

{

if (PointsList.Count > 1) //Если есть хотя бы одна вершина, то…

{

//соединяем первую вершину с последней

g.DrawLine(pen, PointsList[0], PointsList[PointsList.Count - 1]);

}

PointsList.Clear();

}

oldCursor.X = e.X;// Запоминаем горизонтальную и

oldCursor.Y = e.Y;// вертикальную координаты курсора

pen.Dispose();       // Освобождаем ресурсы пера

g.Dispose();          //Освобождаем ресурсы объекта Graphics

}

Запустив приложение можно убедиться в работоспособности приведенного текста программы. Однако, рассмотренный вариант реализации функции MouseDown(…) не совсем удачная в том смысле, что, если учесть необходимость того, что эту же функцию придется использовать и для рисования контура фигуры в режиме "резинка", то новая реализация потребует изменения уже отлаженного участка программы, работающей в поточечном режиме. Поэтому для более эффективной реализации функции MouseDown(…), является введение в использование переключателя Switch, в котором учитывается режим вырисовывания. Для этого необходимо в класс главной формы ввести еще одну переменную типа перечисления для хранения режима редактирования. Соответствующая реализация этой функции приведена в листинге 2.

Листинг 2.

public partial class Form1 : Form

{

List<Point> PointsList; //Список вершин фигуры

Point oldCursor;            //Старое положение курсора

DrawMode drMode;      //Режим рисования фигур

public Form1()

{

InitializeComponent();

PointsList = new List<Point>();

oldCursor = new Point(0,0);

drMode = DrawMode.None;

}

private void Form1_MouseDown(object sender, MouseEventArgs e)

{

Graphics g = CreateGraphics();    //Создаем объект Graphics

Pen pen=new Pen(Color.Black, 1);//Создаем объект "пера"

//Объявляем локальную переменную для хранения координат вершины

Point pnt;          

switch (drMode)

{

case DrawMode.ByPoint:

//Если нажата левая клавиша мыши

if (e.Button == MouseButtons.Left

{

if (PointsList.Count > 0) //Если есть хотя бы одна вершина

{

//Соединяем последнюю вершину с координатами курсора

g.DrawLine(pen, PointsList[PointsList.Count - 1], e.Location);

}

pnt = new Point(e.X, e.Y);//Создаем объект вершины

PointsList.Add(pnt);         //Добавляем ее в список вершин

}

else //Если нажата правая клавиша мыши

{

if (PointsList.Count > 1) //Если есть хотя бы одна вершина

{

//Соединяем первую вершину с последней

g.DrawLine(pen, PointsList[0], PointsList[PointsList.Count - 1]);

}

// Очистка списка вершин с целью его подготовки к работе

PointsList.Clear();//со следующей фигурой

}

break;

//case DrawMode.Ribbon: break;

}

oldCursor.X = e.X;//Запоминаем горизонтальную

oldCursor.Y = e.Y;//и вертикальную координаты курсора

pen.Dispose();    //Освобождаем ресурсы пера

g.Dispose();      //Освобождаем ресурсы объекта Graphics

}

}

Такой способ организации реализации функции Form1_MouseDown с использованием оператора переключателя позволяет в последующем заставить эту же функцию формы, в зависимости от используемого режима, по разному реагировать на событие нажатия на клавишу "мыши". Причем делать это никак не затрагивая участков программного текста, реализующих реакцию этой же формы на это же событие, но в других режимах ее функционирования. Введение новой реакции на событие достигается простым введением в тело переключателя Switch нового раздела case:, например, case DrawMode.Ribbon: как это показано в закомментированной строке в нижней части тела оператора Switch Листинга 2.

3.3. Реализация второго способа.

Реализация второго способа несколько сложнее, чем первого, но зато более эффективная. Для этого необходимо несколько видоизменить функцию Form1_MouseDown и задействовать ещё одну функцию отклика формы на внешние события. А именно: на событие, связанное с перемещением "мыши" - Form1_MouseMove. С учётом сказанного и в соответствии с особенностями второго способа (см. раздел 2.1.2),  необходимо разработать соответствующий программный текст его реализации, реагирующий на возникновение следующих событий:

При нажатии на клавишу мыши (например, на левую) необходимо выполнить следующие действия:

  •  При фиксации места для очередной вершины многоугольника необходимо создавать объект, сохраняющий её координаты, и добавлять его в список вершин.
  •  Если же фиксируется признак окончания формирования фигуры (например, нажатие на правую клавишу мыши), то необходимо сделать следующее:
  •  Дорисовать последнюю её грань в виде линии, соединяющей последнюю и первую вершины фигуры. 
  •  Уничтожить все элементы списка, в которых хранятся координаты вершин многоугольника, и очистить сам список, тем самым подготовив его для формирования следующей фигуры.

В Листинге 3 представлена одна из возможных и наиболее простых реализаций соответствующей функции-обработчика этого события.

Листинг 3. Реализация функции Form1_MouseDown

private void Form1_MouseDown(object sender, MouseEventArgs e)

{

Graphics g = CreateGraphics();    //Создаем объект Graphics

Pen pen=new Pen(Color.Black, 1);//Создаем объект "пера"

//Объявляем локальную переменную для хранения координат вершины

Point pnt;          

switch (drMode)

{

case DrawMode.ByPoint://здеь располагается программный текст реагирования формы

// на событие нажатия на клавишу "мыши" в режиме "По-точкам"

// (См. листинг 2)

break;

case DrawMode.Ribbon:

if (e.Button == MouseButtons.Left)//Если нажата левая клавиша мыши

{

          // Создаем объект вершины с координатами курсора

pnt = new Point(e.X, e.Y);

PointsList.Add(pnt);   // Добавляем его в список вершин

}

else  //Если нажата правая клавиша мыши

{

if (PointsList.Count > 0) //Если есть хотя бы одна вершина

{

Refresh();// Перерисовка формы с рисованием незамкнутой фигуры

//Соединяем первую вершину с последней

g.DrawLine(pen, PointsList[0], PointsList[PointsList.Count - 1])

// Очистка списка вершин с целью его подготовки к работе

PointsList.Clear();//со следующей фигурой

}

}

Break. Эта переменн

}

oldCursor.X = e.X;//Запоминаем горизонтальную

oldCursor.Y = e.Y;//и вертикальную координаты курсора

pen.Dispose();       //Освобождаем ресурсы пера

g.Dispose();          //Освобождаем ресурсы объекта Graphics

}

 Примечание. Анализ процесса вырисовывания показывает, что когда завершается формирование контура фигуры, то нужно не только соединить линией первую и последнюю ее вершину, но и стереть линию "резинки", соединяющую последнюю вершину и курсор. А для этого нужно вызвать другую функцию, которая стирала бы всю клиентную область экрана и нарисовала замкнутый контур фигуры заново. Следует заметить, что у любой формы есть функция, которая стирает и перерисовывает область, которую она занимает. Она нужна для того, чтобы оперативно восстановить внешний вид формы после того, как его закроет другое окно, после его перемещения или после сворачивания в пиктограмму и т.д. Эта функция называется Paint. Непосредственное к ней обращение не рекомендуется. Но для того, чтобы все-таки к ней обратиться  предусмотрено несколько вспомогательных функций: Invalidate(), Refresh() и Update().

В нашем случае используется функция Refresh(). Однако для того, чтобы это сработало, необходимо определить: что же должна делать (в нашем случае – рисовать) эта функция. Один из возможных вариантов ее программного текста представлен в листинге 5.

Листинг 5.

private void Form1_Paint(object sender, PaintEventArgs e)

{

Pen pen = new Pen(Color.Black, 1); //Создаем объект "пера"

Point[] points = new Point[PointsList.Count];// Объявляем и создаем массив вершин

points=PointsList.ToArray(); //Инициализируем массив элементами списка

if (PointsList.Count > 1)  //Если в списке больше одной вершины, то…

{

e.Graphics.DrawLines(pen, points);//Рисуем незамкнутый контур фигуры

}

}

При перемещении курсора мыши необходимо выполнить следующие операции:

  •  Если список вершин - пустой, то ничего не рисовать.
  •  Если же в списке есть вершины, то вначале нужно стереть старую линию, соединяющую последнюю  вершину формируемой грани фигуры (неподвижная точка) с точкой  предыдущего местоположения курсора мыши.
  •  Затем нужно нарисовать новую линию, начинающуюся в точке расположения уже зафиксированной последней вершины многоугольника и заканчивающуюся в точке нахождения в данной момент курсора.

Одна из возможных реализаций рассматриваемого способа представлена в Листинге 4.

Листинг 4. Реализация функции FormMouseMove

private void Form1_MouseMove(object sender, MouseEventArgs e)

{

Graphics g = CreateGraphics();//Создаем объект Graphics

Pen pen = new Pen(Color.Black, 1);

switch (drMode)

{

case DrawMode.Ribbon:

if (PointsList.Count < 1) break;

Refresh();//Рисовать незамкнутый контур фигуры

//Нарисовать линию от последней вершины до курсора

g.DrawLine(pen, PointsList[PointsList.Count - 1], e.Location);

                                        break;

default:                                     break;

}

oldCursor.X = e.X;//Запоминаем горизонтальную

oldCursor.Y = e.Y;//и вертикальную координаты курсора

pen.Dispose();    //Освобождаем ресурсы пера

g.Dispose();      //Освобождаем ресурсы объекта Graphics

}

 Примечание. Рисование непосредственно на форме – это не самый удачный способ формирования графических изображений в ее клиентной области. Альтернативным и наиболее часто применяемым способом является способ, использующий компоненту под название PictureBox. Эту компоненту располагают в клиентной части формы и соответствующих реакций формы на внешние события используют аналогичные реакции компоненты PictureBox.

4. Задание на лабораторную работу

4.1. Разработать алгоритм функционирования приложения для формирования изображения фигур, реализующий поточечный режим рисования и рисований "резинкой". Поскольку приложение должно реагировать на два события: на нажатие клавиши "мыши" и ее перемещения, то алгоритм функционирования следует разбить на две части. Первая часть – это реакция приложения на нажатие "мыши", а вторая часть - его реакция на перемещение "мыши".

4.2. Разработать графический интерфейс приложения (конструирование формы, подбор соответствующих элементов управления и генерируемых ими событий, на которые приложение должно реагировать).

4.3. Разработать программу, реализующую поточечный режим рисования контуров многоугольных фигур.

4.4. Разработать программу, комбинирующую поточечный режим рисования и режим "резинки".

4.5. Модифицировать созданную программу, но с использованием в качестве поверхности рисования не поверхность главной формы приложения, а поверхность компоненты PictureBox.

5. Содержание отчета по лабораторной работе.

Отчет должен содержать:

  •  Титульный лист.
  •  Задание на лабораторную работу.
  •  Алгоритмы работы разработанного приложения с подробных их описанием (1 часть -реакция на нажатие на клавишу "мыши" и 2 часть - реакция на перемещение "мыши").
  •  Описание интерфейса приложения, его состава и процесса формирования функций обработчиков для интерфейсных элементов.
  •  Описание особенностей используемых программных решений, которые были использованы при реализации приложения и реализующих разные способы формирования контуров многоугольников.
  •  Исходные тексты программных модулей приложений с комментариями.
  •  Краткая инструкция пользователю приложения.
  •  Внешний вид главного окна созданного приложения.
  •  Выводы по работе и рекомендации по совершенствованию приложения.




1. Реферат- Психическая энергия и здоровье человека
2. реферату Аудитори які мають серт
3. Заработная плата
4. Семантически производные существительные в романе
5. Риэлтерские организации- бухгалтерский учет и налогообложение
6. Тема 1.6 Вимірювання складу газів води та пари Загальні відомості про газоаналізатори Прилади пр
7. Тема- Классификация товаров аптечного ассортимента Задание1
8. Политика и право
9. I. Сахарный диабет у лиц с ожирением у лиц с нормальной массой тела II
10. .11.10 14.11.10 14
11. на тему ПРОБЛЕМЫ ЗАКОННОСТИ В РОССИЙСКОЙ ФЕДЕРАЦИИ Автор работы-
12. . Однако проблема воспитания детей в материнских семьях не ограничивается лишь материальными трудностями.
13. СладКо Маркетинговая стратегия является составной частью общей стратегии предприятия
14. 1 Зарождение биржевой торговли реальными товарами возникновение понятия биржи 1
15. лекция проф. Ю.В.Шубика
16. Контрольная работа- Экономика России и Германии в преддверии Первой мировой войны
17. Бизнесгрупп осуществляется в соответствии с нормативными документами имеющими разный статус.
18. Интернет понимается глобальная компьютерная сеть или Сеть сетей
19. реферат дисертації на здобуття наукового ступеня кандидата педагогічних наук Київ 2000
20. Тема- ОСНОВЫ ХИРУРГИИ НАРУШЕНИЙ РЕГИОНАРНОГО КРОВОТОКА