ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 08.11.2023

Просмотров: 11

Скачиваний: 2

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

7
Application.Run(
new
MyForm()); создает объект
MyForm и выводит форму на экран. После того, как вы ввели код на рис. 1 и сохранили его в файле
Hello.cs
, его нужно скомпилировать. Откройте окно командной строки, перейдите в каталог, где располагается Hello.cs, и введите csc
/target:
winexe hello.cs Параметр
/target указывает компилятору, что нужно создать приложение с графическим пользовательским интерфейсом (а не консольное приложение или, скажем,
DLL
). Полученный исполняемый файл называется
Hello.exe
1.2. Рисование внутри формы GDI+ Создание приложений
Windows Forms с богатыми графическими возможностями требует изучения
Graphics и других классов, предоставляющих управляемому коду доступ к Windows
GDI+. GDI
существовал в
Windows с версии
1.
GDI+
— это расширенная версия
GDI,
доступная в системах, где установлена
Windows XP
или Одно из различий между
GDI
и
GDI+
— поддержка в последней градиентных кистей brush), фундаментальных сплайнов и других графических средств. Но главное — разные модели программирования. Вот- личие от
GDI
, использующей модель с сохранением состояния,
GDI+
обычно состояния не запоминает. В традиционном приложении такие параметры, как шрифт и цвет текста устанавливаются для контекста устройства. В приложении Windows Forms
- нет. Здесь параметры, определяющие характеристики выводимых результатов, передаются при вызове каждого метода Пример этого вы видели в программе из предыдущего раздела, которая для задания шрифта и цвета текста передавала методу
DrawString объекты
Font и
SolidBrush
.
1.2.1. Рисование линий, кривых и фигур
Font и
SolidBrush
— это графические примитивы, используемые для управления форматом изображения, выводимого в окно формы. Но это не единственные примитивы, которые
GDI+
предоставляет в ваше распоряжение. Вот еще Графические примитивы GDI+ Класс Описание Представляет растровые изображения
Font
Определяет атрибуты шрифта размер, гарнитуру и т. д
HatchBrush
Определяет заливку решетчатым узором
LinearGradientBrush
Определяет заливку с линейным градиентом
Реn
Определяет внешний вид линий и кривых
SolidBrush
Определяет заливку сплошным цветом
TextureBrush
Определяет заливку с использованием растрового изображения
Некоторые из этих классов определены в пространстве имен System.Drawing
, другие — в пространстве имен (
NB
)
. Перья, представленные экземплярами класса Реп, управляют внешним видом линий и кривых. Объект
Font задают внешний вид текста, тогда как кисти, представленные объектами классов
HatchBrusb, LinearGradientBrush, SolidBrush и
TextureBrusb определяют виды заливки. Приведенный ниже метод рисует прямоугольники трех стилей
1) без заливки,
2)
закрашенный красным цветом и
3)
с градиентной заливкой, переходящей из красного в синее
protected
override
void
OnPaint (PaintEventArgs
e
)
{
Pen pen =
new
Pen(Color.Black);
// Нарисовать пустой прямоугольник.
e
.Graphics.DrawRectangle(pen, 10, 10, 390, 90);


8
// Нарисовать прямоугольник со сплошной заливкой.
SolidBrush solid =
new
SolidBrush (Color.Red);
e
.Graphics.FilLRectangle (solid, 10, 110, 390, 90);
e.
Graphics.DrawRectangle (pen, 10, 110, 390, 90);
// Нарисовать прямоугольник с градиентной заливкой.
Rectangle rect =
new
Rectangle (10, 210, 390, 90);
LinearGradientBrush gradient =
new
LinearGradientBrush (rect, Color.Red,
Color.Blue, LinearGradientMode.Horizontal);
e
.Graphics.FillRectangle (gradient, rect);
e
.Graphics.DrawRectangle (pen, rect);
} Рис. 3. Прямоугольники различных стилей Результаты показаны на рис. 3. Объект Ре, передаваемый
DrawRectangle
, обводит каждый прямоугольник черной линией толщиной в
1
единицу. (По умолчанию 1 единица — это один пиксел, поэтому все линии в данном примере имеют ширину в 1 пиксел.) Кисти, передаваемые
FillRectangle
, управляют заливкой внутренней части прямоугольников. Заметьте рамки и заливки рисуются по отдельности. Ветеранам
Windows
- программирования это будет особенно интересно, так как функции
Windows для рисования замкнутых фигур рисуют рамку и выводят заливку одновременно, Класс
Graphics содержит разнообразные открытые методы
Draw и Fill для рисования линий, кривых, прямоугольников, эллипсов и других фигур. Неполный список приведен в таблице. Документация
.NET Framework
SDK
содержит превосходный трактат пои снабжена примерами рисования объектов — от простых линий до самых сложных фигур с заливкой. Методы класса для рисования линий, кривых и фигур Метод Описание Рисует дугу
DrawBezier
Рисует кривую Безье
DrawCurve
Рисует фундаментальный сплайн
DrawEllipse
Рисует круг или эллипс
DrawIcon
Рисует значок Рисует картинку
DrawLine
Рисует линию
DrawPie
Рисует сектор
DrawPolygon
Рисует многоугольник
DrawString
Рисует строку текста
Рисует круг или эллипс с заливкой
FillPie
Рисует сектор с заливкой
FillPolygon
Рисует многоугольник с заливкой
FillRectangle
Рисует прямоугольник с заливкой. Освобождение объектов GDI+ Как известно, классы-оболочки неуправляемых ресурсов, таких как описатели файлов и соединения БД, требуют обработки, гарантирующей, что эти ресурсы будут освобождены должным образом Pen, Brush и другие классы
GDI+
, представляющие графические примитивы, также попадают в
эту
категорию, поскольку яапяются оболочками описателей Если не закрывать описатели, это приведет к утечке ресурсов, особенно существенной в приложениях, непрерывно работающих длительное время. Чтобы обезопасить себя от этого, следует вызывать
Dispose для перьев, кистей и других примитивов, явным образом освобождая инкапсулируе- мые ими ресурсы. Освобождать следует даже объекты
Graphics
, которые созданы программой, а не получены из
PaintEventArgs
. Один из способов освобождения объектов
GDI+
— вызвать
Dispose вручную
Pen pen =
new
Pen(Color.Black);
…..
pen.Dispose(); Некоторые С#-программисты предпочитают использовать особую форму using
, которая автоматически генерирует вызовы
Dispose и помещает их в блоки finally
. Одно из преимуществ данного приема в том, что
Dispose вызывается гарантированно, даже в случае исключения
using
(Pen pen =
new
Pen (Color.Black))
{ А некоторые программисты не делают ни того, ни другого, надеясь, что сборщик мусора успеет прибраться, прежде чем
GDI+
исчерпает свои ресурсы, или что приложение не будет работать так долго, чтобы ресурсы
GDI+
упали до критического уровня. Другой прием — создать перья, кисти и другие графические примитивы при старте приложения, чтобы использовать их потом многократно. Это значительно снижает объем потребляемых приложением ресурсов
GDI+
и практически устраняет необходимость вызывать
Dispose для каждого объекта
GDI+
. Производительность также повышается, хоть и незначительно.
1.2.3. Координаты и преобразования При вызове
DrawRectangle ивы определяете координаты, указывающие положение левого верхнего угла прямоугольника, и расстояния, задающие его ширину и высоту. По умолчанию расстояния измеряются в пикселах. Координаты задают расположение в двумерной системе, начало которой располагается в левом верхнем углу формы, а оси хи у
направлены вправо и вниз соответственно. Систему координат и единицы измерения, применяемые по умолчанию, можно настроить, добавив в программу несколько простых операторов. Методам
Graphics передаются глобальные координаты. По путина экран они дважды преобразуются сначала транслируются в страничные координаты, определяющие положение на логической поверхности для рисования, а затем страничные координаты преобразуются в координаты устройства, задающие физическое положение на экране. Для трансляции глобальных координат в страничные
GDI+
использует матрицу преобразования. Матрицы преобразования это математические объекты, позволяющие преобразовывать пары координат х-у из одной системы координат в другую. Они повсеместно используются в компьютерной графике и подробно описаны в соответствующей литературе. Матрицы преобразования, применяемые
GDI+
для пересчета координат, являются экземплярами класса
System.Drawing.Drawing2D.Matrix
. Каждый объект
Graphics содержит объект
Matrix
, доступ к которому осуществляется через свойство
Transform
. По умолчанию используется единичная матрица (
identity matrix
) — этот математический термин обозначает матрицу, не производящую преобразований (также, как умножение числа надает тоже число. Вы можете изменять матрицу преобразования, а значит, и способ пересчета глобальных координат в страничные, одним из двух способов. Первый — инициализировать экземпляр класса
Matrix значениями, задающими желаемое преобразование, и присвоить его свойству
Transform
. Это прекрасный способ
для экспертов по линейной алгебре, но простые смертные, вероятно, предпочтут иное зададут преобразование координат таких методов
Graphics
, как
TranslateTransform и
RotateTransform. TranslateTransform перемещает начало системы координат на указанные расстояния по осям хи у RotateTransform поворачивает оси хи у. Оба метода позволяют поместить начало координат в любую точку и повернуть оси хи у
на любой угол. Непонятно Возможно, поможет пример. Следующий метод
OnPaint рисует прямоугольник шириной 200 ивы- сотой 100 единиц в левом верхнем углу (рис. 4):
protected
override
void
OnPaint (PaintEventArgs
e
)
{
SolidBrush brush =
new
SolidBrush (Color.Red);
e.
Graphics.FillRectangle (brush, 0, 0, 200, 100);
brush.Dispose();
} Рис. 4. Прямоугольник, нарисованный без перемещения и поворота

Следующий вариант метода рисует тот же прямоугольник, но только после вызова TranslateTransofrm
, который перемещает начало координат в точку (
100, 100
). Так как теперь
GDI+
прибавляет
100
к введенным вами значениям хи у, то прямоугольник переместился на
100
единиц вправо и вниз (рис. 5):
protected override void
OnPaint (PaintEventArgs
e
)
{
SolidBrush brush =
new
SolidBrush (Color.Red);
e.
Graphics.TranslateTransform (100.0f, 100.0f);
e.
Graphics.FillRectangle (brush, 0, 0, 200, 100);
brush.Dispose();
} В заключительном примере после перемещения начала координат, оси хи у
поворачиваются напротив часовой стрелки с помощью рис. 6):
protected override void
OnPaint (PaintEventArgs
e
)
{
SolidBrush brush =
new
SolidBrush(Color.Red);
e.
Graphics.TranslateTransform(100.0f, 100.0f);
e
.Graphics.RotateTransform (-45.0f);
e.
Graphics.FillRectangle(brush, 0, 0, 200, 100);
brush.Dispose();
}

11
Puc. 5. Прямоугольник после перемещения начала координат. 6. Прямоугольник после перемещения и поворота системы координат и
RotateTransform
— это мощные инструменты перемещения координатной системы и ориентации ее осей. Родственный метод
ScaleTransform позволяет еще и масштабировать координаты. При использовании преобразований системы координат следует иметь ввиду, что порядок их применения влияет на результаты, В предыдущем примере система координат была сначала перемещена, а затем повернута. Начало отсчета переместилось в точку (
100, 100
), после чего был выполнен поворот напротив часовой стрелки. Если сначала выполнить поворота потом перемещение, прямоугольник появится в другом месте, так как перемещение произойдет вдоль уже повернутых осей. Это можно представить себе так если выстояв комнате, пройдете несколько шагов вперед, а затем повернетесь, то окажетесь не в том же месте, где выбыли бы, если бы сначала повернулись, а
потом
пошли. Тот же принцип применим и к матричным преобразованиям. На рис. 8 представлен исходный текст программы, которая рисует циферблат аналоговых часов в соответствии с текущим временем суток (рис. 7). Рисование выполняется с помощью
FillRectangle (строка и
FillPolygon (строка 73

)
, но настоящую работу выполняют преобразования координат. строка 82
) перемещает начало координат в центр формы, смещая координаты хи у на величины, равные половинам ширины и высоты формы.
RotateTransform (строка поворачивает систему координат перед рисованием часовой и минутной стрелок, а также квадратов, отмечающих деления циферблата.
ScaleTransform (строка масштабирует изображение так, чтобы циферблат занимал всю клиентскую область формы независимо от размера последней. Координаты, передаваемые
FillRectangle и
FillPolygon
, рассчитываются в предположении,
что клиентская область формы имеет размер
200
на
200
единиц.
ScaleTransform задает масштабные множители по осям в соответствии с отношением физических размеров клиентской области к ее логическим размерам. В методе
OnPaint класса
MyForm происходит так много интересного, что немудрено не заметить важный оператор в конструкторе
МуFоrm (строка 12
)
.
SetStyle (ControlStyles.
Re
slze
Re
draw, true); Этот метод настраивает форму так, чтобы вся ее клиентская область объявлялась недействительной (и, следовательно, перериcовывалась) при всяком изменении размеров формы. Это необходимо, если вы хотите, чтобы циферблат часов растягивался и сжимался вместе с окном формы. Если вам непонятно влияние данного оператора на результаты, временно закомментируйте его и перекомпилируйте программу. Затем измените размер формы — вы увидите, что размеры циферблата не изменятся, пока какое-нибудь внешнее воздействие например, минимизация и восстановление окна формы) не приведет к полной перерисовке. Рис. 7. Аналоговые часы с использованием
GDI+
Листинг
9.2.
Clock.cs
1:
using
System;
// Prosiz, гл. 4, стр. 95, Clock
2:
using
System.Drawing;
3:
using
System.Windows.Forms;
4:
5:
namespace
WindAppl_Pros_Chap4_c95_Clock
6: {
7:
class
MyForm
:
Form
8: {
9: MyForm()
// конструктор {
11: Text = Часы
12: SetStyle(
ControlStyles
.ResizeRedraw,
true
);
13: }
14:
15:
protected
override
void
OnPaint(
PaintEventArgs
e)
16: {
17:
SolidBrush
red =
new
SolidBrush
(
Color
.Red);
18:
SolidBrush
white =
new
SolidBrush
(
Color
.White);
19:
SolidBrush
blue =
new
SolidBrush
(
Color
.Blue);
20:

13
21:
// Инициализировать матрицу преобразования
22: InitializeTransform(e.Graphics);
23:
24:
// Нарисовать квадраты, соответствующие позициям часов на циферблате
26:
for
(
int
i = 0; i < 12; i++)
27: {
28: e.Graphics.RotateTransform(30.0f);
29: e.Graphics.FillRectangle(white, 85, -5, 10, 10);
30: }
31:
32:
// Получить текущее время
33:
DateTime
now =
DateTime
.Now;
34:
int
minute = now.Minute;
36:
int
hour = now.Hour % 12;
37:
38:
// Заново инициализировать матрицу преобразования
39: InitializeTransform(e.Graphics);
40:
41:
// Нарисовать часовую стрелку
42: e.Graphics.RotateTransform((hour * 30) + (minute / 2));
43: DrawHand(e.Graphics, blue, 40);
44:
46:
// Заново инициализировать матрицу преобразования
47: InitializeTransform(e.Graphics);
48:
49:
// Нарисовать минутную стрелку
50: e.Graphics.RotateTransform(minute * 6);
51: DrawHand(e.Graphics, red, 80);
52:
53:
// Освободить кисти
54: red.Dispose();
55: white.Dispose();
56: blue.Dispose();
57: }
58:
59:
void
DrawHand(
Graphics
g,
Brush
brush,
int
length)
60: {
61:
// Нарисовать стрелку, указывающую точно вверх,
62:
// предоставив RotateTransform повернуть ее на нужный угол
63:
Point
[ ] points =
new
Point
[4];
64: points[0].X = 0;
65: points[0].Y = -length;
66: points[1].X = -12;
67: points[1].Y = 0;
68: points[2].X = 0;
69: points[2].Y = 12;
70: points[3].X = 12;
71: points[3].Y = 0;

14
72:
73: g.FillPolygon(brush, points);
74: }
75:
76:
void
InitializeTransform(
Graphics
g)
77: {
78:
// Применять преобразования, перемещающие начало координат
79:
// в центр формы и масштабирующие изображение
80:
// в соответствии с ее текущими размерами
81: g.ResetTransform();
82: g.TranslateTransform(ClientSize.Width / 2, ClientSize.Height / 2);
83:
float
scale = System.
Math
.Min(ClientSize.Width, ClientSize.Height) / 200.0f;
84: g.ScaleTransform(scale, scale);
85: }
86:
87:
static
void
Main()
88: {
89:
Application
.Run(
new
MyForm
());
90: }
91: }
92: }
V ' ' ' :::'•• S i l l - . - , - : .- Рис. 8. Исходный текст программы Clock
1.2.4. Единицы измерения Подобно тому, как свойство
Transform объекта
Graphics управляет преобразованием глобальных координат в страничные, свойства
PageUnit и
PageScale играют важную роль в преобразовании страничных координат в координаты устройства.
PageUnit задает систему единиц и может быть установлено в любое значение. определенное перечислением
System.Drawing.GraphicsUnit: Pixel
пикселы,
Inch
— дюймы и т. д PageScale задает масштабирующий коэффициент. Он может быть использован вместо или в сочетании с
ScaleTransform для масштабирования большинства изображений. Некоторые типы изображений, включая шрифты, могут быть масштабированы только с помощью
PageScale
. Значением по умолчанию для
PageUnit является
GraphicsUnit.Display
, те. единица в страничных координатах равна 1 пикселу экрана. Следующий метод
OnPaint устанавливает единицу измерения в дюймы и рисует линейку (рис. 9a) (на рис. б представлена линейка в метрической системе единиц
protected override void
OnPaint (PaintEventArgs
e
)
{
Pen pen =
new
Pen (Color.Black, 0);
SolidBrush yellow =
new
SolidBrush (Color.Yellow);
SolidBrush black =
new
SolidBrush (Color.Black)
;
// Установить единицу измерения в дюймы,
e.Graphics.PageUnit = GraphicsUnit.Inch;
// Добавить по 0.5 ко всем координатам хи у. е (0.5f, 0.5f);
// Нарисовать линейку.
e.Graphics.FillRectangle (yellow, 0, 0, 6, 1);
e.Graphics.DrawRectangle (pen, 0, 0, 6, 1);
// Нарисовать деления.
for (float x=0.25f; x<6.0f; x+=0.25f)
e
.Graphics.DrawLine (
pen
, x, 0.0f, x, 0.08f);

15
for (float x=0.5f; x<6.0f; x+=0.5f)
e
.Graphics.DrawLine (
pen
, x, 0.0f, x, 0.16f);
for (float x=1.0f; x<6,0f; x+=1.0f)
e
.Graphics.DrawLine (
pen
, x, 0.0f, x, 0.25f);
// Нарисовать числовые метки.
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
for
(float x=1.0f; x<6.0f; x+=1.0f)
{
string label = String.Format ("{0}", Convert.ToInt32 (x));
RectangleF rect =
new
RectangleF (x - 0.25f, 0.25f, 0.5f, 0.25f);
e
.Graphics,DrawString (label, Font, black, rect, format);
}
// Освободить объекты GDI+
pen.Dispose ();
yellow.Dispose ();
black.Dispose ();
} Риса. Линейка, нарисованная в дюймовой системе единиц измерения Рис. б. Линейка, нарисованная в метрической системе единиц измерения Такую же линейку можно было бы нарисовать, оставив значение
PageUnit по умолчанию и используя свойства и объекта
Graphics для пересчета дюймов в пикселы вручную. Однако задание координат и расстояний непосредственно в дюймах делает код читабельнее. Заметьте
TranslateTransform смещает координаты хи
у на полдюйма, чтобы вывести изображение линейки из левого верхнего угла формы. Обратите также внимание на
0
. передаваемый конструктору объекта Ре. Значение
0
устанавливает ширину пера в
1
пиксел независимо от текущей системы единиц измерения. Если не указать
0
, ширина пера равнялась бы по умолчанию одной единице. В системе единиц измерения
GraphicsUnitInch единица равна дюйму, а перо шириной в такую единицу было бы очень толстым. Вы также должны иметь ввиду, что значения в дюймах — этологические значения, те. линейка, вероятно, не будет иметь ровно 6 дюймов в длину. Логические значения отличны от физических, так как
Windows не знает, сколько в точности пикселов на дюйм отображается на вашем дисплее. В связи с этим используется предполагаемое разрешение в 96 точек на дюйм.
1.3. Меню Меню — это основа приложений с графическим пользовательским интерфейсом. Практически каждый сидящий перед компьютером знает, что щелкнув пункт расположенного сверху меню, получишь выпадающий список команд. Даже новички легко овладевают этим с одной-двух попыток. Поскольку меню — столь важная часть пользовательского интерфейса,
.NET Framework предоставляет поддержку для использующих их приложений. Классы пространства имен
System.Windows.Forms
, перечисленные в таблице, участвуют в создании меню и работе сними. В следующих разделах мы обсудим эти и другие связанные сменю классы, а также рассмотрим примеры их применения. Классы меню из
System.Windows.Forms Класс
Описание
Menu
Абстрактный базовый класс для других классов меню
MainMenu
Представляет главное меню
СоntехtMеnи
Представляет контекстные меню
MenuItem
Представляет пункты меню. Главные меню Главное меню, или меню верхнего уровня, располагается в виде горизонтальной полосы прямо подзаголовком окна. Следующий код создает главное меню, содержащее два выпадающих меню «File» и «Edit», и связывает его с формой
// Создать объект MainMenu.
MainMenu menu =
new
MainMenu();
// Добавить меню File и его пункты.
Menultem item = menu.Menultems.Add (
"&File"
);
item.Menultems.Add (
"&New"
,
new
EventHandler(OnFileNew));
item.Menultems.Add (
"&0pen..."
,
new
EventHandler(OnFileOpen));
item.Menultems.Add (
"&Save"
,
new
EventHandler(OnFileSave));
item.Menultems.Add (
"Save &As..."
,
new
EventHandler(OnFileSaveAs));
item.Menultems.Add (
"-"
);
// Разделитель (горизонтальная полоса,
item.Menultems.Add (
"E&xit"
,
new
EventHandler (OnFileExit));
// Добавить меню Edit и и его пункты.
item = menu.Menultems.Add (
"&Edit"
);
item.Menultems.Add (
"Cu&t"
,
new
EventHandler(OnEditCut));
item.Menultems.Add (
"&Copy"
,
new
EventHandler(OnEditCopy)};
item.Menultems.Add (
"&Paste"
,
new
EventHandler(OnEditPaste));
// Присоединить меню к форме.
Menu = menu;