Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms...

70
Государственный университет Высшая школа экономики Факультет Бизнес-Информатики Кафедра Основ информатики и прикладного программного обеспечения C# Объектно-ориентированный язык программирования Пособие к практическим занятиям - 9 Проф. Забудский Е.И. Москва 2006

Transcript of Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms...

Page 1: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

Государственный университет – Высшая школа экономики

Факультет Бизнес-Информатики

Кафедра Основ информатики и прикладного программного обеспечения

C# Объектно-ориентированный язык программирования

Пособие к практическим занятиям - №9

Проф. Забудский Е.И.

Москва 2006

Page 2: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

Тема 9. Объектно-ориентированный подход к разработке программного обеспечения.

System.Windows.Forms Visual Studio .NET Графический Интерфейс Пользователя –

Graphical User Interface (GUI). Два практических занятия

(4 часов)

1. Разработка проекта NetCalc (Калькулятор) с использованием встроенного дизайнера форм – средство автоматизации разработки Visual Studio .NET (листинг 9.9 NetCalc.cs).

2. Разработка вручную восьми приложений Windows Forms в формате GUI (листинги 9.1…9.8)

За компонентно-ориентированным программированием – будущее (см. листинги 4.5 - 4.10 – Материалы к Практич. занятию № 4)

// КОММЕНТАРИЙ. На сайте http://www.firststeps.ru/ “Первые шаги” представлено много интерес-ных обучающих материалов по различным интeгрированным средам и языкам программирова-ния, в том числе представлены C# и платформа .NET (step by step).

Данное пособие распространяется свободно. Изложенный материал, предназна-ченный для публикации в “твердом” варианте, дополнен и откорректирован.

© Забудский Е.И., 2006

Page 3: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

3

Содержание1. Программная модель Windows Forms …………………………………………………………… 4

1.1. Ваша первая Windows-форма. Листинг 9.1. Hello.cs, Puc. 2а,б …………………………….. 5 1.2. Рисование внутри формы: GDI+ ………………………………………………………………….. 7

1.2.1. Рисование линий, кривых и фигур. Таблица Графические примитивы GDI+, Puc.3 ……. 7 Таблица Методы класса Graphics для рисования линий, кривых и фигур ………………… 8

1.2.2. Освобождение объектов GDI+ …………………………………………………………………….. 9 1.2.3. Координаты и преобразования. Листинг 9.2. Clock.cs, Puc. 4…Puc. 7 …………………….. 9 1.2.4. Единицы измерения, Puc. 9 ………………………………………………………………………... 14

1.3. Меню. Таблица Классы меню из System.Windows.Forms …………………………………… 16 1.3.1. Главные меню ………………………………………………………………………………………... 16 1.3.2. Обработка команд меню …………………………………………………………………………… 17 1.3.3. Контекстные меню …………………………………………………………………………………… 17 1.3.4. Состояния пунктов меню …………………………………………………………………………… 18 1.3.5. «Быстрые» клавиши ………………………………………………………………………………… 19

1.4. Приложение ImageView. Листинг 9.3. lmageView.cs, рис. 10а,б,в …………………………….. 19 1.5. Мышь и клавиатура. Таблица Виртуальные методы формы для обработки сигналов

от мыши и с клавиатуры ……………………………………………………………………………. 24

1.5.1. Обработка ввода с клавиатуры …………………………………………………………………… 25 1.5.2. Обработка сигналов от мыши. Листинг 9.4. MouseTracker.cs, рис. 11 ………………………. 25 1.5.3. Приложение NetDraw. Листинг 9.5. NetDraw.cs, рис. 13 ………………………………………... 28

1.6. Другие события формы. Таблица Виртуальные методы, соответствующие событиям формы. Листинг 9.6. CloseDemo.cs, рис. 15 ……………………………………………………… 31

1.7. Элементы управления. Таблица Классы элементов управления из System.Windows.Forms ……………………………………………………………………………... 33

1.7.1. Приложение ControlDemo. Листинг 9.7. ControlDemo.cs, рис. 17 ………………………….. 35 1.8. Привязки. Таблица Стили привязки, рис. 19, рис. 20 ………………………………………… 38 1.9. Диалоговые окна. Таблица Классы стандартных диалогов, определенные в

System.Windows.Forms …………………………………………………………………………….. 40

1.9.1. Приложение DialogDemo. Листинг 9.8. DialogDemo.cs, рис. 21а,б ………………………… 40 2. Windows Forms и Visual Studio .NET, рис. 23 …………………………………………… 48

Шаг 1: Создание проекта NetCalc, рис. 24 ……………………………………………………... 49

Шаг 2: Разработка формы, рис. 25, рис. 26 …………………………………………………… 49

Шаг 3: Добавление полей ………………………………………………………………………….. 51 Шаг 4: Изменение конструктора класса формы ………………………………………………... 52 Шаг 5: Добавление вспомогательных методов ………………………………………………… 52 Шаг 6: Добавление обработчиков событий Click ………………………………………………. 52 Шаг 7: Добавление обработчиков для клавиатуры ……………………………………………. 52 Шаг 8: Переопределение ProcessDialogKey ……………………………………………………. 53 Шаг 9: Компоновка и запуск приложения ………………………………………………………... 53 Листинг 9.9 NetCalc.cs. Исходный текст NetCalc ……………………………………………... 54 Заключение …………………………………………………………………………………………… 70 Литература ……………………………………………………………………………………………. 70

Page 4: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

4

Windows Forms и Visual Studio Microsoft .NET Framework — платформа, ориентированная в основном на разработку Web-приложений и Web-сервисов, но она поддерживает и иные программные модели. Программная модель Windows Forms —применяется для разработки приложений .NET Framework с графическим интерфейсом пользователя.

Внешне приложения Windows Forms похожи на обычные Windows-приложения. В них есть окна и часто исполь-зуются такие элементы приложений с графическим интерфейсом пользователя, как меню, элементы управле-ния и диалоговые окна. По сути же они являются управляемыми приложениями. Они содержат общий проме-жуточный язык (CIL) и метаданные, используют библиотеку классов .NET Framework (FCL) и выполняются в общеязыковой исполняющей среде (CLR).

Главное достоинство применения модели Windows Forms в разработке Windows-приложений в том, что эта мо-дель стандартизирует пользовательский интерфейс и при этом лишена массы ошибок и нестыковок, которыми грешит Windows API. Так, каждый опытный Windows-программист знает, что некоторые оконные стили можно применить только при создании окна. Windows Forms устраняет эту проблему. Если вы применяете такой стиль к существующему окну, инфраструктура незаметно уничтожит это окно и воссоздаст его с указанным стилем. Кроме того, FCL гораздо богаче Windows API, и, когда вы пишете приложение Windows Forms, в вашем распо-ряжении вся мощь FCL. Как правило, приложения Windows Forms компактнее соответствующих Windows-приложений. Например, приложение, применяющее стандартный Windows API требует сотен строк кода (или библиотеку стороннего производителя) для извлечения изображения из JPEG-файла. В приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

1. Программная модель Windows Forms В Windows Forms термин форма — синоним окна. Главное окно приложения является формой. Если у прило-жения есть другие окна верхнего уровня, они также являются формами. Диалоговые окна — тоже формы. Не-смотря на свое название, приложения Windows Forms не выглядят как просто формы. Они, как и традиционные Windows-приложения, осуществляют полный контроль над всем происходящим в их окнах. Приложения Windows Forms в большой степени зависят от классов FCL из пространства имен System.Windows.Forms, вклю-чающего такие классы: 1) как Form, который моделирует поведение окон или «форм»; 2) Menu, который по-зволяет работать с меню; 3) и Clipboard, который предоставляет управляемый интерфейс к системному буферу обмена. System.Windows.Forms также содержит множество классов для элементов управления, которые назы-ваются Button, TextBox, ListView и MonthCalendar.

В основе почти каждого приложения Windows Forms лежит класс, производный от System.Windows.Forms.Form. Экземпляр этого производного класса Form представляет главное окно приложения. Он наследует от Form массу свойств и методов, обеспечивающих формам богатый программный интерфейс. Хотите узнать размеры клиентской области формы? В Windows вам надо вызывать API-функцию GetClientRect, а в Windows Forms достаточно прочитать свойство ClientRectangle или ClientSize формы. Многие свойства можно не только чи-тать, но и устанавливать. Так, стиль границы окна можно изменить, задав свойство BorderStyle, размер окна меняется через свойства Size или ClientSize, а текст заголовка формы — посредством свойства Text.

Другим важным строительным блоком приложений Windows Forms является класс Application пространства имен System.Windows.Forms. У него есть статический метод Run, управляющий приложением Windows Forms с помощью конвейера сообщений. Вы этот конвейер, конечно, не увидите — обработка сообщений скрыта .NET Framework. Но он существует и является одной из многочисленных деталей, о которых вам не нужно беспоко-иться — ведь о них заботится инфраструктура! Многие приложения Windows Forms связаны и с классами из пространства имен System.Drawing, которое содержит классы-оболочки интерфейса графических устройств GDI+ (Graphics Device Interface+). Такие классы, как Brush и Реn представляют логические объекты, связанные с рисованием. Они определяют вид прямых, кривых и областей с заливкой. Классы Bitmap и Image представ-ляют изображения и умеют импортировать их из файлов разных типов, включая BMP, GIF и JPEG, Но важней-ший класс в System.Drawing — Graphics. В Windows Forms он эквивалентен контексту устройства Windows. Что-бы нарисовать линию на форме, вызовите метод DrawLine объекта Graphics, а для вывода строки текста — DrawString. Graphics содержит богатый набор методов и свойств для графического вывода на форму или дру-гое устройство (такое как принтер), которое связано с объектом Graphics.

Page 5: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

5

1.1. Ваша первая Windows-форма Лучшим знакомством с новой платформой будет приложение «Hello, world». (Из приложений «Hello, world» воз-никали целые компании!) Версия «Hello, world» для Windows Forms показана на рис. 1, а результат ее работы — на рис. 2а (см. также рис. 2б, рис. 3…рис. 6).

Листинг 9.1. Hello.cs 1: using System; // Prosiz, гл. 4, стр. 87, Hello 2: using System.Drawing; 3: using System.Windows.Forms; 4: 5: namespace WindAppl_Pros_Chap4_c87_Hello 6: { 7: class MyForm : Form 8: { 9: MyForm() 10: { 11: Text = "Windows Forms Demo"; // свойство Text 12: } 13: 14: protected override void OnPaint(PaintEventArgs e) 15: { 16: e.Graphics.DrawString("Hello, world", Font, 17: new SolidBrush(Color.Black), ClientRectangle); 18: } 19: 20: static void Main() 21: { 22: Application.Run(new MyForm()); 23: } 24: } 26: }

Рис. 1. Исходный текст Hello.cs

Я Windws Forms Demo Puc. 2а. Программа «Hello, world» для Windows Forms

В приложении Windows Forms каждая форма представлена экземпляром класса, производного от Form. В Hello.cs — это класс МуFоrm (строка 7). Конструктор MyForm изменяет текст заголовка формы (строка 11),

Page 6: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

6

присваивая строку свойству Text (см. диагр. классов). Text — это одно из более чем сотни свойств, унаследо-ванных МуFогm от Form. Каждый Windows-программист знает, что окна получают сообщения WM_PAINT и что большая часть вывода на экран происходит при обработке этих сообщений. В Windows Forms эквивалентом сообщения WM_PAINT является виртуальный метод OnPaint (см. строку 14 и диагр. классов) класса Form. Производный класс может переопределить его для отображения себя в ответ на сообщение WM_PAINT.

Вариант OnPaint в MyForm (обратите внимание на ключевое слово override, указывающее компилятору С#, что вы переопределяете виртуальный метод, унаследованный от базового класса, а не скрываете его) выводит «Hello, world» в клиентскую область формы — часть формы внутри рамок и заголовка окна. OnPaint получает объект PaintEventArgs(System.Windows.Forms.PaintEventArgs), содержащий свойства Graphics и ClipRectangle: первое хранит ссылку на объект Graphics, используя который, OnPaint рисует в клиентской области формы; второе — ссылку на объект Rectangle (System.Drawing.Rectangle), задающий часть клиентской области, кото-рая должна быть перерисована.

Для рисования MyForm.OnPaint использует метод Graphics.DrawString (см. строка 16). Первый параметр DrawString — это сама выводимая строка. Второй — объект Font (System.Drawing.Font), описывающий шрифт текста. MyForm.OnPaint использует шрифт по умолчанию, ссылка на который хранится в свойстве Font класса Form. Третий — это объект Brush (System.Drawing.Brush). задающий цвет текста. Здесь используется черный объект SolidBrush (System.DrawingSolid.Brush), поэтому выводится черный текст. Четвертый и последний па-раметр — это форматирующий прямоугольник, который определяет расположение текста. MyFormOnPaint ис-пользует для этого всю клиентскую область формы, определяемую свойством ClientRectangle. Так как DrawText по умолчанию выводит текст в левом верхнем углу форматирующего прямоугольника, то слова «Hello, world» появятся в левом верхнем углу формы. Чтобы расположить слова «Hello, world» в центре окна, следует использовать альтернативную форму DrawString, которая принимает как входной параметр объект StringFormat, и инициализировать свойства Alignment и LineAlignment этого объекта, дабы обеспечить горизонтальное и вертикальное центрирование стро-ки. Вот модифицированная версия OnPaint. Результат модифицированной программы работы — на рис. 2б.

protected override void OnPaint (PaintEventArgs e) { StringFormat format = new StringFormat(); format.Alignment = StringAlignment.Center; format.LineAlignment = StringAlignment.Center; e.Graphics.DrawString ("Hello, world", Font, new SolidBrush(Color.Black), ClientRectangle, format); }

Puc. 2б. Программа «Hello, world» для Windows Fortns

Последний член MyForm — статический метод Main. Main — это точка входа приложения. Чтобы форма появи-лась на экране, нужно лишь создать экземпляр класса MyForm и передать ссылку па него в вызове Application.Run. На рис. 1 оператор:

Page 7: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

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 или .NET Framework.

Одно из различий между GDI и GDI+ — поддержка в последней градиентных кистей (gradient brush), фунда-ментальных сплайнов и других графических средств. Но главное — разные модели программирования. В от-личие от GDI, использующей модель с сохранением состояния, GDI+ обычно состояния не запоминает. В тра-диционном Windows-приложении такие параметры, как шрифт и цвет текста устанавливаются для контекста устройства. В приложении Windows Forms - нет. Здесь параметры, определяющие характеристики выводимых результатов, передаются при вызове каждого метода Graphics. Пример этого вы видели в программе из пре-дыдущего раздела, которая для задания шрифта и цвета текста передавала методу DrawString объекты Font и SolidBrush.

1.2.1. Рисование линий, кривых и фигур

Font и SolidBrush — это графические примитивы, используемые для управления форматом изображения, вы-водимого в окно формы. Но это не единственные примитивы, которые GDI+ предоставляет в ваше распоряже-ние. Вот еще:

Графические примитивы GDI+

Класс Описание

Bitmap Представляет растровые изображения Font Определяет атрибуты шрифта: размер, гарнитуру и т. д HatchBrush Определяет заливку решетчатым узором LinearGradientBrush Определяет заливку с линейным градиентом Реn Определяет внешний вид линий и кривых SolidBrush Определяет заливку сплошным цветом TextureBrush Определяет заливку с использованием растрового изображения

Некоторые из этих классов определены в пространстве имен System.Drawing, другие — в пространстве имен System.Drawing.Drawing2D (NB). Перья, представленные экземплярами класса Реп, управляют внешним видом линий и кривых. Объект Font задают внешний вид текста, тогда как кисти, представленные объектами классов HatchBrusb, LinearGradientBrush, SolidBrush и TextureBrusb определяют виды заливки. Приведенный ниже ме-тод OnPaint рисует прямоугольники трех стилей: 1) без заливки, 2) закрашенный красным цветом и 3) с гради-ентной заливкой, переходящей из красного в синее:

protected override void OnPaint (PaintEventArgs e) { Pen pen = new Pen(Color.Black);

// Нарисовать пустой прямоугольник. e.Graphics.DrawRectangle(pen, 10, 10, 390, 90);

Page 8: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

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. Объект Реn, передаваемый DrawRectangle, обводит каждый прямоугольник черной линией толщиной в 1 единицу. (По умолчанию 1 единица — это один пиксел, поэтому все линии в дан-ном примере имеют ширину в 1 пиксел.) Кисти, передаваемые FillRectangle, управляют заливкой внутренней части прямоугольников. Заметьте: рамки и заливки рисуются по отдельности. Ветеранам Windows-программирования это будет особенно интересно, так как функции Windows GDI для рисования замкнутых фи-гур рисуют рамку и выводят заливку одновременно,

Класс Graphics содержит разнообразные открытые методы Draw и Fill для рисования линий, кривых, прямо-угольников, эллипсов и других фигур. Неполный список приведен в таблице. Документация .NET Framework SDK содержит превосходный трактат по GDI+ и снабжена примерами рисования объектов — от простых линий до самых сложных фигур с заливкой.

Методы класса Graphics для рисования линий, кривых и фигур

Метод Описание DrawArc Рисует дугу DrawBezier Рисует кривую Безье DrawCurve Рисует фундаментальный сплайн DrawEllipse Рисует круг или эллипс DrawIcon Рисует значок DrawImage Рисует картинку DrawLine Рисует линию DrawPie Рисует сектор DrawPolygon Рисует многоугольник DrawString Рисует строку текста

Page 9: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

9

FillEllipse Рисует круг или эллипс с заливкой FillPie Рисует сектор с заливкой FillPolygon Рисует многоугольник с заливкой FillRectangle Рисует прямоугольник с заливкой

1.2.2. Освобождение объектов GDI+ Как известно, классы-оболочки неуправляемых ресурсов, таких как описатели файлов и соединения БД, тре-буют обработки, гарантирующей, что эти ресурсы будут освобождены должным образом. Pen, Brush и другие классы GDI+, представляющие графические примитивы, также попадают в эту категорию, поскольку яапяются оболочками описателей GDI+. Если не закрывать описатели 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 и FillRectangle вы определяете координаты, указывающие положение левого верх-него угла прямоугольника, и расстояния, задающие его ширину и высоту. По умолчанию расстояния измеряют-ся в пикселах. Координаты задают расположение в двумерной системе, начало которой располагается в левом верхнем углу формы, а оси х и у направлены вправо и вниз соответственно. Систему координат и единицы из-мерения, применяемые по умолчанию, можно настроить, добавив в программу несколько простых операторов.

Методам Graphics передаются глобальные координаты. По пути на экран они дважды преобразуются: сначала транслируются в страничные координаты, определяющие положение на логической поверхности для рисова-ния, а затем страничные координаты преобразуются в координаты устройства, задающие физическое поло-жение на экране.

Для трансляции глобальных координат в страничные GDI+ использует матрицу преобразования. Матрицы пре-образования — это математические объекты, позволяющие преобразовывать пары координат х-у из одной системы координат в другую. Они повсеместно используются в компьютерной графике и подробно описаны в соответствующей литературе.

Матрицы преобразования, применяемые GDI+ для пересчета координат, являются экземплярами класса System.Drawing.Drawing2D.Matrix. Каждый объект Graphics содержит объект Matrix, доступ к которому осущест-вляется через свойство Transform. По умолчанию используется единичная матрица (identity matrix) — этот ма-тематический термин обозначает матрицу, не производящую преобразований (так же, как умножение числа на 1 дает то же число). Вы можете изменять матрицу преобразования, а значит, и способ пересчета глобальных координат в страничные, одним из двух способов. Первый — инициализировать экземпляр класса Matrix значе-ниями, задающими желаемое преобразование, и присвоить его свойству Transform. Это прекрасный способ

Page 10: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

10

для экспертов по линейной алгебре, но простые смертные, вероятно, предпочтут иное: зададут преобразова-ние координат таких методов 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(); }

В заключительном примере после перемещения начала координат, оси х и у поворачиваются на 30° против часовой стрелки с помощью RotateTransform (рис. 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(); }

Page 11: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

11

Puc. 5. Прямоугольник после перемещения начала координат

Puc. 6. Прямоугольник после перемещения и поворота системы координат

TranslateTransform и RotateTransform — это мощные инструменты перемещения координатной системы и ори-ентации ее осей. Родственный метод ScaleTransform позволяет еще и масштабировать координаты.

При использовании преобразований системы координат следует иметь в виду, что порядок их применения влияет на результаты, В предыдущем примере система координат была сначала перемещена, а затем повер-нута. Начало отсчета переместилось в точку (100, 100), после чего был выполнен поворот на -45° (против ча-совой стрелки). Если сначала выполнить поворот, а потом перемещение, прямоугольник появится в другом месте, так как перемещение произойдет вдоль уже повернутых осей. Это можно представить себе так: если вы, стоя в комнате, пройдете несколько шагов вперед, а затем повернетесь, то окажетесь не в том же месте, где вы были бы, если бы сначала повернулись, а потом пошли. Тот же принцип применим и к матричным преобра-зованиям.

На рис. 8 представлен исходный текст программы Clock, которая рисует циферблат аналоговых часов в соот-ветствии с текущим временем суток (рис. 7). Рисование выполняется с помощью FillRectangle (строка 29) и FillPolygon (строка 73), но настоящую работу выполняют преобразования координат. TranslateTransform (стро-ка 82) перемещает начало координат в центр формы, смещая координаты х и у на величины, равные полови-нам ширины и высоты формы. RotateTransform (строка 50) поворачивает систему координат перед рисованием часовой и минутной стрелок, а также квадратов, отмечающих деления циферблата. ScaleTransform (строка 84) масштабирует изображение так, чтобы циферблат занимал всю клиентскую область формы независимо от размера последней. Координаты, передаваемые FillRectangle и FillPolygon, рассчитываются в предположении,

Page 12: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

12

что клиентская область формы имеет размер 200 на 200 единиц. ScaleTransform задает масштабные множите-ли по осям в соответствии с отношением физических размеров клиентской области к ее логическим размерам.

В методе OnPaint класса MyForm происходит так много интересного, что немудрено не заметить важный опе-ратор в конструкторе МуFоrm (строка 12).

SetStyle (ControlStyles.ReslzeRedraw, 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() // конструктор

10: { 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:

Page 13: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

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;

Page 14: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

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) (на рис. 9б представлена линейка в метрической системе единиц):

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 ко всем координатам х и у. е.Graphics.TranslateTransform (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);

Page 15: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

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 (); }

Рис. 9а. Линейка, нарисованная в дюймовой системе единиц измерения

Рис. 9б. Линейка, нарисованная в метрической системе единиц измерения

Такую же линейку можно было бы нарисовать, оставив значение PageUnit по умолчанию и используя свойства DpiX и DpiY объекта Graphics для пересчета дюймов в пикселы вручную. Однако задание координат и расстоя-ний непосредственно в дюймах делает код читабельнее. Заметьте TranslateTransform смещает координаты х и

Page 16: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

16

у на полдюйма, чтобы вывести изображение линейки из левого верхнего угла формы. Обратите также внима-ние на 0. передаваемый конструктору объекта Реn. Значение 0 устанавливает ширину пера в 1 пиксел незави-симо от текущей системы единиц измерения. Если не указать 0, ширина пера равнялась бы по умолчанию од-ной единице. В системе единиц измерения GraphicsUnitInch единица равна дюйму, а перо шириной в такую единицу было бы очень толстым.

Вы также должны иметь в виду, что значения в дюймах — это логические значения, т. е. линейка, вероятно, не будет иметь ровно 6 дюймов в длину. Логические значения отличны от физических, так как Windows не знает, сколько в точности пикселов на дюйм отображается на вашем дисплее. В связи с этим используется предпола-гаемое разрешение в 96 точек на дюйм.

1.3. Меню Меню — это основа приложений с графическим пользовательским интерфейсом. Практически каждый сидящий перед компьютером знает, что щелкнув пункт расположенного сверху меню, получишь выпадающий список ко-манд. Даже новички легко овладевают этим с одной-двух попыток.

Поскольку меню — столь важная часть пользовательского интерфейса, .NET Framework предоставляет под-держку для использующих их приложений. Классы пространства имен System.Windows.Forms, перечисленные в таблице, участвуют в создании меню и работе с ними. В следующих разделах мы обсудим эти и другие связан-ные с меню классы, а также рассмотрим примеры их применения.

Классы меню из System.Windows.Forms

Класс Описание Menu Абстрактный базовый класс для других классов меню MainMenu Представляет главное меню СоntехtMеnи Представляет контекстные меню MenuItem Представляет пункты меню

1.3.1. Главные меню Главное меню, или меню верхнего уровня, располагается в виде горизонтальной полосы прямо под заголовком окна. Следующий код создает главное меню, содержащее два выпадающих меню «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;

Page 17: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

17

Первый оператор создает объект MainMenu и сохраняет полученную ссылку на него в переменной menu. Все меню, включая экземпляры MainMenu, наследуют от класса Menu свойство Menultems, представляющее пункты меню. Вызов метода Add для набора MenuItemCottection, доступного через свойство Menultems, добавляет пункт к меню и может также зарегистрировать обработчик событий, вызываемый, когда пользователь выберет этот пункт. Операторы:

Menultem item = menu.Menultems.Add ("&File"};

item = menu.Menultems.Add ("&Edit");

добавляют к главному меню пункты верхнего уровня «File» и «Edit». Остальные вызовы Add добавляют пункты к меню File и Edit. (Амперсанд в тексте пункта меню задает «быструю клавишу». Например, «&File» задает в качестве быстрой клавиши для пункта File сочетание Alt+F и автоматически подчеркивает F.) Заключительный оператор присоединяет MainMenu к форме, присваивая ссылку на этот объект свойству Menu. Все формы на-следуют свойство Menu от System.Windows.Forms.Form.

1.3.2. Обработка команд меню Выбор пункта меню генерирует событие Click и активизирует его обработчик, если таковой зарегистрирован. В нашем примере регистрировались обработчики пунктов меню с именами OnFileNew, OnFileOpen и т. д. Обра-ботчик события Click имеет такой прототип:

void HandlerName(Object sender, EventArgs e) Обработчик OnFileExit для команды File/Exit мог бы быть реализован так:

void OnFileExit (Object sender, EventArgs e) { Close (); // Закрыть форму. } Первый параметр обработчика указывает пункт меню, выбранный пользователем. Второй параметр — кон-тейнер дополнительной информации о событии. Обычно обработчики событий меню игнорируют этот пара-метр, так как EventArgs не содержит никакой полезной информации.

Альтернативный способ подключения обработчиков событий к пунктам меню — это конструкция += в языке С#:

Menultem exit = item.Menultems.Add("&Exit"); exit.Click += new EventHandler(OnFileExit); Предпочтительнее указывать обработчики событий при вызове Add, так как это делает программу понятнее.

1.3.3. Контекстные меню Многие приложения выводят контекстные меню в ответ на щелчок правой кнопки мыши. Такие меню содержат контекстно-зависимый список команд, которые могут быть применены к объекту, расположенному в точке щелчка. В приложении Windows Forms контекстные меню представлены объектами ContextMenu. Пункты в ContextMenu добавляются так же, что и в МаinМеnu. Один из способов отображения контекстного меню — вы-зов ContextMenuShow, В следующем примере создается и отображается на экране контекстное меню из трех пунктов: ContextMenu menu = new ContextMenu(); menu.Menultems.Add("&Open", new EventHandler(OnOpen)); menu.Menultems.Add("&Rename", new EventHandler(OnRename)); menu.Menultems.Add("&Delete", new EventHandler(OnDelete)); menu.Show(this, new Point (x, y)); Первый параметр ContextMenuShow указывает форму к которой относится это меню (при выборе пунктов кон-текстного меню будут вызываться обработчики событий этой формы). Второй параметр задает место, в кото-ром будет отображаться меню. Координаты задаются в пикселах относительно левого верхнего угла формы, указанной первым параметром.

Page 18: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

18

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

ContextMenu menu = new ContextMenu(); …. ContextMenu = menu; В результате .NET Framework будет автоматически отображать это контекстное меню по правому щелчку в пределах формы.

Как и в случае главного меню, пункты контекстного при щелчке генерируют события Click. И так же, как и для главного меню, вы можете указывать обработчики при добавлении пункта к меню или добавлять их отдельно с помощью оператора +=.

1.3.4. Состояния пунктов меню Многие приложения в процессе работы изменяют состояния пунктов меню согласно своему текущему состоя-нию. Так, если в приложении не выбрано ничего подходящего для меню Edit, правила хорошего тона требуют, чтобы приложение отключило команды Cut и Сору.

Приложения Windows Forms управляют состоянием пунктов меню свойствами объектов Menultem: 1) Свойство Checked определяет, есть ли галочка рядом с соответствующим пунктом меню, 2) Свойство Enabled управляет состоянием «включено/выключено», a 3) Свойство Text обеспечивает доступ к тексту пункта меню. Если item — это объект Menultem, то следующий оператор устанавливает рядом с пунктом меню галочку:

item.Checked = true; А этот снимает ее:

item.Checked = false;

MFC-программистам знакомо понятие обработчиков обновления (update handlers) — функций, единственная задача которых — обновлять состояния пунктов меню. MFC вызывает такие обработчики непосредственно пе-ред тем, как меню отображается на экране, что позволяет обработчикам обновить пункты меню, прежде чем их увидит пользователь. С помощью обработчиков обновления код, изменяющий состояние приложения, отделя-ется от кода, обновляющего состояния пунктов меню.

Обработчики обновления можно писать и для Windows Forms. Секрет в том, чтобы зарегистрировать обработ-чик события Popup, которое генерируется по щелчку меню, содержащего пункт, состояние которого нужно об-новить и выполнить обновление в этом обработчике. Вот как регистрируется обработчик события Popup для меню Edit и обновляются пункты этого меню в соответствии с текущим состоянием приложения:

Menultem item = menu.Menultems.Add("&Edit"); item.Popup += new EventHandler(OnPopupEditMenu); …. void OnPopupEditMenu(Object sender, EventArgs e) { Menultem menu = (Menultem) sender; foreach (Menultem item in menu.Menultems) { switch (item.Text)

{ case "&Cut": case "&Copy": item. Enabled = IsSomethingSelected(); break;

Page 19: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

19

case "&Paste": item. Enabled = IsSomethingOnClipboard(); break; } } } IsSomethingSelected («Выделено ли что-то») и IsSometbingOnClipboard («Есть ли что-то в буфере обмена») — это вымышленные методы, назначение которых, надеюсь, вполне очевидно. Обработка событий MenuItemPopup позволяет отложить обновление пунктов своего меню, пока это действительно не потребуется, и снизить вероятность того, что это просто забудут сделать.

1.3.5. «Быстрые» клавиши

Последнее замечание в связи с меню касается «быстрых» клавиш, т. е. таких, нажатие на которые приводит к вызову команд меню, например, Ctrl+O (для команды «Открыть»), Ctrl+S (для «Сохранить») и др. Windows-программисты реализуют «быстрые» клавиши путем загрузки ресурсов «быстрых» клавиш — в Windows Forms все гораздо проще. При создании Menultem «быструю» клавишу можно задать как третий параметр конструк-тора, после чего получившийся объект Menultem добавляется к меню через MenultemCollectionAdd.

item.MenuItems.Add (new Menultem("&0pen...", new EventHandler(OnFileOpen), Shortcut.CtrlO)); CtrlO — это член перечисления Shortcut, определенного в System.Windows.Forms.Shortcut. Shortcut содержит длинный список элементов, представляющих доступные «быстрые» клавиши.

1.4. Приложение ImageView ImageView (рис. 10а,б,в,) — это программа просмотра изображений, способная работать с разнообразными файлами картинок. Ее сердцевиной является оператор:

Bitmap bitmap = new Bitmap(FileName); создающий объект System.Drawing.Bitmap, инкапcулирующий изображение из файла, указанного FileName. За-грузка изображения одним оператором — мощное средство. Класс Bitmap способен работать с BMP, GIF, JPEG и другими распространенными форматами файлов изображений. В традиционном (неуправляемом) Windows-приложении для этого нужно было писать огромный объем кода или покупать графическую библиотеку сторон-него производителя. Столь же интересен оператор:

g.DrawImage (MyBitmap, ClientRectangle); Он выводит картинку в окно формы. (MyBitmap — это закрытое поле, в котором хранится ссылка на текущую картинку). Windows-программисты для отображения растровых картинок вынуждены работать с контекстами устройства в памяти и функцией BitBlt. В Windows Forms вы просто вызываете Graphics.DrawImage.

ImageView демонстрирует и другие важные принципы программирования для Windows Forms, в том числе:

как использовать меню, обработчики команд меню и обработчики обновления; как изменить размер формы, модифицировав ее свойство ClientSize; как с помощью диалоговых окон открывать файлы (экземпляры System.Windows.Forms.OpenFileDialog), что-бы получить имя файла от пользователя;

как с помощью MessageBoxShow вывести на экран информационные окна: как создать форму с полосой прокрутки путем установки свойства AutoScroll формы в true и свойства

AutoScrolMinSize в соответствии с размером виртуальной области просмотра; как правильно освобождать объекты Bitmap.

Последний пункт в этом списке важен, иначе память утекала бы из ImageView, как сквозь решето. Вместо того чтобы открывать файл изображения и присваивать МуВitmар ссылку на Bitmap одним оператором;

MyBitmap = new Bitmap(FileName);

Page 20: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

20

ImageView поступает так:

Bitmap bitmap = new Bitmap(FileName); if (MyBitmap != null) MyBitmap.Dispose(); // Важно! MyBitmap = bitmap; Чувствуете разницу? Объекты Bitmap инкапсулируют растровые изображения, которые являются неуправляе-мыми ресурсами, потребляющими много памяти. Вызывая Dispose для старого объекта Bitmap после открытия новой картинки, ImageView освобождает инкапсулированную картинку сразу, вместо того чтобы отложить это до момента вызова метода Bitmap finalize сборщиком мусора. Приведенный код прекрасный пример того, по-чему программисты всегда должны быть настороже во избежание истощающих системные ресурсы побочных эффектов недетерминированного уничтожения.

Рис. 10a. Форма и меню в программе ImageView

Листинг 9.3. lmageView.cs

1: using System; // Prosiz, гл. 4, стр. 104, ImageView 2: using System.Drawing; 3: using System.Windows.Forms; 4: 5: namespace WindAppl_Pros_Chap4_c104_ImageView 6: { 7: class MyForm : Form 8: { 9: MenuItem NativeSize; 10: MenuItem FitToWindow; 11: bool ShowNativeSize = true; 12: int FilterIndex = -1;

Page 21: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

21

13: Bitmap MyBitmap = null; 14: 15: MyForm() 16: { 17: // Задать название формы 18: Text = "Просмотр изображений"; 19: 20: // Задать размер формы 21: ClientSize = new Size(640, 480); 22: 23: // Создать меню 24: MainMenu menu = new MainMenu(); 26: MenuItem item = menu.MenuItems.Add("&Options"); 27: item.Popup += new EventHandler(OnPopupOptionsMenu); 28: item.MenuItems.Add(new MenuItem("&Open...", 29: new EventHandler(OnOpenImage), Shortcut.CtrlO));30: item.MenuItems.Add("-"); 31: item.MenuItems.Add(FitToWindow = 32: new MenuItem("Size Image to &Fit Window", 33: new EventHandler(OnFitToWindow))); 34: 36: item.MenuItems.Add(NativeSize = new MenuItem("Show Image in &Native Size", 37: new EventHandler(OnNativeSize))); 38: 39: 40: item.MenuItems.Add("-"); 41: item.MenuItems.Add(new MenuItem("E&xit", new EventHandler(OnExit))); 42: 43: 44: // Присоединить меню к форме 46: Menu = menu; 47: } 48: 49: // Обработчик обновления пунктов меню Options 50: void OnPopupOptionsMenu(object sender, EventArgs e) 51: { 52: NativeSize.Checked = ShowNativeSize; 53: FitToWindow.Checked = !ShowNativeSize; 54: } 55: 56: // Обработчик команды Open 57: void OnOpenImage(object sender, EventArgs e) 58: { 59: OpenFileDialog ofd = new OpenFileDialog(); 60: 61: ofd.Filter = "Image Files (JPEG, GIF, BMP, etc.)|" + 62: "*.jpg;*.jpeg;*.gif;*.bmp;*.tif;*.tiff;*.png|" + 63: "JPEG files (*.jpg;*.jpeg)|*.jpg;*.jpeg|" +

Page 22: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

22

64: "GIF Files (*.gif)|*.gif|" + 65: "BMP Files (*.bmp)|*.bmp|" + 66: "TIFF Files (*.tif;*.tiff)|*.tif;*.tiff|" + 67: "PNG Files (*.png)|*.png|" + 68: "All files (*.*)|*.*"; 69: 70: if (FilterIndex != -1) 71: ofd.FilterIndex = FilterIndex; 72: 73: if (ofd.ShowDialog() == DialogResult.OK) 74: { 75: String FileName = ofd.FileName; 76: if (FileName.Length != 0) 77: { 78: FilterIndex = ofd.FilterIndex; 79: try 80: { 81: Bitmap bitmap = new Bitmap(FileName); 82: if (MyBitmap != null) 83: MyBitmap.Dispose(); // Важно 84: MyBitmap = bitmap; 85: string[] parts = FileName.Split('\\'); 86: Text = "Image Viewer - " + parts[parts.Length - 1]; 87: if (ShowNativeSize) 88: { 89: AutoScroll = true; 90: AutoScrollMinSize = MyBitmap.Size; 91: AutoScrollPosition = new Point(0, 0); 92: } 93: Invalidate(); 94: } 95: catch (ArgumentException) 96: { 97: MessageBox.Show(String.Format("{0} is not " + "a valid image file", FileName), 98: "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 99:

100: } 101: } 102: } 103: ofd.Dispose(); 104: } 105: 106: // Обработчик команды Size Image to Fit Window 107: void OnFitToWindow(object sender, EventArgs e) 108: { 109: ShowNativeSize = false; 110: SetStyle(ControlStyles.ResizeRedraw, true); 111: if (MyBitmap != null)

Page 23: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

23

112: { 113: AutoScroll = false; 114: Invalidate(); 115: } 116: } 117: 118: // Обработчик команды Show Image in Native Size (исходный размер) 119: void OnNativeSize(object sender, EventArgs e) 120: { 121: ShowNativeSize = true; 122: SetStyle(ControlStyles.ResizeRedraw, false); 123: if (MyBitmap != null) 124: { 126: AutoScroll = true; 127: AutoScrollMinSize = MyBitmap.Size; 128: AutoScrollPosition = new Point(0, 0); 129: Invalidate(); 130: } 131: } 132: 133: // Обработчик команды Exit 134: void OnExit(object sender, EventArgs e) 136: { 137: Close(); 138: } 139: 140: // Обработчик OnPaint 141: protected override void OnPaint(PaintEventArgs e) 142: { 143: if (MyBitmap != null) 144: { 146: Graphics g = e.Graphics; 147: if (ShowNativeSize) 148: g.DrawImage(MyBitmap, 149: AutoScrollPosition.X, AutoScrollPosition.Y, 150: MyBitmap.Width, MyBitmap.Height); 151: else 152: g.DrawImage(MyBitmap, ClientRectangle); 153: } 154: } 155: [STAThreadAttribute] 156: static void Main() 157: { 158: Application.Run(new MyForm()); 159: } 160: } 161: }

Рис. 10б. Исходный текст программы ImageView

Page 24: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

24

Рис. 10в. JPEG-файл в программе ImageView

1.5. Мышь и клавиатура Весь ввод в ImageView осуществляется посредством меню, но формы могут обрабатывать и данные, введен-ные посредством мыши и клавиатуры. Windows уведомляет формы о событиях мыши и клавиатуры с помощью сообщений. Так, сообщение WM_LBUTTONDOWN означает нажатие левой кнопки мыши в клиентской области формы. Windows-формы обрабатывают сигналы, поступающие от мыши и с клавиатуры, переопределяя вирту-альные методы, унаследованные от класса Form. В таблице перечислены виртуальные методы, соответст-вующие событиям, генерируемым мышью и клавиатурой. В самой правой колонке указан тип аргумента собы-тия, передаваемого как параметр метода.

Виртуальные методы формы для обработки сигналов от мыши и с клавиатуры

Метод Когда вызывается Тип аргумента OnKeyDown По нажатию клавиши KeyEventArgs OnKeyPress При вводе символа с клавиатуры KeyPressEventArgsOnKeyUp При отпускании клавиши KeyEventArgs OnMouseDown При нажатии на кнопку мыши MouseEventArgs OnMouseEnter При входе указателя мыши в окно формы EventArgs OnMouseHover При остановке указателя мыши поверх окна формы EventArgs OnMouseLeave При покидании окна формы указателем мыши EventArgs OnMouseMove При перемещении указателя мыши по окну формы MouseEventArgs OnMouseUp При отпускании кнопки мыши MouseEventArgs ОпМоusеWheel При прокручивании колесика мыши MouseEventArgs

Page 25: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

25

1.5.1. Обработка ввода с клавиатуры

Методы OnKeyDown и OnKeyUp соответствуют сообщениям Windows WM_KEYDOWN и WM_KEYUP. В случае переопределения в производном классе они вызываются при нажатии и отпускании клавиш. Свойство KeyCode объекта KeyEventArgs, передаваемого OnKeyDown и OnKeyUp, указывает клавишу, сгенерировавшую данное событие. Вот пример, перехватывающий нажатие клавиши «F1»:

protected override void OnKeyDown(KeyEventArgs e) { if (e.KeyCode == Keys.F1) { // Нажата клавиша F1. } } KeyEventArgs также содержит свойства Alt, Control, Shift и Modifiers, позволяющие определить, были ли в момент возникновения данного события нажаты клавиши Ctrl, Alt или Shift (или их комбинации). Родственный метод OnKeyPress, соответствующий сообщению Windows WM_CHAR, вызывается, когда с клавиату-ры вводится символ. Не все клавиши генерируют символ при своем нажатии. Некоторые, такие как клавиши от А до 2, — да, а другие, такие как F1 и F2, — нет. Для обработки несимвольных клавиш нужно переопределять OnKeyDown. Для обработки же символьных обычно переопределяется OnKeyPress.. Почему? Потому что OnKeyPress принимает аргумент типа KeyPressEventArgs, чье свойство КеуСhаг указывает введенный символ с учетом текущего состояния других клавиш (таких, как Shift). Если пользователь нажимает клавишу С — OnKeyDown просто сообщит о том, что клавиша С нажата. Тогда как OnKeyPress скажет о том, прописная это С или строчная. Вот пример обработчика OnKeyPress, по-разному реагирующего на нажатие строчной и прописной С:

protected override void OnKeyPress(KeyPressEventArgs e) { if (e.KeyChar == 'C') { // Одни действия. } else if(e.KeyChar == 'c') { // Другие действия. } }

1.5.2. Обработка сигналов от мыши При нахождении указателя мыши поверх формы нажатие и отпускание кнопки мыши вызывает методы OnMouseDown и OnMouseUp формы в указанном порядке. Оба метода принимают параметр MouseEventArgs, имеющий: 1) свойство Button, которое указывает нажатую кнопку мыши (MouseButtonsLeft, MouseButtonsMiddle или MouseButtonsRight); 2) свойство Clicks, указывающее, является ли этот щелчок одиночным (Clicks == 1) или двойным (Clicks == 2); 3) а также свойства X и У, определяющие положение указателя. Координаты указа-теля выражены в пикселах относительно левого верхнего угла формы, в которой произошел щелчок. Пара координат (100, 200), например, указывает, что событие произошло, когда курсор находится в 100 пикселах правее и 200 пикселах ниже левого верхнего угла формы.

Метод OnMouseDown рисует небольшой крестик в текущей позиции курсора при каждом нажатии левой кнопки мыши: protected override void OnMouseDown (MouseEventArgs e) {

Page 26: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

26

if (e.Button == MouseButtons.Left) { Graphics g = Graphics.FromHwnd(Handle); Pen pen = new Pen (Color.Black); g.DrawLine (pen, e.X - 4, e.Y - 4, e.X + 4, e.Y + 4); g.DrawLine (pen, e.X - 4, e.Y + 4, e.X + 4, e.Y - 4); pen.Dispose(); g.Dispose(); } } Этот пример демонстрирует, как рисовать в окне формы вне обработчика OnPaint. Секрет в том, чтобы пере-дать описатель окна формы, который хранится в FormHandle, статическому методу GraphicsFromHwnd. В ответ вы получите объект Graphics. He забудьте вызвать для него Dispose, когда закончите, так как объект Graphics, полученный таким способом, в отличие от объекта Graphics, передаваемого в PaintEventArgs, автоматически не освобождается.

Альтернативный способ реакции на щелчки мыши — переопределить методы OnClick и OnDoubleClick класса Form. Первый вызывается при щелчке формы левой кнопкой мыши, второй — при двойном щелчке в окне формы. Ни один из них не получает сведений о координатах щелчка. Обычно эти методы представляют боль-ший интерес для разработчиков элементов управления, чем для разработчиков форм, так как во втором случае при обработке нажатия на кнопку мыши нужно знать, в какой точке оно произошло.

Для слежения за перемещением мыши поверх формы переопределите в своем производном классе метод OnMouseMove, соответствующий Windows-сообщению WM_MOUSEMOVE. Свойства X и У передаваемого это-му методу объекта MouseEventArgs соответствуют последнему положению указателя, свойство Button — кноп-ке или кнопкам, которые сейчас нажаты. Если, скажем, нажаты и левая, и правая кнопка мыши, то значение свойства Button равно «поразрядному или» между MouseButtonsLeft и MouseButtonsRight.

Рис. 11. MouseTracker отображает текущие координаты курсора

Методы OnMouseEnter, OnMouseHover и OnMouseLeave позволяют форме определить, когда курсор входит в пределы ее окна, неподвижно покоится над ней и покидает ее. Одним из применений этих методов является отображение текущих координат курсора. Как это сделать, демонстрирует приложение MouseTracker (рис. 12). По мере перемещения мыши поверх формы OnMouseMove обновляет координаты, отображаемые в центре формы (рис. 11). Когда мышь покидает форму, OnMouseLeave стирает изображение координат. Обработчика

Page 27: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

27

OnMouseEnter нет, так как за его вызовом почти сразу же следует вызов OnMouseMove, которому передаются координаты указателя.

Листинг 9.4. MouseTracker.cs

1: using System; 2: using System.Drawing; 3: using System.Windows.Forms; 4: 5: namespace WindAppl_Pros_Chap4_MouseTracker 6: { 7: class MyForm : Form 8: { 9: int cx;

10: int cy; 11: 12: MyForm() 13: { 14: Text = "Mouse Tracker"; 15: 16: Graphics g = Graphics.FromHwnd(Handle); 17: SizeF size = g.MeasureString("MMMM, MMMM", Font); 18: cx = (Convert.ToInt32(size.Width) / 2) + 8; 19: cy = (Convert.ToInt32(size.Height) / 2) + 8; 20: g.Dispose(); 21: } 22: 23: protected override void OnMouseMove(MouseEventArgs e) 24: { 26: Graphics g = Graphics.FromHwnd(Handle); 27: EraseCoordinates(g); 28: ShowCoordinates(e.X, e.Y, g); 29: g.Dispose(); 30: } 31: 32: protected override void OnMouseLeave(EventArgs e) 33: { 34: Graphics g = Graphics.FromHwnd(Handle); 36: EraseCoordinates(g); 37: g.Dispose(); 38: } 39: 40: void EraseCoordinates(Graphics g) 41: { 42: int x = ClientRectangle.Width / 2; 43: int y = ClientRectangle.Height / 2; 44: 46: SolidBrush brush = new SolidBrush(BackColor); 47: g.FillRectangle(brush, x - cx, y - cy, x + cx, y + cy); 48: brush.Dispose();

Page 28: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

28

49: } 50: 51: void ShowCoordinates(int x, int y, Graphics g) 52: { 53: StringFormat format = new StringFormat(); 54: format.Alignment = StringAlignment.Center; 55: format.LineAlignment = StringAlignment.Center; 56: 57: string coords = String.Format("{0}, {1}", x, y); 58: 59: SolidBrush brush = new SolidBrush(Color.Black); 60: g.DrawString(coords, Font, brush, ClientRectangle, format); 61: brush.Dispose(); 62: } 63: 64: static void Main() 65: { 66: Application.Run(new MyForm()); 67: } 68: } 69: }

Рис. 12. Исходный текст программы MouseTracker

1.5.3. Приложение NetDraw Рассмотрим еще одно приложение, в котором обрабатываются: 1) и сигналы мыши, 2) и ввод с клавиатуры, NetDraw (рис. 14) — это простая программа для рисования (рис. 13). Для рисования перемещайте мышь, удер-живая ее левую кнопку нажатой. Чтобы очистить область рисования, нажмите клавишу Del.

Рис. 13. Результат работы Программа NetDraw

Основную работу выполняют методы OnMouseDown, OnMouseMove и OnMouseUp. Первый создает новый объект Stroke, представляющий линию, которую пользователь только что начал рисовать, и записывает его в

Page 29: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

29

CurrentStroke. Второй добавляет новую пару координат х-у к объекту Stroke, созданному в OnMouseDown. Третий добавляет текущий объект Stroke к набору Strokes, который представляет собой объект ArrayList, со-держащий все линии, нарисованные пользователем. Когда нужно перерисовать форму, метод OnPaint прохо-дит по массиву Strokes и вызывает StrokeDraw для воспроизведения всех линий.

Класс Stroke, определенный в NetDraw.cs, содержит ArrayList, хранящий массив объектов Point. Благодаря ArrayList, один объект Stroke может содержать практически неограниченное число точек х-у. Метод Draw класса Stroke рисует линии, соединяющие эти точки, с помощью GraphicsDrawLine.

Листинг 9.5. NetDraw.cs 1: using System; // Prosiz, гл. 4, стр. 112, NetDraw 2: using System.Collections; 3: using System.Windows.Forms; 4: using System.Drawing; 5: using System.Drawing.Drawing2D; 6: 7: namespace WindAppl_Pros_Chap4_c112_NetDraw 8: { 9: class MyForm : Form /////////////////// начало класса MyForm ///////////////

10: { 11: Stroke CurrentStroke = null; 12: ArrayList Strokes = new ArrayList(); 13: 14: MyForm() 15: { 16: Text = "NetDraw"; 17: } 18: 19: protected override void OnPaint(PaintEventArgs e) 20: { 21: // Нарисовать все записанные линии 22: foreach (Stroke stroke in Strokes) 23: stroke.Draw(e.Graphics); 24: } 26: 27: protected override void OnMouseDown(MouseEventArgs e) 28: { 29: if (e.Button == MouseButtons.Left) 30: { 31: // Создать новый объект Stroke и присвоить его CurrentStroke 32: CurrentStroke = new Stroke(e.X, e.Y); 33: } 34: } 36: 37: protected override void OnMouseMove(MouseEventArgs e) 38: { 39: if ((e.Button & MouseButtons.Left) != 0 && CurrentStroke != null) 40: { 41: // Добавить новый сегмент к текущей линии 42: CurrentStroke.Add(e.X, e.Y);

Page 30: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

30

43: Graphics g = Graphics.FromHwnd(Handle); 44: CurrentStroke.DrawLastSegment(g); 46: g.Dispose(); 47: } 48: } 49: 50: protected override void OnMouseUp(MouseEventArgs e) 51: { 52: if (e.Button == MouseButtons.Left && CurrentStroke != null) 53: { 54: // Завершить текущую линию 55: if (CurrentStroke.Count > 1) 56: Strokes.Add(CurrentStroke); 57: CurrentStroke = null; 58: } 59: } 60: 61: protected override void OnKeyDown(KeyEventArgs e) 62: { 63: if (e.KeyCode == Keys.Delete) 64: { 65: // Удалить все линии и перерисовать форму 66: Strokes.Clear(); 67: Invalidate(); 68: } 69: } 70: 71: static void Main() 72: { 73: Application.Run(new MyForm()); 74: } 75: } ///////////////////////////////////////////////////конец класса MyForm//////////////////////////// 76: 77: class Stroke /////////////////////////////// начало класса Stroke ////////////////////////// 78: { 79: ArrayList Points = new ArrayList(); 80: 81: public int Count 82: { 83: get { return Points.Count; } 84: } 85: 86: public Stroke(int x, int y) 87: { 88: Points.Add(new Point(x, y)); 89: } 90: 91: public void Add(int x, int y)

Page 31: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

31

92: { 93: Points.Add(new Point(x, y)); 94: } 95: 96: public void Draw(Graphics g) 97: { 98: Pen pen = new Pen(Color.Black, 8); 99: pen.EndCap = LineCap.Round;

100: for (int i = 0; i < Points.Count - 1; i++) 101: g.DrawLine(pen, (Point)Points[i], (Point)Points[i + 1]); 102: pen.Dispose(); 103: } 104: 105: public void DrawLastSegment(Graphics g) 106: { 107: Point p1 = (Point)Points[Points.Count - 2]; 108: Point p2 = (Point)Points[Points.Count - 1]; 109: Pen pen = new Pen(Color.Black, 8); 110: pen.EndCap = LineCap.Round; 111: g.DrawLine(pen, p1, p2); 112: pen.Dispose(); 113: } 114: } /////////////////////////////// конец класса Stroke ////////////////////////////////// 115: }

Рис. 14. Исходный текст программы NetDraw

1.6. Другие события формы Класс, производный от Form, наследует длинный список виртуальных методов с названиями, начинающимися на On. Эти методы вызываются в ответ на события формы (form-level events). Их наличие объясняется стрем-лением упростить обработку событий. Если бы методов On не было, то каждый написанный нами обработчик нужно было бы обернуть в делегат и связать с событием вручную.

OnKeyDown, OnMouseDown и OnPaint — лишь немногие из методов On, которые можно переопределить в про-изводном классе для обработки событий, происходящих за время жизни формы. Некоторые подобные методы перечислены в таблице. Если вы хотите знать, когда ваша форма изменяет размер, просто переопределите OnSizeChanged в производном классе. OnSizeChanged является эквивалентом Windows-сообщений WM_SIZE и вызывается при всяком изменении размеров формы. Нужно определить момент прямо перед закрытием формы, чтобы сообщить пользователю о не сохраненных данных? Тогда переопределите OnClosing. Если хо-тите, можете воспрепятствовать закрытию формы, установив в true свойство Cancel объекта CancelEventArgs, передаваемого OnClose как параметр,

Виртуальные методы, соответствующие событиям формы

Метод Когда вызывается OnActivated При активизации формы OnClosed При закрытии формы OnClosing Перед закрытием формы OnDeactivate При деактивизации формы OnGotFocus При получении формой фокуса ввода OnLoad При создании формы OnLocationChanged При перемещении формы

Page 32: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

32

OnLostFocus При потере формой фокуса ввода OnPaintBackground При необходимости перерисовки фона форма OnSizeChanged При изменении размеров формы

Программа CloseDemo.cs (рис. 16) демонстрирует одно из применений OnClosing (рис. 15). Когда пользователь щелкает кнопку Close в правом верхнему углу формы, Windows начинает процесс закрытия формы, и инфра-структура вызывает OnClosing, В нашей реализации OnClosing на экране отображается информационное окно, предлагающее пользователю щелкнуть «Да» для закрытия формы или «Нет», чтобы не закрывать ее. Если от-вет пользователя «Да», то OnClosing устанавливает CancelEventArgs.Cancel в false, и форма закрывается. Ес-ли же ответом будет «Нет», то CancelEventArgs.Cancel устанавливается в true, и форма остается на экране.

Рис. 15. Результат работы программа CloseDemo

Листинг 9.6. CloseDemo.cs

1: using System; // Prosiz, гл. 4, стр. 116, CloseDemo 2: using System.Windows.Forms; 3: using System.ComponentModel; 4: 5: namespace WindAppl_Pros_Chap4_c116_CloseDemo 6: { 7: class MyForm : Form 8: { 9: MyForm() 10: { 11: Text = "OnClosing Demo"; 12: } 13: 14: protected override void OnClosing(CancelEventArgs e) 15: { 16: DialogResult result = 17: MessageBox.Show("Закрыть эту форму?", "Пожалуйста, подтвердите",18: MessageBoxButtons.YesNo, MessageBoxIcon.Question); 19: e.Cancel = (result == DialogResult.No); 20: }

Page 33: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

33

21: 22: static void Main() 23: { 24: Application.Run(new MyForm()); 26: } 27: } 28: }

Рис. 16. Исходный текст программы CloseDemo

1.7. Элементы управления Windows насчитывает более 20 встроенных типов элементов управления, позволяющих разработчикам эконо-мить время и обеспечивающих совместимость внешнего вида и пользовательского интерфейса в Windows-приложениях. Командные кнопки и окна списков — это примеры элементов управления, как и древовидные списки, панели инструментов и строки состояния. Элементы управления были основой ОС Windows еще в вер-сии 1, и с годами их список расширялся.

Пространство имен System.Windows.Forms содержит обертки встроенных элементов управления Windows, а также предоставляет несколько собственных элементов управления, не имеющих прямых аналогов в ОС. Все они перечислены в таблице. Каждый класс предоставляет свойства, облегчающие программирование элемен-тов управления. Хотите создать стилизованную кнопку с картинкой в качестве фона? Нет проблем. Просто оберните изображение в объект Bitmap и назначьте его свойству кнопки Backgroundlmage. Другой пример свя-зан с цветом элементов управления. Пробовали когда-нибудь изменять фон полей ввода? В Windows Forms это просто: запишите цвет в свойство BackColor элемента управления, а дальше .NET Framework все сделает за вас.

Классы элементов управления из System.Windows.Forms

## Класс Описание 1 Button Командные кнопки

2 CheckBox Флажки

3 CheckedListBox Списки с элементами, содержащими флажки

4 ComboBox Поля со списком

5 DataGrid Элементы управления для отображения табличных данных 6 DataGridTextBox Поля ввода в элементах управления DataGrid 7 DateTimePicker Элементы управления для ввода даты и времени 8 GroupBox Рамки 9 HScrollBar Горизонтальные полосы прокрутки

10 Label Элементы управления, отображающие не редактируемый текст

11 LinkLabel Элементы управления, отображающие гиперссылки

12 ListBox Окна списков

13 ListView Списки (отображают списки элементов в различных стилях)

14 MonthCalendar Элементы управления «месячный календарь»

15 NumericUpDown Наборные счетчики

16 PictureBox Элементы управления для отображения картинок

17 PrintPreviewControl Элементы управления для предварительного просмотра печати

18 ProgressBar Индикаторы выполнения

19 PropertyGrid Элементы управления для отображения списков свойств объектов

20 RadioButton Переключатели

21 RichTextBox Поля ввода с форматированием

Page 34: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

34

22 StatusBar Строки состояния

23 TabControl Закладки

24 TextBox Поля ввода

26 ToolBar Панели инструментов

27 ToolTip Всплывающие подсказки

28 TrackBar Ползунки

29 TreeView Древовидные списки

30 VScrollBar Вертикальные полосы прокрутки

Создание элемента управления и отображение его в окне формы — это трехэтапный процесс:

1. создать экземпляр соответствующего класса элементов управления; 2. инициализировать значения свойств элемента управления; 3. добавить элемент управления к форме, вызвав Add для набора Controls формы.

Следующий код создает командную кнопку и добавляет ее к форме. Кнопка имеет размер 94 X 24 пиксела, а ее верхний угол расположен на 16 пикселов правее и на 16 пикселов ниже верхнего левого угл формы. Текст на кнопке: «Click Me»:

Button MyButton; // Поле. …. MyButton = new Button(); // 1. создать экземпляр MyButton.Location = new Point (16, 16); // 2. инициализировать значения свойств MyButton.Size = new Size (96, 24); // “ - ” MyButton.Text = "Click Me"; // “ - ” Controls.Add (MyButton); // 3. добавить элемент управления к форме Большинство элементов управления генерирует события, уведомляющие их владельца о действиях пользова-теля. Так, при щелчке кнопки генерируется событие Click. Форма, желающая что-то сделать в ответ на щелчок кнопки MyButton, должна зарегистрировать обработчик событий:

MyButton.Click += new EventHandler(OnButtonClicked); void OnButtonClicked(Object sender, EventArgs e) // Обработчик событий { // Здесь программируется отклик на щелчок кнопки. } EventHandler — это делегат, определенный в пространстве имен System. Делегаты — это оболочки функций обратного вызова, которые обеспечивают безопасность типов и особенно полезны при подключении обра-ботчиков к событиям. Первый параметр обработчика события определяет элемент управления, сгенери-ровавший событие. Второй — несет дополнительные сведения о событии. Как и в случае событий, связанных с пунктами меню, EventArgs не содержит полезной информации. Но некоторые элементы управления передают обработчикам своих событий аргументы других типов. Так, древовидные списки генерируют событие AfterExpand после раскрытия ветви дерева. В качестве делегата AflerExpand использует TreeViewEventHandler. Второй параметр, передаваемый TreeViewEventHandler, имеет тип TreeViewEventArgs и несет сведения о ветви дерева, которая была раскрыта.

Как узнать, какие события генерирует данный элемент управления? Читайте документацию! Например, найди-те там ListBox — вы увидите, что он определяет событие DoubleClick, генерируемое всякий раз, когда пользо-ватель дважды щелкнет элемент списка. Прототип DoubleClick таков:

public event EventHandler DoubleClick;

Page 35: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

35

Как видите, обработчик события DoubleClick должен быть обернут в делегат EventHandler. И, зная прототип EventHandler:

public delegate void EventHandler(Object sender, EventArgs e); вы знаете и прототип обработчиков DoubleClick.

1.7.1. Приложение ControlDemo Приложение ControlDemo (рис. 17) демонстрирует основы применения элементов управления в Windows Forms. Единственная форма приложения содержит 4 элемента управления: Label, TextBox, ListBox и Button. Чтобы посмотреть ContolDemo в работе, запустите его, наберите в поле ввода имя пути (скажем, C:\Winnt) и щелкните кнопку Show File Names — в окне списка будут отображены имена файлов, хранящиеся в указанном каталоге. Дважды щелкните одно из имен — на экране появится окно, сообщающее время создания и послед-него изменения данного файла.

Главная форма ControlDemo (рис. 18) — это экземпляр класса MyForm. Конструктор MyForm создает элементы управления и сохраняет ссылки на них в закрытых полях. Затем он добавляет элементы управления к набору Controls формы, что приведет к их появлению в окне формы. Обработчики событий OnShowFileNames и OnShowFilelnfo реагируют на событие Click от командной кнопки и DoubleClick от окна списка. Эти обработчики используют статические методы классов Directory и File из пространства имен SystemIO для составления спи-ска файлов и получения информации о них.

Обратите внимание на операторы присваивания числовых значений свойству Tablndex элементов управления TextBox, ListBox и Button. Tablndex определяет порядок перехода фокуса ввода от одного элемента управления к другому при нажатии пользователем клавиши Tab. Код перемещения фокуса реализован инфраструктурой, ваша задача — только задать порядок. Элементу управления Label порядковый номер не присваивается, так как этот элемент никогда не получает фокус ввода.

Puc. 17. Отображение информации о файле в программе ControlDemo

Листинг 9.7. ControlDemo.cs 1: using System; // Prosiz, гл. 4, стр. 119, ControlDemo 2: using System.Windows.Forms;

Page 36: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

36

3: using System.Drawing; 4: using System.IO; 5: 6: namespace WindAppl_Pros_Chap4_C119_ContrDemo 7: { 8: class MyForm : Form 9: { 10: Label PathNameLabel; 11: TextBox PathNameBox; 12: ListBox FileNameBox; 13: Button ShowFileNamesButton; 14: 15: string CurrentPath; 16: 17: MyForm() 18: { 19: // Задать название формы 20: Text = "Демонстрация элементов управления"; 21: 22: // Задать размер формы 23: ClientSize = new Size(256, 248); 24: 26: // Создать элементы управления (см. в таблице выше их перечень) 27: PathNameLabel = new Label(); // См. п. 10 в таблице 28: PathNameLabel.Location = new Point(16, 16); 29: PathNameLabel.Size = new Size(224, 16); 30: PathNameLabel.Text = "Имя пути"; 31: 32: PathNameBox = new TextBox(); // См. п. 24 в таблице 33: PathNameBox.Location = new Point(16, 32); 34: PathNameBox.Size = new Size(224, 24); 36: PathNameBox.TabIndex = 1; 37: 38: FileNameBox = new ListBox(); // См. п. 12 в таблице 39: FileNameBox.Location = new Point(16, 72); 40: FileNameBox.Size = new Size(224, 128); 41: FileNameBox.TabIndex = 2; 42: FileNameBox.DoubleClick += new EventHandler(OnShowFileInfo); 43: 44: ShowFileNamesButton = new Button(); // См. п. 1 в таблице 46: ShowFileNamesButton.Location = new Point(112, 208); 47: ShowFileNamesButton.Size = new Size(128, 24); 48: ShowFileNamesButton.Text = "Отображение имени файла" 49: ShowFileNamesButton.TabIndex = 3; 50: ShowFileNamesButton.Click += new EventHandler(OnShowFileNames); 51: 52: 53: // Добавить элементы управления к форме

Page 37: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

37

54: Controls.Add(PathNameLabel); 55: Controls.Add(PathNameBox); 56: Controls.Add(ShowFileNamesButton); 57: Controls.Add(FileNameBox); 58: } 59: 60: // Обработчик события кнопки Show File Names 61: void OnShowFileNames(object sender, EventArgs e) 62: { 63: // Получить имя пути из поля ввода 64: string path = PathNameBox.Text; 65: 66: if (path.Length > 0) 67: { 68: // Убедиться в существовании каталога 69: if (Directory.Exists(path)) 70: { 71: // Очистить окно списка 72: FileNameBox.Items.Clear(); 73: 74: // Получить список всех файлов каталога 75: string[ ] files = Directory.GetFiles(path); 76: 77: 78: // Поместить имена файлов в окно списка ( ListBox ) 79: foreach (string file in files) 80: { 81: FileAttributes attr = File.GetAttributes(file); 82: if ((attr & FileAttributes.Hidden) == 0) 83: FileNameBox.Items.Add(Path.GetFileName(file)); 84: } 85: 86: // Запомнить имя пути, которое понадобится, 87: // если пользователь дважды кликнет имя файла 88: CurrentPath = Path.GetFullPath(PathNameBox.Text); 89: } 90: // Сообщить пользователю, что заданный путь неверен 91: else 92: MessageBox.Show(path + " Неправильный путь", 93: "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error); 94: } 95: } 96: 97: // Обрабочик событий DoubleClick от окна списка ( ListBox ) 98: void OnShowFileInfo(object sender, EventArgs e) 99: {

100: // Создать полное имя пути по имени файла, 101: // которое дважды кликнул пользователь

Page 38: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

38

102: string file = CurrentPath; 103: if (!file.EndsWith(":") && !file.EndsWith("\\")) 104: file += "\\"; 105: file += FileNameBox.SelectedItem.ToString(); 106: 107: // Отобразить дату и время создания 108: // и последнего изменения файла 109: DateTime created = File.GetCreationTime(file); 110: DateTime modified = File.GetLastWriteTime(file); 111: 112: string msg = "Создан: " + created.ToLongDateString() + 113: " at " + created.ToLongTimeString() + "\n" + 114: "Модифицирован: " + modified.ToLongDateString() + 115: " at " + modified.ToLongTimeString(); 116: 117: MessageBox.Show(msg, FileNameBox.SelectedItem.ToString(), 118: MessageBoxButtons.OK, MessageBoxIcon.Information); 119: } 120: 121: static void Main() 122: { 123: Application.Run(new MyForm()); 124: } 126: } 127: }

Рис. 18. Исходный код программы ControlDemo

1.8. Привязки Когда разработчики Windows Forms в Редмонде корпели над своей частью проекта .NET Framework, они реши-ли добавить немного украшений ( «бантиков» ). Одним из таких украшений являются привязки (anchoring). При-вязки позволяют создавать формы, элементы управления которых изменяют свои размеры и положение при изменении размеров формы. На рис. 4-19 показан внешний вид ControlDemo после увеличения размеров окна формы. Хотя форма стала больше, элементы управления сохранили свои первоначальные размеры. На рис. 4-20 показана та же форма с применением привязок. Теперь геометрия элементов управления следует за раз-мером формы. Привязки применяются к каждому элементу управления по отдельности. Для этого надо инициализировать свойство Anchor, унаследованное элементом управления от класса Control. Привязка может быть задана как произвольное сочетание приведенных ниже значений, указывающих поведение элемента управления при из-менении размеров его контейнера.

Стили привязки Стиль привязки Назначение AncborStylesLeft Поддерживает постоянное расстояние между левым краем элемента управления и

левым краем формы

AnchorStylesRigbt Поддерживает постоянное расстояние между правым краем элемента управления и правым краем формы.

AncborStyles.Top Поддерживает постоянное расстояние между верхним краем элемента управления и верхним краем формы.

AnchorStyles Bottom

Поддерживает постоянное расстояние между нижним краем элемента управления и нижним краем формы

AnchorStylesNone Ничего не делает.

Page 39: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

39

Чтобы элемент управления перемещался вправо при увеличении ширины своего контейнера, привяжите его правый край к правому краю формы:

MyControl.Anchor = AnchorStyles.Right; Чтобы при увеличении ширины формы ширина элемента управления тоже увеличивалась, привяжите и пра-вый, и левый края:

MyControl.Anchor = AnchorStyles.Left ! AnchorStyles.Right;

Puc. 19. ControlDemo без привязок

Puc. 20. ControlDemo с привязками

Комбинируя разные стили привязок, можно настроить желаемое поведение элемента управления при измене-нии размеров формы. Чтобы получить представление о работе привязок, добавьте следующие операторы в конструктор MyForm в ControlDemo.cs:

PathNameBox.Anchor = AnchorStyles.Left & AnchorStyles.Right & AnchorStyles.Top; FileNameBox.Anchor = AnchorStyles.Left & AnchorStyles.Right & AnchorStyles.Bottom & AnchorStyles.Top;

Page 40: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

40

ShowFileNamesButton.Anchor = AnchorStyles.Right & AnchorStyles.Bottom; PathNameLabel.Anchor = AnchorStyles.Left & AnchorStyles.Right & AnchorStyles.Bottom & AnchorStyles.Top; Результаты показаны на рис. 20: размеры элемента управления TextBox изменяются по горизонтали при изме-нении ширины формы, но его положение фиксировано относительно верхнего края формы. Элемент управле-ния ListBox при изменении размеров формы изменяет как ширину, так и высоту, а размеры элемента управле-ния Button остаются неизменными, но сам он перемещается вместе с правым нижним углом формы,

1.9. Диалоговые окна Диалоговое окно — это форма, применяемая для сбора данных, вводимых пользователем. Диалоговые окна, или, как их часто называют, «диалоги» бывают модальными и немодальными. При отображении на экране мо-дального диалога его владелец, которым обычно является главная форма приложения, временно становится неактивным. Окно "Открыть файл*, используемое в несчетном количестве Windows-приложений, — пример мо-дального диалога. Немодальные диалоги не делают своих владельцев неактивными — пользователь может в любой момент перейти к главной форме, не закрывая такой диалог. Пример такого диалога — окно «Найти и заменить» в Microsoft Word.

Для опытных программистов, использующих Windows API, диалоговые окна — это особые создания, достаточ-но отличающиеся от обычных окон, чтобы доставить неудобства. В Windows Forms диалоговые окна не имеют никаких особенностей — это обычные формы. Буквально. Фактически только три характеристики отличают форму диалогового окна от обычной формы:

• стили диалогов немного отличаются от стилей обычных форм; например, у них редко бывают кнопки «Свер-нуть» и «Развернуть», поэтому свойства формы MinimizeBox и MaximizeBox для диалогов обычно равны false; • обычно в диалогах есть кнопки «ОК» и «Отмена»; пара строк кода в диалоге Windows Forms позволяет сооб-щить инфраструктуре об этих кнопках и позволить ей автоматически закрыть диалог по нажатию на них; • чтобы отобразить на экране обычную форму, вызывается ее метод Show, а модальный диалог — ShowDialog.

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

Windows предоставляет набор системных или стандартных диалогов, которые можно задействовать в при-ложениях для вывода диалогов открытия файла, печати и т. п. FCL предоставляет удобные классы стандарт-ных диалогов, перечисленные в таблице. Все эти классы определены в пространстве имен System.Windows.Forms. Один из этих диалогов — OpenFileDialog — мы с вами уже встречали выше в ImageView. Эти классы позволят вам сэкономить время разработки и обеспечат вашему приложению внешний вид и пользовательский интерфейс, совместимый с другими Windows-приложениями.

Классы стандартных диалогов, определенные в System.Windows.Forms

Класс Тип диалога ColorDialog Диалоги выбора цвета

FontDialog Диалоги выбора шрифтов

OpenFileDialog Диалоги для выбора файлов

PageSetupDialog Диалоги настройки параметров печатной страницы

PrintDialog Диалоги настройки параметров печати

SaveFileDialog Диалоги ввода имен файлов для сохранения

1.9.1. Приложение DialogDemo На рис. 22 показан исходный код приложения Windows Forms, отображающего диалоговое окно при выборе команды меню. Чтобы отобразить на экране диалог, выберите пункт Edit Ellipse из меню Options. Элементы

Page 41: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

41

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

ClientSize = new Size(296, 196); Второй задает значение свойства StartPosition, центрирующее диалог относительно родительской формы при каждом его запуске:

StartPosition = FormStartPosition.CenterParent; Третий задает тип рамки FixedDialog, не позволяющий изменять размеры диалога:

FormBorderStyle = FormBorderStyle.FixedDialog; Следующие два оператора удаляют кнопки «Свернуть» и «Развернуть», отображаемые в заголовке диалогово-го окна по умолчанию, а оператор, идущий за ними, задает текст названия диалога:

MaximizeBox = false; MinimizeBox = false; Text = "Edit Ellipse"; Последний оператор отключает вывод на панель задач кнопки для этого окна:

ShowInTaskbar = false;

В отличие от форм верхнего уровня диалоги не должны появляться на панели задач. Большая часть остально-го кода конструктора MyDialog создает элементы управления диалога и добавляет их в набор Controls. Однако следует обратить внимание на два оператора:

AcceptButton = OKButton; CancelButton = NotOKButton; AcceptButton и CancelButton — это свойства, унаследованные MyDialog от класса Form. AcceptButton указывает кнопку «OK» диалога, CancelButton — кнопку «Отмена». Это особенные кнопки, так как их нажатие закрывает диалог — заставляет его исчезнуть с экрана. Приведенные выше операторы задают кнопки «ОК» и «Отмена», чтобы инфраструктура могла закрыть диалог автоматически по нажатию на любую из них.

Следующие операторы создают диалог и отображают его на экране:

MyDialog dlg = new MyDialog(); …. if (dlg.ShowDialog (this) == DialogResult.OK) { Диалог появляется на экране при вызове ShowDialog. Вызов ShowDialog — блокирующий; эта функция не воз-вращает управления, пока диалог не закроется. Ее возвращаемое значение указывает, как был закрыт диалог. DialogResult.OK означает, что пользователь щелкнул кнопку «ОК», DialogBox.Cancel — что он щелкнул «Отме-на», В последнем случае большинство приложений игнорирует введенные в диалоге данные. Если ShowDialog возвращает значение, отличное от DialogResult.OK, то DialogDemo игнорирует введенные данные, что дает пользователю возможность отменить диалог, сохранив внешний вид эллипса неизменным.

Доступ к данным, введенным пользователем, осуществляется через открытые свойства класса MyDialog: UserWidth, UserHeight и UserUnits. Считывая эти свойства, они возвращают данные элементов управления. При записи в эти свойства данные переносятся в элементы управления. Прежде чем отобразить диалог на экране вызовом ShowDialog, приложение DialogDemo инициализирует его элементы управления, устанавливая соот-ветствующие им свойства:

dig.UserWidth = MyWidth; dig.UserHeight = MyHeight; dlg.UserUnits = (int) MyUnits;

Page 42: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

42

А когда диалог закрывается (если это произошло по нажатии кнопки OK), DialogDemo получает введенные пользователем данные, считывая значения свойств MyDialog:

MyWidth = dig.UserWidth; HyHeight = dig.UserHeight; MyUnits = (MyForm.Units) dig.UserUnits;

Таким образом, открытые свойства диалогового класса служат механизмом передачи данных между програм-мой и элементами управления диалога. В приложениях Windows Forms это типичный механизм обмена данны-ми с элементами управления диалоговых окон.

Рис. 21а. Программа DialogDemo

Рис. 21б. Программа DialogDemo

Page 43: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

43

Листинг 9.8. DialogDemo.cs Рис. 22. Исходный текст программы DialogDemo 1: using System; // Prosiz, Глава 4, Приложение DialogDemo, с. 124 2: using System.Drawing; 3: using System.Windows.Forms; 4: 5: namespace WindAppl_Pros_Chap_c124_DialogDemo 6: { 7: public class MyForm : Form 8: { 9: enum Units 10: { 11: Inches, 12: Centimeters, 13: Pixels 14: } 15: 16: int MyWidth = 200; 17: int MyHeight = 400; 18: Units MyUnits = Units.Pixels; 19: 20: public MyForm() // конструктор класса 21: { // Установите название формы 22: Text = "Демонстрация диалога"; 23: 24: ClientSize = new Size(640, 480);// Установите размер формы 26: 27: MainMenu menu = new MainMenu(); // Создайте меню 28: MenuItem item = menu.MenuItems.Add("Oпции"); 29: item.MenuItems.Add("Редактировать эллипс ...", 30: new EventHandler(OnEditEllipse)); 31: item.MenuItems.Add("-"); 32: item.MenuItems.Add("Выход", new EventHandler(OnExit)); 33: 34: Menu = menu; // Подсоедините меню к форме 36: } 37: 38: // 4-й шаг - ОБРАБОТЧИК события 39: //для команды Редактировать Эллипс - EditEllipse 40: void OnEditEllipse(object sender, EventArgs e) 41: { 42: MyDialog dlg = new MyDialog(); // Создайте диалог 43: 44: dlg.UserWidth = MyWidth; // Инициализируйте диалог 46: dlg.UserHeight = MyHeight; 47: dlg.UserUnits = (int)MyUnits; 48: // Отобразите диалог на дисплее 49: if (dlg.ShowDialog(this) == DialogResult.OK) 50: {

Page 44: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

44

51: // Если была нажата кнопка ОК, получить введенные, - 52: // пользователем данные и перерисовать форму 53: MyWidth = dlg.UserWidth; 54: MyHeight = dlg.UserHeight; 55: MyUnits = (MyForm.Units) dlg.UserUnits; 56: Invalidate(); 57: } 58: dlg.Dispose(); // Избавьтесь от диалога 59: } 60: 61: // 4-й шаг - ОБРАБОТЧИК события для команды Exit 62: void OnExit(object sender, EventArgs e) 63: { 64: Close(); 65: } 66: // 4-й шаг - ОБРАБОТЧИК события OnPaint 67: protected override void OnPaint(PaintEventArgs e) 68: { 69: int multiplier = 1; 70: 71: switch (MyUnits) 72: { 73: 74: case Units.Inches: 75: e.Graphics.PageUnit = GraphicsUnit.Inch; 76: break; 77: 78: case Units.Centimeters: 79: e.Graphics.PageUnit = GraphicsUnit.Millimeter; 80: multiplier = 10; 81: break; 82: 83: case Units.Pixels: 84: e.Graphics.PageUnit = GraphicsUnit.Pixel; 85: break; 86: } 87: 88: SolidBrush brush = new SolidBrush(Color.Magenta); 89: e.Graphics.FillEllipse(brush, 0, 0, MyWidth * multiplier, MyHeight * multiplier); 90: brush.Dispose(); 91: } 92: } /////////////////////////// конец класса MyForm : Form ////////////////////////// 93: 94: 95: class MyDialog : Form /////////////// начало класса MyDialog: Form ////////////////// 96: { 97: Label WidthLabel; 98: Label HeightLabel;

Page 45: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

45

99: TextBox WidthBox; 100: TextBox HeightBox; 101: GroupBox UnitsGroup; 102: RadioButton InchesButton; 103: RadioButton CentimetersButton; 104: RadioButton PixelsButton; 105: Button OKButton; 106: Button NotOKButton; 107: 108: public int UserWidth 109: { 110: get { return Convert.ToInt32(WidthBox.Text); } 111: set { WidthBox.Text = value.ToString(); } 112: } 113: 114: public int UserHeight 115: { 116: get { return Convert.ToInt32(HeightBox.Text); } 117: set { HeightBox.Text = value.ToString(); } 118: } 119: 120: public int UserUnits 121: { 122: get 123: { 124: for (int i = 0; i < UnitsGroup.Controls.Count; i++) 126: { 127: RadioButton button = (RadioButton)UnitsGroup.Controls[i]; 128: if (button.Checked) return i; 129: } 130: return -1; 131: } 132: set 133: { 134: RadioButton button = (RadioButton)UnitsGroup.Controls[value]; 136: button.Checked = true; 137: } 138: } 139: 140: public MyDialog() // конструктор класса 141: { 142: // Инициализировать параметры отображения диалога. 143: ClientSize = new Size(296, 196); 144: StartPosition = FormStartPosition.CenterParent; 146: FormBorderStyle = FormBorderStyle.FixedDialog; 147: MaximizeBox = false; 148: MinimizeBox = false; 149: Text = "Edit Ellipse";

Page 46: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

46

150: ShowInTaskbar = false; 151: // Создать элементы управления диалога 152: WidthLabel = new Label(); 153: WidthLabel.Location = new Point(16, 16); 154: WidthLabel.Size = new Size(48, 24); 155: WidthLabel.Text = "Width"; 156: 157: HeightLabel = new Label(); 158: HeightLabel.Location = new Point(16, 48); 159: HeightLabel.Size = new Size(48, 24); 160: HeightLabel.Text = "Height"; 161: 162: WidthBox = new TextBox(); 163: WidthBox.Location = new Point(64, 12); 164: WidthBox.Size = new Size(96, 24); 165: WidthBox.TabIndex = 1; 166: 167: HeightBox = new TextBox(); 168: HeightBox.Location = new Point(64, 44); 169: HeightBox.Size = new Size(96, 24); 170: HeightBox.TabIndex = 2; 171: 172: UnitsGroup = new GroupBox(); 173: UnitsGroup.Location = new Point(16, 76); 174: UnitsGroup.Size = new Size(144, 100); 175: UnitsGroup.Text = "Units"; 176: 177: InchesButton = new RadioButton(); 178: InchesButton.Location = new Point(16, 24); 179: InchesButton.Size = new Size(112, 16); 180: InchesButton.Text = "Inches"; 181: 182: CentimetersButton = new RadioButton(); 183: CentimetersButton.Location = new Point(16, 48); 184: CentimetersButton.Size = new Size(112, 16); 185: CentimetersButton.Text = "Centimeters"; 186: 187: PixelsButton = new RadioButton(); 188: PixelsButton.Location = new Point(16, 72); 189: PixelsButton.Size = new Size(112, 16); 190: PixelsButton.Text = "Pixels"; 191: 192: OKButton = new Button(); 193: OKButton.Location = new Point(184, 12); 194: OKButton.Size = new Size(96, 24); 195: OKButton.TabIndex = 3; 196: OKButton.Text = "OK"; 197: OKButton.DialogResult = DialogResult.OK;

Page 47: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

47

198: 199: NotOKButton = new Button(); 200: NotOKButton.Location = new Point(184, 44); 201: NotOKButton.Size = new Size(96, 24); 202: NotOKButton.TabIndex = 4; 203: NotOKButton.Text = "Cancel"; 204: NotOKButton.DialogResult = DialogResult.Cancel; 205: 206: AcceptButton = OKButton; 207: CancelButton = NotOKButton; 208: 209: //Добавьте элементы управления к диалогу 210: Controls.Add(WidthLabel); 211: Controls.Add(HeightLabel); 212: Controls.Add(WidthBox); 213: Controls.Add(HeightBox); 214: Controls.Add(UnitsGroup); 215: UnitsGroup.Controls.Add(InchesButton); 216: UnitsGroup.Controls.Add(CentimetersButton); 217: UnitsGroup.Controls.Add(PixelsButton); 218: Controls.Add(OKButton); 219: Controls.Add(NotOKButton); 220: } 221: } /////////////////////////////////// конец класса MyDialog: Form ///////////////////////////////// 222: 223: class EllipsTest /////////// начало класса EllipsTest///////////////////// 224: { 226: static void Main() ////////////// метод Main() 227: { 228: Application.Run(new MyForm()); 229: } 230: } 231: ////////////////////////////////// конец класса EllipsTest//////////////////////////////////////////// 232: }

Page 48: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

48

2. Windows Forms и Visual Studo .NET 2003 Построение приложений вручную — отличный способ изучить внутреннюю механику Windows Forms, но на практике большинство разработчиков приложений Windows Forms используют средства автоматизации разра-ботки Visual Studio .NET. Visual Studio .NET — это версия Visual Studio для разработчиков, пишущих для .NET Framework. Среди обширного списка ее возможностей — интегрированные редактор, компилятор и отладчик, контекстно-зависимая справка с использованием механизма IntclliSense, а также встроенный дизайнер форм, упрощающий задачу создания внешнего вида и реализации Windows-форм. Чтобы познакомить с разработкой Windows Forms с использованием Visual Studio .NET в оставшиеся разделы включено пошаговое описание соз-дания весьма полезного приложения Windows Forms — калькулятора, в режиме обратной польской нотации (ОПН) способного выполнять сложение, вычитание, умножение и деление (рис. 23).

Рис. 23. Приложение NetCalc

Если вы не знакомы с обратной польской нотацией, вот краткое введение. Чтобы сложить 2 и 2 на обычном калькуляторе, вы нажимаете следующие кнопки:

2 + 2 =

А чтобы сложить эти числа накалькуляторе ОПН, нужно ввести:

2 Enter

2 +

В результате нажатия двух первых кнопок «2» и «Ввод» значение 2 помещается во внутренний вычислитель-ный стек калькулятора. Второе нажатие кнопки «2» помещает в стек еще одну двойку, перемещая предыдущую на одну ячейку глубже. Нажатие кнопки «+» извлекает из стека обе двойки, складывает их и помещает резуль-тат обратно в стек. В окне результатов калькулятора отображается 4, так как здесь всегда отображается зна-чение на вершине стека. Одно из преимуществ калькуляторов ОПН в том, что скобки не нужны и даже сложные выражения могут быть вычислены с минимальными усилиями. Допустим, нужно вычислить выражение:

((2 + 2) * 6) / 3 Его непросто вычислить на обычном калькуляторе (особенно, не поддерживающем скобки), но не на калькуля-торе ОПН. Вот последовательность нажатия клавиш:

2 Enter

2 + 6 * 3 /

Page 49: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

49

ОПН была популяризована в 1970-х калькуляторами для научных расчетов фирмы Hewlett-Packard. Научив-шись считать с помощью ОПН, вы уже никогда не захотите использовать обычный калькулятор. Но хватит о достоинствах ОПН. Перейдем к самой программе.

Шаг 1: Создание проекта NetCalc Первым шагом создания приложения Windows Forms в Visual Studio.NET является создание проекта. Выберите команду New/Project из меню File Visual Studio .NET. Убедитесь, что в появившемся на экране диалоге New Project в окне Project Types выбрано Visual C# Project (рис. 24). Затем в качестве типа создаваемого приложе-ния выберите Windows Application. Введите «NetCalc» в поле Name и щелкните кнопку ОК.

Puc. 24. Создание проекта Windows Forms на С#

Шаг 2: Разработка формы После создания проекта Visual Studio .NET автоматически отобразит дизайнер форм — визуальный инстру-мент, позволяющий добавлять к форме элементы управления, задавать значения их свойств, писать обработ-чики событий и т. д. Первоначально в дизайнере форм отображается пустая форма. Теперь надо задать на-звание формы, отображаемое в ее заголовке. По умолчанию это «Form1». Найдите свойство Text формы в ок-не Properties (по умолчанию это окно отображается в правом нижнем углу главного окна Visual Studio .NET) и введите вместо «Form1» строку «NetCalc». Новое название должно появиться в заголовке окна в дизайнере форм. Не выходя из окна Properties, запретим изменение размеров формы, установив ее свойство FormBorderStyle в FixedDialog, и отключим кнопку «Развернуть», установив MaximizeBox в false. Вот сводка свойств, которые нужно изменить:

Свойство Значение Text «NetCalc» FormBorderStyle FixedDialog MaximizeBox False

Слева в окне Visual Studio .NET находится вертикальная кнопка «Toolbox». Если на миг задержать указатель мыши на этой кнопке, на экране появится список элементов управления, которые могут быть добавлены к Windows-форме, Создайте форму выбрав из списка элементы управления Button и TextBox и разместив их на форме (рис. 25). Затем в окне Properties назначьте элементу управления TextBox такие свойства:

Свойство Значение TextAlign Right TabStop False

Page 50: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

50

Text «0.00» Readonly True BackColor Window Cursor Default Name «Display» Font Microsoft Sans Serif,

10 пунктов

Рис. 25. Форма NetCalc с элементами управления Button (19) и TextBox (1)

Далее с помощью окна Properties (рис. 26) назначьте следующие значения свойствам Text и Name кнопок:

Text Name Text Name «Enter» «EnterButton» «1» «OneButton» «Fix» «FixButton» «2» «TwoButton»

«Clear» «ClearButton» «3» «ThreeButton» «Del» «DeleteButton» «4» «FourButton»

«,» «DecimalButton» «5» «FiveButton» «.» «SubtractButton» «6» «SixButton» «+» «AddButton» «7» «SevenButton» «*» «MultiplyButton» «8» «EightButton»

«/» «DivideButton» «9» «NineButton»

«0» «ZeroButton»

Установите размер шрифта для кнопок «+», «-», «х» и «/» в 12 пт и в заключение установите свойство TabStop для всех элементов управления в false. Такое значение свойства TabStop для всех элементов управления (включая TextBox) не позволяет использовать клавишу табуляции для перемещения фокуса ввода. Иногда поддержка перехода между элементами управления с помощью клавиши табуляции полезна. Однако на шаге 7 мы снабдим NetCalc собственным клавиатурным интерфейсом, гораздо более удобным, чем просто поддержка перемещения фокуса ввода.

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

Page 51: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

51

Рис. 26. Форма NetCalc в дизайнере форм Visual Studio.NET

Шаг 3: Добавление полей Добавьте следующие закрытые поля к классу Form1, представляющему форму, созданную нами на шаге 2. Этот класс, производный от System.Windows.Forms.Form, был создан автоматически при создании проекта:

Тип Название Начальное значение Slack RegStack new Stack () string FormatString «f2» bool FixPending False bool DecimalInString False bool EntryInProgress False const int MaxChars 21

Поля можно добавить вручную или с помощью Visual Studio.NET: щелкните вкладку Class View, которая (по умолчанию) отображается рядом с правым нижним краем окна Visual Studio .NET, чтобы отобразить список классов проекта. Затем щелкните правой кнопкой класс Form1 и выберите из контекстного меню Add/Add Field. В появившемся мастере Add Wizard введите сведения о новом поле. Когда вы закончите, в программе должны появиться операторы объявления и инициализации полей:

private Stack RegStack = new Stack(); private string FormatString = "f2"; private bool FixPending = false; private bool DecimallnString = false; private bool EntrylnProgress = false; private const int MaxChars = 21; Если вы создали эти операторы с помощью мастера Add Field, то инициализацию, например «false», вам при-дется добавить вручную, так как мастер не генерирует инициализаторы для полей, не объявленных как const. Поле RegStack является экземпляром класса System.Collections.Stack. Оно представляет внутренний стек калькулятора. Мы будем использовать методы Push и Pop, чтобы помещать значения в стек и извлекать их из него, как в настоящем ОПН-калькуляторе. Вам нет нужды вручную писать в Form1.cs: using System.Collections, так как Visual Studio.NET добавила этот оператор автоматически.

Page 52: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

52

Шаг 4: Изменение конструктора класса формы Откройте Form.cs и найдите конструктор класса Form1. В теле конструктора есть комментарий, предлагающий добавить дополнительный код инициализации после вызова InitializeComponent:

// // TODO: Add any constructor code after InitializeComponent call – // Добавьте любой код в тело конструктора после вызов InitializeComponent

После комментария ( // TODO: … ) добавьте операторы:

RegStack.Push(0.0); DisplayXRegister(); Первый оператор Push инициализирует внутренний стек калькулятора, помещая в него 0. Второй оператор инициализирует дисплей калькулятора, копируя значение из регистра X (самый верхний элемент стека) в эле-мент управления TextBox.

Шаг 5: Добавление вспомогательных методов Добавьте вспомогательные методы к классу Form1:

Название метода Возвращаемый тип Параметры ProcessDigit void int Value Reset void Heт ConditionalResetDisplay void Heт InitializeXRegisterFromDisplay void Heт DisplayXRegister void Heт ReportError void string Message

Вы можете добавить эти методы вручную (в Form.cs) или щелкнув правой кнопкой Form1 в окне Class View и выбрав команду Add/Add Method. Сделайте все эти методы закрытыми ( private ). Заполните тела методов ко-дом, показанным для окончательных вариантов методов на рис. 27. Если вы выбрали команду AddMethod, Visual Studio.NET вставляет пустые тела методов автоматически. Ввод кода метода между фигурными скобка-ми предоставляется вам (NB).

Шаг 6: Добавление обработчиков событий Click Вернитесь в дизайнер форм и дважды щелкните каждую из 19 командных кнопок формы. Каждый двойной щелчок добавляет к Form1 обработчик события Click и связывает обработчик с кнопкой. Названиями обработ-чиков будут OneButton_Click, TwoButton_Click и т. д. Заполните пустые тела методов кодом ( этот код выделен курсивом ), показанным на рис. 27.

Шаг 7: Добавление обработчиков для клавиатуры Теперь NetCalc — вполне функционирующее приложение, но без клавиатурного интерфейса. Удобный кальку-лятор обязан поддерживать ввод с клавиатуры. Поэтому вернитесь в дизайнер форм и измените свойство формы KeyPreview с false на true. Это позволяет форме «видеть» нажатия на клавиши до того, как их «увидят» ее элементы управления, даже если один из них имеет в данный момент фокус ввода. Затем для перехвата событий от клавиатуры переопределите методы класса Form1:

Название Возвращаемый тип Параметры OnKeyPress void KeyPressEventArgs e OnKeyDown void KeyEventArgs e

Как обычно, методы можно добавить вручную или командой AddMethod. Пометьте все эти методы как protected. Если вы выбрали AddMethod, обязательно установите флажок Override, чтобы включить ключевое

Page 53: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

53

слово override в объявление метода. В заключение добавьте реализацию этих методов, как вы делали это на шаге 6.

Шаг 8: Переопределение ProcessDialogKey Собрав и запустив NetCalc, вы увидите, что клавиатурный интерфейс работает, только если вы пользуетесь исключительно клавиатурой. Например, если вы щелкнули кнопку «2», а затем нажали «Ввод» на клавиатуре, в окне калькулятора появится еще одна двойка. Это происходит потому, что щелчок устанавливает фокус ввода на кнопку 2, а когда фокус установлен на кнопку, система интерпретирует нажатие клавиши «Ввод» как щелчок кнопки. Проблема решается путем переопределения малоизвестного метода ProcessDialogKey, который Form1 унаследовал от Form. Он вызывается инфраструктурой при нажатии некоторых клавиш (например, «Ввод»), чтобы дать форме возможность обработать их. Вы должны переопределить этот метод таким образом: protected override bool ProcessDialogKey(Keys keyData) { return keyData == Keys.Enter ? false : base.ProcessDialogKey (keyData); } Эта реализация метода выполняет обработку по умолчанию для клавиш, отличных от «Ввод», путем вызова реализации ProcessDialogKey из базового класса. Но когда нажата клавиша «Ввод», ProcessDialogKeys не вы-зывает базовый класс, отключая тем самым обработку этой клавиши по умолчанию. В результате клавиша «Ввод» становится просто еще одной клавишей на клавиатуре, и нажатие на нее активизирует обработчик OnKeyDown, даже если в данный момент фокус ввода установлен на каком-то из элементов управления.

Шаг 9: Компоновка и запуск приложения Соберите приложение NetCalc, выбрав команду Build из меню Build Visual Studio.NET, Запустите его командой Start Without Debugging из меню Debug (или нажав эквивалентную быструю клавишу Ctrl+F5). Выполнив не-сколько операций сложения, вычитания, умножения и деления, убедитесь, что программа работает правильно. Кроме того, измените точность отображения на дисплее калькулятора с двух цифр в дробной части на четыре, щелкнув кнопку Fix и затем кнопку 4. Если справа от десятичной точки появятся четыре цифры, значит, у вас получилось.

Исходный текст приложения NetCalc Законченная версия NetCalc.cs приведена на Листинге 9.9. Строки добавленные программистом-разработ-чиком проекта, выделены курсивом, а все остальные строки сгенерированы Visual Studio.NET. Отметим сле-дующее: 1. В начале файла вы видите операторы, объявляющие экземпляры Button и TextBox как закрытые (private)

поля (см. cтроки 15…36). Эти операторы были добавлены, когда вы добавили на форму элементы управ-ления в дизайнере форм (шаг 2, рис. 25).

2. Часть конструктора Form1() класса Form1, сгенерированная Дизайнером форм, содержит вызов метода InitializeComponent (см. cтроку 48), который создает экземпляры элементов управления Button и TextBox и инициализирует значения их свойств. Большая часть этого кода была добавлена при размещении элемен-тов управления на форме. Оставшаяся часть была добавлена, когда вы редактировали свойства элемен-тов управления в окне Properties (рис. 26).

3. InitializeComponent также содержит операторы, связывающие элементы управения Button с обработчиками события Click. Вот один из этих операторов (см. строку 252 и др.): this.MultiplyButton.Click += new System.EventHandler(this.MultiplyButton_Click); Эти операторы добавлялись, когда вы дважды щелкали кнопки в окне дизайнера форм (шаг 6).

4. Ближе к концу InitializeComponent есть единственный оператор, добавляющий все элементы управления к форме. Вместо того чтобы последовательно вызывать Add, Дизайнер форм добавил все элементы управ-ления в один присест, вызвав AddRange для набора Controls формы (см. строки 319…343).

5. Обработчик Click цифровых кнопок калькулятора обращается к вспомогательному методуг ProcessDigit, который: а) добавляет цифру к числу, отображаемому в окне калькулятора, или б) меняет точность

Page 54: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

54

отображения (см. строки 467…515). Щелчок кнопки Fix переключает внутренний флаг FixPending, указы-вающий (см. строки 605…608), как им интерпретировать следующую вводимую цифру: как значение точ-ности или как часть вводимого числа.

6. Щелчок кнопки Enter вызывает EnterButton_Click, который помещает отображаемое на экране калькулятора число во внутренний стек калькулятора (см. строки 595…600).

Щелчок любой кнопки с арифметическим действием (“+”, “-”, “/”, “*”) вызывает выполнение соответствую-щей арифметической операции над двумя первыми значениями, извлекаемыми из стека. Если при нажатии одной из этих кнопок число, отображаемое на дисплее калькулятора, не находится в стеке, оно туда помеща-ется и служит операндом следующего вычисления (см. строки 519…590).

Листинг 9.9. NetCalc.cs 1: using System; // Prosiz, гл. 4, стр. 131...139..., NetCalc 2: using System.Drawing; 3: using System.Collections; 4: using System.ComponentModel; 5: using System.Windows.Forms; 6: using System.Data; 7: 8: namespace WindAppl_Pros_Chap4_c131_NetCalc 9: { 10: /// < резюме> 11: /// Итоговое описание для Form1 12: /// < резюме > 13: public class Form1 : System.Windows.Forms.Form 14: { 15: private System.Windows.Forms.Button FixButton; 16: private System.Windows.Forms.Button EnterButton; 17: private System.Windows.Forms.TextBox Display; 18: private System.Windows.Forms.Button ClearButton; 19: private System.Windows.Forms.Button SubtractButton; 20: private System.Windows.Forms.Button SevenButton; 21: private System.Windows.Forms.Button EightButton; 22: private System.Windows.Forms.Button NineButton; 23: private System.Windows.Forms.Button FiveButton; 24: private System.Windows.Forms.Button FourButton; 26: private System.Windows.Forms.Button AddButton; 27: private System.Windows.Forms.Button SixButton; 28: private System.Windows.Forms.Button ThreeButton; 29: private System.Windows.Forms.Button MultiplyButton; 30: private System.Windows.Forms.Button OneButton; 31: private System.Windows.Forms.Button TwoButton; 32: private System.Windows.Forms.Button ZeroButton; 33: private System.Windows.Forms.Button DivideButton; 34: private System.Windows.Forms.Button DeleteButton; 36: private System.Windows.Forms.Button DecimalButton; 37: /// <summary> 38: /// components - переменная Дизайнера 39: /// </summary> 40: private System.ComponentModel.Container components = null;

Page 55: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

55

41: 42: public Form1() // конструктор класса Form1 43: { 44: // 46: // Необходимо для поддержки Дизайнера Windows Form 47: // 48: InitializeComponent(); 49: 50: // 51: // TODO: Добавьте любой код в тело конструктора после вызова 52: // InitializeComponent 53: RegStack.Push(0.0); 54: DisplayXRegister(); 55: } 56: 57: /// <summary> 58: /// Удапение любых использованных ресурсов – очистка Memory. 59: /// </summary> 60: protected override void Dispose(bool disposing) 61: { 62: if (disposing) 63: { 64: if (components != null) 65: { 66: components.Dispose(); 67: } 68: } 69: base.Dispose(disposing); 70: } 71: 72: #region Windows Form Designer generated code 73: /// <summary> 74: /// Код для поддержки Дизайнера - не модифицируйте этот код в редакторе. 75: /// </summary> 76: 77: private void InitializeComponent() 78: { 79: this.FixButton = new System.Windows.Forms.Button(); 80: this.EnterButton = new System.Windows.Forms.Button(); 81: this.Display = new System.Windows.Forms.TextBox(); 82: this.ClearButton = new System.Windows.Forms.Button(); 83: this.SubtractButton = new System.Windows.Forms.Button(); 84: this.SevenButton = new System.Windows.Forms.Button(); 85: this.EightButton = new System.Windows.Forms.Button(); 86: this.NineButton = new System.Windows.Forms.Button(); 87: this.FiveButton = new System.Windows.Forms.Button(); 88: this.FourButton = new System.Windows.Forms.Button(); 89: this.AddButton = new System.Windows.Forms.Button();

Page 56: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

56

90: this.SixButton = new System.Windows.Forms.Button(); 91: this.ThreeButton = new System.Windows.Forms.Button(); 92: this.MultiplyButton = new System.Windows.Forms.Button(); 93: this.OneButton = new System.Windows.Forms.Button(); 94: this.TwoButton = new System.Windows.Forms.Button(); 95: this.ZeroButton = new System.Windows.Forms.Button(); 96: this.DivideButton = new System.Windows.Forms.Button(); 97: this.DeleteButton = new System.Windows.Forms.Button(); 98: this.DecimalButton = new System.Windows.Forms.Button(); 99: this.SuspendLayout();

100: // 101: // FixButton 102: // 103: this.FixButton.Location = new System.Drawing.Point(112, 57); 104: this.FixButton.Name = "FixButton"; 105: this.FixButton.Size = new System.Drawing.Size(40, 32); 106: this.FixButton.TabIndex = 14; 107: this.FixButton.TabStop = false; 108: this.FixButton.Text = "Fix"; 109: this.FixButton.Click += new System.EventHandler(this.FixButton_Click); 110: // 111: // EnterButton 112: // 113: this.EnterButton.Location = new System.Drawing.Point(16, 57); 114: this.EnterButton.Name = "EnterButton"; 115: this.EnterButton.Size = new System.Drawing.Size(88, 32); 116: this.EnterButton.TabIndex = 15; 117: this.EnterButton.TabStop = false; 118: this.EnterButton.Text = "Enter"; 119: this.EnterButton.Click += new System.EventHandler(this.EnterButton_Click); 120: // 121: // Display 122: // 123: this.Display.BackColor = System.Drawing.SystemColors.Window; 124: this.Display.Cursor = System.Windows.Forms.Cursors.Default; 126: this.Display.Font = new System.Drawing.Font("Microsoft Sans Serif",

9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));

127: this.Display.Location = new System.Drawing.Point(16, 17); 128: this.Display.Name = "Display"; 129: this.Display.ReadOnly = true; 130: this.Display.Size = new System.Drawing.Size(184, 22); 131: this.Display.TabIndex = 13; 132: this.Display.TabStop = false; 133: this.Display.Text = "0.00"; 134: this.Display.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; 136: // 137: // ClearButton

Page 57: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

57

138: // 139: this.ClearButton.Location = new System.Drawing.Point(160, 57); 140: this.ClearButton.Name = "ClearButton"; 141: this.ClearButton.Size = new System.Drawing.Size(40, 32); 142: this.ClearButton.TabIndex = 11; 143: this.ClearButton.TabStop = false; 144: this.ClearButton.Text = "Clear"; 146: this.ClearButton.Click += new System.EventHandler(this.ClearButton_Click); 147: // 148: // SubtractButton 149: // 150: this.SubtractButton.Font = new System.Drawing.Font("Microsoft Sans

Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));

151: this.SubtractButton.Location = new System.Drawing.Point(16, 97); 152: this.SubtractButton.Name = "SubtractButton"; 153: this.SubtractButton.Size = new System.Drawing.Size(40, 32); 154: this.SubtractButton.TabIndex = 12; 155: this.SubtractButton.TabStop = false; 156: this.SubtractButton.Text = "-"; 157: this.SubtractButton.Click +=

new System.EventHandler(this.SubtractButton_Click); 158: // 159: // SevenButton 160: // 161: this.SevenButton.Location = new System.Drawing.Point(64, 97); 162: this.SevenButton.Name = "SevenButton"; 163: this.SevenButton.Size = new System.Drawing.Size(40, 32); 164: this.SevenButton.TabIndex = 19; 165: this.SevenButton.TabStop = false; 166: this.SevenButton.Text = "7"; 167: this.SevenButton.Click +=

new System.EventHandler(this.SevenButton_Click); 168: // 169: // EightButton 170: // 171: this.EightButton.Location = new System.Drawing.Point(112, 97); 172: this.EightButton.Name = "EightButton"; 173: this.EightButton.Size = new System.Drawing.Size(40, 32); 174: this.EightButton.TabIndex = 20; 175: this.EightButton.TabStop = false; 176: this.EightButton.Text = "8"; 177: this.EightButton.Click +=

new System.EventHandler(this.EightButton_Click); 178: // 179: // NineButton 180: // 181: this.NineButton.Location = new System.Drawing.Point(160, 97); 182: this.NineButton.Name = "NineButton";

Page 58: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

58

183: this.NineButton.Size = new System.Drawing.Size(40, 32); 184: this.NineButton.TabIndex = 18; 185: this.NineButton.TabStop = false; 186: this.NineButton.Text = "9"; 187: this.NineButton.Click +=

new System.EventHandler(this.NineButton_Click); 188: // 189: // FiveButton 190: // 191: this.FiveButton.Location = new System.Drawing.Point(112, 137); 192: this.FiveButton.Name = "FiveButton"; 193: this.FiveButton.Size = new System.Drawing.Size(40, 32); 194: this.FiveButton.TabIndex = 16; 195: this.FiveButton.TabStop = false; 196: this.FiveButton.Text = "5"; 197: this.FiveButton.Click +=

new System.EventHandler(this.FiveButton_Click); 198: // 199: // FourButton 200: // 201: this.FourButton.Location = new System.Drawing.Point(64, 137); 202: this.FourButton.Name = "FourButton"; 203: this.FourButton.Size = new System.Drawing.Size(40, 32); 204: this.FourButton.TabIndex = 17; 205: this.FourButton.TabStop = false; 206: this.FourButton.Text = "4"; 207: this.FourButton.Click +=

new System.EventHandler(this.FourButton_Click); 208: // 209: // AddButton 210: // 211: this.AddButton.Font = new System.Drawing.Font("Microsoft Sans Serif",

12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));

212: this.AddButton.Location = new System.Drawing.Point(16, 137); 213: this.AddButton.Name = "AddButton"; 214: this.AddButton.Size = new System.Drawing.Size(40, 32); 215: this.AddButton.TabIndex = 4; 216: this.AddButton.TabStop = false; 217: this.AddButton.Text = "+"; 218: this.AddButton.Click +=

new System.EventHandler(this.AddButton_Click); 219: // 220: // SixButton 221: // 222: this.SixButton.Location = new System.Drawing.Point(160, 137); 223: this.SixButton.Name = "SixButton"; 224: this.SixButton.Size = new System.Drawing.Size(40, 32);

Page 59: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

59

226: this.SixButton.TabIndex = 5; 227: this.SixButton.TabStop = false; 228: this.SixButton.Text = "6"; 229: this.SixButton.Click +=

new System.EventHandler(this.SixButton_Click); 230: // 231: // ThreeButton 232: // 233: this.ThreeButton.Location = new System.Drawing.Point(160, 177); 234: this.ThreeButton.Name = "ThreeButton"; 236: this.ThreeButton.Size = new System.Drawing.Size(40, 32); 237: this.ThreeButton.TabIndex = 3; 238: this.ThreeButton.TabStop = false; 239: this.ThreeButton.Text = "3"; 240: this.ThreeButton.Click +=

new System.EventHandler(this.ThreeButton_Click); 241: // 242: // MultiplyButton 243: // 244: this.MultiplyButton.Font = new System.Drawing.Font("Microsoft Sans

Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));

246: this.MultiplyButton.Location = new System.Drawing.Point(16, 177); 247: this.MultiplyButton.Name = "MultiplyButton"; 248: this.MultiplyButton.Size = new System.Drawing.Size(40, 32); 249: this.MultiplyButton.TabIndex = 1; 250: this.MultiplyButton.TabStop = false; 251: this.MultiplyButton.Text = "*"; 252: this.MultiplyButton.Click +=

new System.EventHandler(this.MultiplyButton_Click); 253: // 254: // OneButton 255: // 256: this.OneButton.Location = new System.Drawing.Point(64, 177); 257: this.OneButton.Name = "OneButton"; 258: this.OneButton.Size = new System.Drawing.Size(40, 32); 259: this.OneButton.TabIndex = 2; 260: this.OneButton.TabStop = false; 261: this.OneButton.Text = "1"; 262: this.OneButton.Click +=

new System.EventHandler(this.OneButton_Click); 263: // 264: // TwoButton 265: // 266: this.TwoButton.Location = new System.Drawing.Point(112, 177); 267: this.TwoButton.Name = "TwoButton"; 268: this.TwoButton.Size = new System.Drawing.Size(40, 32); 269: this.TwoButton.TabIndex = 9; 270: this.TwoButton.TabStop = false;

Page 60: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

60

271: this.TwoButton.Text = "2"; 272: this.TwoButton.Click +=

new System.EventHandler(this.TwoButton_Click); 273: // 274: // ZeroButton 275: // 276: this.ZeroButton.Location = new System.Drawing.Point(64, 217); 277: this.ZeroButton.Name = "ZeroButton"; 278: this.ZeroButton.Size = new System.Drawing.Size(40, 32); 279: this.ZeroButton.TabIndex = 10; 280: this.ZeroButton.TabStop = false; 281: this.ZeroButton.Text = "0"; 282: this.ZeroButton.Click +=

new System.EventHandler(this.ZeroButton_Click); 283: // 284: // DivideButton 285: // 286: this.DivideButton.Font = new System.Drawing.Font("Microsoft Sans Serif",

12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));

287: this.DivideButton.Location = new System.Drawing.Point(16, 217); 288: this.DivideButton.Name = "DivideButton"; 289: this.DivideButton.Size = new System.Drawing.Size(40, 32); 290: this.DivideButton.TabIndex = 8; 291: this.DivideButton.TabStop = false; 292: this.DivideButton.Text = "/"; 293: this.DivideButton.Click +=

new System.EventHandler(this.DivideButton_Click); 294: // 295: // DeleteButton 296: // 297: this.DeleteButton.Location = new System.Drawing.Point(160, 217); 298: this.DeleteButton.Name = "DeleteButton"; 299: this.DeleteButton.Size = new System.Drawing.Size(40, 32); 300: this.DeleteButton.TabIndex = 6; 301: this.DeleteButton.TabStop = false; 302: this.DeleteButton.Text = "Del"; 303: this.DeleteButton.Click +=

new System.EventHandler(this.DeleteButton_Click); 304: // 305: // DecimalButton 306: // 307: this.DecimalButton.Location = new System.Drawing.Point(112, 217); 308: this.DecimalButton.Name = "DecimalButton"; 309: this.DecimalButton.Size = new System.Drawing.Size(40, 32); 310: this.DecimalButton.TabIndex = 7; 311: this.DecimalButton.TabStop = false; 312: this.DecimalButton.Text = ".";

Page 61: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

61

313: this.DecimalButton.Click += new System.EventHandler(this.DecimalButton_Click);

314: // 315: // Form1 316: // 317: this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); 318: this.ClientSize = new System.Drawing.Size(218, 266); 319: this.Controls.AddRange(new System.Windows.Forms.Control[ ] 320: { 321: this.FixButton, 322: this.EnterButton, 323: this.Display, 324: this.ClearButton, 326: this.SubtractButton, 327: this.SevenButton, 328: this.EightButton, 329: this.NineButton, 330: this.FiveButton, 331: this.FourButton, 332: this.AddButton, 333: this.SixButton, 334: this.ThreeButton, 336: this.MultiplyButton, 337: this.OneButton, 338: this.TwoButton, 339: this.ZeroButton, 340: this.DivideButton, 341: this.DeleteButton, 342: this.DecimalButton 343: }); 344: this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 346: this.KeyPreview = true; 347: this.MaximizeBox = false; 348: this.Name = "Form1"; 349: this.Text = "NetCalc"; 350: this.ResumeLayout(false); 351: 352: } 353: #endregion 354: 355: /// <summary> 356: /// Основная точка входа для приложения. 357: /// </summary> 358: [STAThread] 359: static void Main() 360: { 361: Application.Run(new Form1()); 362: }

Page 62: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

62

363: 364: private Stack RegStack = new Stack(); 365: private string FormatString = "f2"; 366: private bool FixPending = false; 367: private bool DecimalInString = false; 368: private bool EntryInProgress = false; 369: private const int MaxChars = 21; 370: 371: // 372: // Обработка нажатия одной из цифровых кнопок. Если 373: // последней была нажата кнопка Fix, изменить точность 374: // калькулятора, обновив FormatString. В противном случае 375: // очистить дисплей ( при необходимости ) и добавить 376: // цифру к уже введеным цифрам. 377: // 378: private void ProcessDigit(int Value) 379: { 380: if (FixPending) 381: { 382: FormatString = "f" + Value.ToString(); 383: if (EntryInProgress) InitializeXRegisterFromDisplay(); 384: DisplayXRegister(); 385: FixPending = false; 386: } 387: else 388: { 389: ConditionalResetDisplay(); 390: if (Display.Text.Length < MaxChars) Display.Text += Value.ToString(); 391: } 392: } 393: 394: // 395: // Сбросить внутренние флаги калькулятора. 396: // 397: private void Reset() 398: { 399: DecimalInString = false; 400: FixPending = false; 401: EntryInProgress = false; 402: } 403: 404: // 405: // Очистить дисплей, если это не продолжение ввода 406: // 407: // 408: private void ConditionalResetDisplay() 409: { 410: if (!EntryInProgress)

Page 63: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

63

411: { 412: EntryInProgress = true; 413: Display.Text = ""; 414: } 415: } 416: 417: // 418: // Преобразование текста на дисплее калькулятора 419: // в числовую величину и помещение ее в стек. 420: // 421: private void InitializeXRegisterFromDisplay() 422: { 423: double x = (Display.Text.Length == 0 || Display.Text == ".") ?

0.0 : Convert.ToDouble(Display.Text); 424: RegStack.Push(x); 426: } 427: 428: // 429: // Отобразить величину, находящуюся наверху стека, в окне 430: // калькулятора. Если произошло переполнение и потеря 431: // значимости, то отобразить сообщение об ошибке. 432: // 433: private void DisplayXRegister() 434: { 436: double x = (double)RegStack.Peek(); 437: if (x > Double.MaxValue || x < Double.MinValue) ReportError("Переполнение"); 438: else 439: { 440: string display = x.ToString(FormatString); 441: if (display.Length > MaxChars || 442: (Math.Abs(x) > 0.0 && Math.Abs(x) < 0.0000000001)) 443: display = x.ToString("e"); 444: Display.Text = display; 446: } 447: } 448: 449: 450: // 451: // Оповестить пользователя об ошибке, выведя сообщение 452: // в окно калькулятора. Ткже сбросить внутреннее состояние 453: // калькулятора, чтобы при нажатии следующей кнопки 454: // процесс начался заново. 455: // 456: private void ReportError(string Message) 457: { 458: Display.Text = Message; 459: RegStack.Clear(); 460: RegStack.Push(0.0);

Page 64: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

64

461: Reset(); 462: } 463: 464: // 465: // Обработчик события Click для цифровых кнопок калькулятора (0-9). 466: // 467: private void ZeroButton_Click(object sender, System.EventArgs e) 468: { 469: ProcessDigit(0); 470: } 471: 472: private void OneButton_Click(object sender, System.EventArgs e) 473: { 474: ProcessDigit(1); 475: } 476: 477: private void TwoButton_Click(object sender, System.EventArgs e) 478: { 479: ProcessDigit(2); 480: } 481: 482: private void ThreeButton_Click(object sender, System.EventArgs e) 483: { 484: ProcessDigit(3); 485: } 486: 487: private void FourButton_Click(object sender, System.EventArgs e) 488: { 489: ProcessDigit(4); 490: } 491: 492: private void FiveButton_Click(object sender, System.EventArgs e) 493: { 494: ProcessDigit(5); 495: } 496: 497: private void SixButton_Click(object sender, System.EventArgs e) 498: { 499: ProcessDigit(6); 500: } 501: 502: private void SevenButton_Click(object sender, System.EventArgs e) 503: { 504: ProcessDigit(7); 505: } 506: 507: private void EightButton_Click(object sender, System.EventArgs e) 508: {

Page 65: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

65

509: ProcessDigit(8); 510: } 511: 512: private void NineButton_Click(object sender, System.EventArgs e) 513: { 514: ProcessDigit(9); 515: } 516: 517: // 518: // 519: // Обработчик событий Click для кнопок: Add, Subtract, Multiply и Divide. 520: // 521: // Общая стратегия для всех четырех обработчиков событий: 522: // 1) если значение, отображаемое на дисплее калькулятора 523: // еще не в стеке, поместить его туда; 524: // 2) извлечь из стека два значения (операнды); 526: // 3) вычислить сумму, разность, произведение или частное; 527: // 4) поместите результат в стек. 528: // 5) отобразить результат. 529: // 530: private void SubtractButton_Click(object sender, System.EventArgs e) 531: { 532: if (EntryInProgress) InitializeXRegisterFromDisplay(); 533: 534: if (RegStack.Count >= 2) 536: { 537: double op1 = (double)RegStack.Pop(); 538: double op2 = (double)RegStack.Pop(); 539: RegStack.Push(op2 - op1); 540: DisplayXRegister(); 541: Reset(); 542: } 543: } 544: 546: private void AddButton_Click(object sender, System.EventArgs e) 547: { 548: if (EntryInProgress) InitializeXRegisterFromDisplay(); 549: 550: if (RegStack.Count >= 2) 551: { 552: double op1 = (double)RegStack.Pop(); 553: double op2 = (double)RegStack.Pop(); 554: RegStack.Push(op2 + op1); 555: DisplayXRegister(); 556: Reset(); 557: } 558: } 559:

Page 66: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

66

560: private void MultiplyButton_Click(object sender, System.EventArgs e) 561: { 562: if (EntryInProgress) InitializeXRegisterFromDisplay(); 563: 564: if (RegStack.Count >= 2) 565: { 566: double op1 = (double)RegStack.Pop(); 567: double op2 = (double)RegStack.Pop(); 568: RegStack.Push(op2 * op1); 569: DisplayXRegister(); 570: Reset(); 571: } 572: } 573: 574: private void DivideButton_Click(object sender, System.EventArgs e) 575: { 576: if (EntryInProgress) InitializeXRegisterFromDisplay(); 577: 578: if (RegStack.Count >= 2) 579: { 580: if ((double)RegStack.Peek() == 0.0) ReportError("Divide by zero"); 581: else 582: { 583: double op1 = (double)RegStack.Pop(); 584: double op2 = (double)RegStack.Pop(); 585: RegStack.Push(op2 / op1); 586: DisplayXRegister(); 587: Reset(); 588: } 589: } 590: } 591: 592: // 593: // Обработчик события Click для кнопки Enter. 594: // 595: private void EnterButton_Click(object sender, System.EventArgs e) 596: { 597: InitializeXRegisterFromDisplay(); 598: DisplayXRegister(); 599: Reset(); 600: } 601: 602: // 603: // Обработчик события Click для кнопки Fix. 604: // 605: private void FixButton_Click(object sender, System.EventArgs e) 606: { 607: FixPending =! FixPending;

Page 67: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

67

608: } 609: 610: // 611: // Обработчик события Click для кнопки Clear. 612: // 613: private void ClearButton_Click(object sender, System.EventArgs e) 614: { 615: RegStack.Clear(); 616: RegStack.Push(0.0); 617: DisplayXRegister(); 618: Reset(); 619: } 620: 621: // 622: // Обработчик события Click для кнопки Delete. 623: // 624: private void DeleteButton_Click(object sender, System.EventArgs e) 626: { 627: int len = Display.Text.Length; 628: if (len > 0 && EntryInProgress) 629: { 630: if (Display.Text[len - 1] == '.') 631: DecimalInString = false; 632: Display.Text = Display.Text.Substring(0, len - 1); 633: } 634: } 636: 637: // 638: // Обработчик события Click для кнопки '.'. 639: // 640: private void DecimalButton_Click(object sender, System.EventArgs e) 641: { 642: ConditionalResetDisplay(); 643: if (Display.Text.Length < MaxChars && !DecimalInString) 644: { 646: Display.Text += "."; 647: DecimalInString = true; 648: } 649: } 650: 651: // 652: // Обработчик событий KeyPress. Составляет 653: // половину всего клавиатурного интерфейса NetCalc. 654: // 655: protected override void OnKeyPress(KeyPressEventArgs e) 656: { 657: switch (e.KeyChar) 658: {

Page 68: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

68

659: 660: case '0': 661: ZeroButton_Click(ZeroButton, new EventArgs()); 662: break; 663: 664: case '1': 665: OneButton_Click(OneButton, new EventArgs()); 666: break; 667: 668: case '2': 669: TwoButton_Click(TwoButton, new EventArgs()); 670: break; 671: 672: case '3': 673: ThreeButton_Click(ThreeButton, new EventArgs()); 674: break; 675: 676: case '4': 677: FourButton_Click(FourButton, new EventArgs()); 678: break; 679: 680: case '5': 681: FiveButton_Click(FiveButton, new EventArgs()); 682: break; 683: 684: case '6': 685: SixButton_Click(SixButton, new EventArgs()); 686: break; 687: 688: case '7': 689: SevenButton_Click(SevenButton, new EventArgs()); 690: break; 691: 692: case '8': 693: EightButton_Click(EightButton, new EventArgs()); 694: break; 695: 696: case '9': 697: NineButton_Click(NineButton, new EventArgs()); 698: break; 699: 700: case '.': 701: DecimalButton_Click(DecimalButton, new EventArgs()); 702: break; 703: 704: case '-': 705: SubtractButton_Click(SubtractButton, new EventArgs()); 706: break;

Page 69: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

69

707: 708: case '+': 709: AddButton_Click(AddButton, new EventArgs()); 710: break; 711: 712: case '*': 713: MultiplyButton_Click(MultiplyButton, new EventArgs()); 714: break; 715: 716: case '/': 717: DivideButton_Click(DivideButton, new EventArgs()); 718: break; 719: } 720: } 721: 722: // 723: // Обработчик событий KeyDown. Оставшаяся 724: // часть клавиатурного интерфейса NetCalc. 726: // 727: protected override void OnKeyDown(KeyEventArgs e) 728: { 729: switch (e.KeyCode) 730: { 731: 732: case Keys.C: 733: ClearButton_Click(ClearButton, new EventArgs()); 734: break; 736: 737: case Keys.F: 738: FixButton_Click(FixButton, new EventArgs()); 739: break; 740: 741: case Keys.Enter: 742: EnterButton_Click(EnterButton, new EventArgs()); 743: break; 744: 746: case Keys.Delete: 747: DeleteButton_Click(DeleteButton, new EventArgs()); 748: break; 749: } 750: } 751: 752: // Это переопределение не позволяет форме "украсть" 753: // сигнал о нажатии клавиши «Enter», которая имитирует 754: // нажатие кнопки. 755: // 756: protected override bool ProcessDialogKey(Keys keyData) 757: {

Page 70: Пр зан 9 - narod.ruzei.narod.ru/Lecture_11_Practic_9.pdfВ приложении Windows Forms потребуется лишь одна строка (см. раздел 1.4, с.19,сл.).

70

758: return keyData == Keys.Enter ? false : base.ProcessDialogKey(keyData); 759: } 760: } 761: }

Заключение Windows Forms — это важная часть .NET Framework, так как позволяет разработчикам создавать приложения с мощным графическим интерфейсом пользователя. Если Microsoft сумеет убедить разработчиков поддержать инициативу .NET и перейти на .NET Framework, Windows Forms вытеснит современные Windows-приложения. Вполне возможно, что уже в недалеком будущем Windows API, MFC и другие инструменты, годами служившие двигателем индустрии программирования, станут анахронизмом. Для программистов, которым приходилось бороться с ограничениями современных платформ, это будет долгожданный день.

Visual Studio .NET — большое подспорье в построении приложений с формами вроде NetCalc, так как она уменьшает объем утомительных операций позиционирования и подгонки размеров элементов управления, а также установки значений их свойств. А чем работа менее утомительна, тем короче цикл разработки. Продукт быстрее выходит на рынок!

Литература 1. Чарльз Петцольд Программирование с использованием Microsoft Windows Forms. Изда-тельство: Русская Редакция, Питер, 2006 г. 432 стр. 3000 экз. 417 р. 2. Джеф Просиз Программирование для Microsoft .NET (+ CD-ROM). Издательство: Русская Редакция, 2003 г. 704 стр. 3000 экз. 460 р.

3. Фролов А.В., Фролов Г.В. Визуальное проектирование приложений С#. – М.: КУДИЦ-ОБРАЗ, 2003. – 512 стр. 3000 экз.