Назад | Начало урока | Вперед
Содержание

Глава 22

Создаем пользовательский интерфейс


В этой главе:

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

Наверное, компьютерные гуру компании Microsoft, работавшие над созданием среды .NET, хотели быть уверены, что их творение полюбят все. Среда .NET способна решать разнообразнейшие задачи: от управления вводом-выводом данных, до создания программ, работающих в Web и оснащенных самым потрясающим графическим интерфейсом.

Эта глава посвящена вопросам разработки графического пользовательского интерфейса (Graphical User Interface-— GUI) с использованием .NET-классов GUI, которые обычно обозначаются термином WinForms. (Формально эти классы называются Windows Forms, но программисты, которые с ними работают, называют их просто WinForms.)

.NET-классы WinForms подключаются к интерфейсу среды Windows и взаимодействуют с его элементами (диалоговыми окнами, кнопками, меню, списками и т.п.). Они непосредственно обращаются к компонентам среды Windows, которые представляют отдельные элементы пользовательского интерфейса.
Например, объект класса System. Windows.Forms.CheckBox выглядит и работает точно так же, как и любой другой флажок (check box), расположенный где-нибудь в диалоговом окне Windows. Это означает, что вы можете создавать .NET-приложения, используя все преимущества среды .NET, CLR и всего, что им сопутствует; в то же время они будут иметь все характеристики и возможности приложений Windows.

Вверх

Основной набор инструментов

Классы WinForms представляют одну из частей Windows API (Application Programming
Interface— программный интерфейс приложения). Другие части Windows API
представлены другими компонентами .NET. Например, интерфейсу графических устройств
(Graphical Device Interface— GDI) в среде .NET соответствуют классы System. Drawing.

Большинство классов WinForms используются для создания визуальных средств управления,
которые являются составными компонентами среды Windows.
Некоторые из них, например объекты
класса ContextMenu, не всегда отображаются на экране. Способ отображения других заранее
определен. Так, например, объекты класса MainMenu всегда отображаются в верхней части окна
программы.
В табл 22.1 приведен список тех классов WinForms, которые, вероятнее всего, вы будете использовать в повседневной работе.
Таблица 22.1. Наиболее часто используемые классы WinForms


Класс
System.Windows.Form
Описание
Application
Управляет программой, в том числе ее запуском и остановкой
Button Кнопка, иногда называемая также командной кнопкой. Щелчок на кнопке влечет за собой выполнение каких-то действий
CheckBox
Флажок. Можно выбирать сразу несколько флажков, принадлежащих одной группе (в отличие от переключателей)
Checked List Box Список флажков, который можно прокручивать. Элемент может вмещать в себя множество флажков, занимая при этом небольшое пространство
Combo Box
Поле со списком. В нем вы можете либо набрать собственное значение, либо выбрать из списка одно из предлагаемых значений. Существует несколько видов полей со списком
ContextMenu Контекстное меню (открывается после щелчка правой кнопкой мыши). Позволяет пользователю выбрать команды или опции, относящиеся к тому элементу, на котором он щелкнул
DataGrid
Таблица данных, в которой отображается информация, сохраненная в источнике данных ADO.NET. Столбцы таблицы данных соответствуют полям в источнике данных, а строки - записям
FileDialog Диалоговое окно, предоставляющее пользователю возможность обзора для выбора файла, который должен быть открыт, или папки, в которой должен быть сохранен новый файл
Form
Базовый класс WinForms, из которого путем наследования создаются классы, описывающие все формы вашей программы. Производные классы могут представлять как постоянные, так и диалоговые окна
Label Надпись. Отображает текст, который обычно относится к другим элементам управления. Пользователь не может изменить текст надписи
LinkLabel
Гиперссылка, щелчок на которой приводит к открытию Web-страницы или к другим действиям
ListBox Список. Пользователю предоставляется возможность выбрать одно из предлагаемых значений
ListView
Список, содержащий в себе элементы и соответствующие им пиктограммы. Такие списки широко используются в среде Windows, и вы наверняка часто с ними сталкивались
MainMenu Главное меню. Отображается в виде строки в верхней части окна программы
Picture Box
Рисунок. Используется для отображения графики в окне формы
ProgressBar Индикатор выполнения. Отображает сообщения о выполняемой операции
RadioButton
Переключатель. Из группы переключателей может быть выбран только один переключатель
RichTextBox Окно с плотным текстом. Отображает форматированный текст и позволяет пользователю набирать новый текст, который в дальнейшем может быть использован программой. Форматирование может включать полужирное начертание, курсив, маркеры абзаца и гиперссылки
Splitter
Разделитель. Позволяет пользователям изменять в окне формы размеры элементов управления
StatusBar Строка состояния, располагающаяся в нижней части окна формы. Отображает информацию и сообщения для пользователя
TabControl
Элемент управления, содержащий набор вкладок. Пользователь может переходить от одной вкладки к другой, щелкая на их корешках
TextBox Текстовое поле. Отображает текст и позволяет пользователю набрать свой текст, который в дальнейшем будет использован программой. Текстовые поля могут состоять из нескольких строк
ToolBar
Панель инструментов, на которой размещаются командные кнопки. Щелкая на кнопках, пользователь может инициировать те или иные действия
TreeView Дерево, представляющее информацию в иерархическом виде, как, например, дерево папок в окне Windows Explorer

Вверх

Формы
Сейчас, наверное, перспектива создания программ с использованием инструментов WinForms кажется вам весьма сомнительной. Если вы уже пытались создавать программы для Windows и использовали для этого такие приложения, как, например, MFC, вы можете подумать, что при использовании классов WiiiForms вам придется столкнуться с теми же проблемами. Не переживайте, это не так. Компания Microsoft учла свой предыдущий опыт, и ее новое творение — среда .NET — объектно-ориентированна и отличается особой простотой в использовании.
Чтобы создать форму, нужно выполнить три основных шага.
  1. Создать новый класс путем наследования класса System.Windows.Forms.Form.
  2. Настроить новую форму (разместить на ней управляющие элементы и т.п.).
  3. Определить порядок взаимодействия программы и созданной формы.
Наследование форм
Все формы, которые используются в вашей программе, создаются путем наследования
класса WiiiForms, который так и называется — Form. В процессе наследования определяется, как новая форма будет выглядеть и как она будет работать. Производный класс от класса
Form создается очень просто.

__gc class MyForm : public Form
{

public:
MyForm();
}

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

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

Настройка формы

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

  1. Создайте объекты классов, которые описывают отдельные элементы управления.
    Button *роОК = new Button();
    RichTextBox *poTE = new RichTextBox();
    PictureBox *polmg = new PictureBox();

  2. Установите свойства элементов управления.
    //Определение свойств кнопки
    poOK->Text = "OK";
    poOK->Left - 280;
    роОК->Тор = 350;
    poOK->BackColor = Color::Blue;

  3. Добавьте элементы управления к коллекции Controls вашей формы.
    this->Controls->Add(poOK);
    this->Contro!s->Add(poTE);
    this->Controls->Add(polmg);
Совет:

Указатель t h i s -> используется в тех случаях, когда вы набираете колы функций-
членов, к числу которых относится, например, конструктор. Здесь этот указатель
используется, чтобы с помощью возможности редактора Visual C++ выделять цве-
том отдельные синтаксические элементы, тем самым обозначив, что набираемые
коды относятся ко всей форме, а не только к одному из ее элементов управления.

Кроме того, вы можете определить свойства самой формы:
//Определение размеров формы
this->Width = 600;
this->Keight = 400;

Открытие формы
После того как вы создадите форму и определите ее характеристики, вам еще нужно будет
сообщить компилятору, что эта форма должна быть главной формой вашей программы. Для
этого используется класс System.Windows.Forms.Application. Метод Run этого класса принимает в качестве аргумента указатель созданной формы и делает ее главной для вашей программы. Обычно метод Run вызывается функцией main.

#ifdef _UNICODE
int wmain(void)
#else
int main(void)
#endif
{

Application::Run(new MyForm());
return 0;
}

Вверх

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

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

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

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

Вверх

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

Имена всех функций-членов, предназначенных для обработки событий, начинаются с
букв On. Например, функция-член, предназначенная для обработки события MouseMove
(перемещение курсора мыши), называется OnMouseMove. Список названий всех функций-членов,
обрабатывающих события, вы можете найти в справочной системе Visual C++ .NET в
разделе Protected Instance Methods. Поскольку все эти функции являются защищенными
(protected), они могут быть перегружены и вызваны только из производных классов.

Предположим, например, что вы хотите, чтобы при перемещении пользователем курсора мыши в
окне созданной вами формы для курсора постоянно отображалось текущее значение координаты X.
Чтобы сделать это, вам нужно перегрузить функцию-член OnMouseMove, но для этого вы должны
знать ее сигнатуру. (Сигнатура функции определяет тип возвращаемого ею результата, а также
количество и тип передаваемых ей аргументов. Дополнительную информацию о сигнатуре функций
вы найдете в главе 18.) Вы можете найти описание функции OnMouseMove в справочной системе
Visual C++ или просто воспользоваться возможностью IntelliSense редактора Visual C++.
(Попробуйте набрать On и, удерживая клавишу , нажмите пробел.) И в том и в другом
случае вы увидите, что функция OnMouseMove должна быть объявлена таким образом:

void OnMouseMove(MouseEventArgs *е);

Вы, наверное, спросите: "Что это еще за MouseEventArgs?" Дело в том. что каждая
функция, обрабатывающая событие, должна иметь какую-то информацию о самом событии.
Так, например, функция OnMouseMove вызывается каждый раз, когда пользователь перемещает
курсор мыши, но она также должна знать, куда курсор перемещается. Именно для этого
и существует аргумент MouseEventArgs, у которого есть свойства, позволяющие определить
текущие координаты X и Y курсора мыши. Для других событий предусмотрены другие
аргументы.

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

После того как функция OnMouseMove объявлена, она должна быть определена. Чтобы
отображать значение координаты X курсора мыши, добавьте к классу в качестве члена данных
элемент управления Label (Надпись) и свойству Text этого объекта присвойте значение
свойства X объекта MouseEventArgs:

void MyForm::OnMouseMove(MouseEventArgs *e)
{

//Отображение координаты Х
m_poXPosition->Text = e->x.ToString() ;
}

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

Вверх

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

Чтобы добавить к какому-то классу делегированную функцию, нужно выполнить два основных шага.
  1. Объявить и определить функцию-член, обрабатывающую событие.
  2. Добавить делегата.

Объявление и определение обрабатывающей событие функции

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

void Change Clicked(Object *sender, EventArgs *e);

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

Совет:

В -NET отсутствуют какие-либо дополнительные требования относительно наименования функций-членов, обрабатывающих события. Однако лучше присваивать такие имена, которые как-то будут указывать на обрабатываемое событие и на объект, к которому это событие относится. Например, название Change_Clicked говорит о том, что здесь обрабатывается событие Click для кнопки Change.

Из определения функции Change_Clicked видно, что после щелчка на кнопке Change
изменяется рисунок, отображаемый объектом PictureBox:

void MyForm::Change_Clicked(Object ^sender, EventArgs *e)

//Изменение отображаемого рисунка
ra_polmg->lmage = Image::FromFile("..\\ Wdesert5.jpg");

Добавление делегата

Просто создать процедуру обработки события недостаточно. Нужно еще сообщить компилятору, когда эта процедура должна выполняться. Нужно связать событие C l i c k с только
что созданным делегатом.
Процесс добавления делегата включает в себя четыре основных элемента:

В нашем примере элементом управления является кнопка Change, представляемая членом данных poChange. Обрабатываемое событие - Click(щелчок кнопкой мыши), которому соответствует делегирующий класс EventHandler. Функцию-член мы назвали именем Change Clicked. Итак, чтобы добавить этого делегата, наберите такой код:

poChange->Click += new EventHandler(this, Change_Clicked);

Этот принцип распространяется и на все другие события. Например, чтобы обработать событие ChangeUICues (смысл которого в том, что пользователь нажимает клавишу <ТаЬ> или щелкает кнопкой мыши на элементе управления, делая его таким образом активным), вам нужно просмотреть справочную информацию об этом событии. Из нее вы узнаете, что данному событию соответствует делегирующий класс UlCuesEventHandler, которому нужна обрабатывающая событие функция-член с такой сигнатурой:

void Change__UICues (Object *sender, UICuesEventArgs *e) ;

Добавление делегата для этого события будет выглядеть следующим образом:

poChange->ChangeUICues += new UICuesEventHandier(this, Change_UICues);

Почему для добавления делегата нужно использовать оператор +=? Дело в том,
что .NET позволяет создавать для одного события множество процедур, которые
это событие обрабатывают. Если вы используете просто оператор присвоения (=),
одна процедура заменит собой все другие. Использование оператора += позволяет
добавлять новые процедуры к уже существующим.


Назад | Начало урока | Вверх | Вперед
Содержание