са EventArgs с описанием события. Второй аргумент мы использовать не
будем, а вот первый аргумент в некоторых случаях будет нами использо-
ваться. Что касается регистрации обработчиков событий, то для этих це-
лей нами традиционно используются экземпляры делегата EventHandler.
Далее имеет смысл обратиться к программному коду.
Программный код и выполнение
программы
И мы с пути кривого ни разу не свернем,
а надо будет — снова пойдем кривым
путем.
Из к/ф «Айболит 66»
Перед тем как приступить непосредственно к рассмотрению программного
кода, сделаем несколько общих замечаний относительно организации про-
граммы. В частности, есть несколько моментов, на которые имеет смысл
обратить внимание при анализе программы:
Метка с образцом текста реализуется через объект специального класса, который создается на основе класса метки Label. Мы поступаем следую-
щим образом: на основе класса Label путем наследования создаем класс
MyLabel. Код этого класса состоит, фактически, из конструктора, в кото-
ром определяются основные параметры текстовой метки. У конструк-
тора класса четыре целочисленных аргумента. Аргументы передаются
в метод SetBounds(), который вызывает из объекта метки и определяет
положение и размеры области метки. Также в конструкторе класса зада-
ется тип границ области метки (выделение области рамкой), ее текстовое
значение и способ выравнивания текста в области метки (выравнивание
по центру). Каждый раз, создавая объект класса MyLabel, получаем метку
с соответствующими характеристиками. Вопрос только в том, куда эту
метку добавить. Объект класса MyLabel создается с передачей четырех
целочисленных аргументов.
Программный код и выполнение программы 285
Для реализации оконной формы создается класс MyForm, который на-
следует класс Form. У класса достаточно много полей, несколько методов
и два свойства. Все основные настройки выполняются в конструкторе
класса.
Важную роль в рамках использованного в программе подхода играют
текстовые массивы, которые содержат названия кнопок, типы шрифтов, их стили. На основе этих списков формируются массивы объектов для
элементов управления. Это «полуавтоматический» подход, который по-
зволяет достаточно легко добавлять или убирать элемент управления —
во многих случаях достаточно добавить или убрать название элемента
в списке названий группы элементов. Правда, при этом могут возникнуть
проблемы с обработкой событий для добавленных/удаленных элементов
и распределением области оконной формы.
Диапазон возможных значений размера шрифта определяется мини-
мальным и максимальным значениями, которые реализуются в виде
полей. При создании списка размеров шрифта в одном из пунктов меню
минимальное и максимальное значения размеров шрифта используются
для формирования списка. Сам список формируется специальным ме-
тодом — этот метод в качестве результата возвращает текстовый массив, элементами которого являются числовые значения (их текстовое пред-
ставление) в диапазоне от минимального до максимального значения.
В качестве полей класса объявляются экземпляры делегата EventHandler (в том числе и один массив из экземпляров делегатов) для обработки
событий, связанных с изменением настроек элементов окна.
В классе описаны два свойства. Оба только возвращают значения.
Имеется целочисленное свойство для определения размера шрифта, и свойство, которое определяет шрифт, применяемый для образца тек-
ста в окне формы. Это свойство возвращает в качестве значения объект
класса Font. Свойство для определения размера шрифта вычисляется
на основе значения текстового поля. Причем обработка значения поля
выполняется так, что программа не прекращает работу при некоррект-
ном значении поля. Также контролируется «пограничный» режим, в ре-
зультате чего применяемый размер шрифта не выходит за допустимые
пределы.
В классе описаны методы, используемые в качестве обработчиков со-
бытий. Эти методы не возвращают результат, и у них по два аргумента: объект класса Object и объект класса EventArgs.
Для формирования главного меню есть специальный метод. В этом же
методе реализуется на программном уровне система обработки событий, связанных со взаимодействием пользователя с главным меню. Для пун-
ктов меню регистрируются делегаты обработчиков событий. При этом
286
Глава 8. Приложение с графическим интерфейсом: учебный проект
неявно предполагается, что делегаты ссылаются на соответствующие
методы. Присваивание значений делегатам выполняется в конструкторе
класса MyForm. Метод для формирования главного меню в конструкторе
вызывается после того, как присвоены значения экземплярам делегатов, посредством которых регистрируются обработчики событий.
Кроме метода для формирования главного меню программы есть метод
для формирования отдельного пункта меню. Этот метод вызывается
в методе для формирования главного меню.
В некоторых случаях приходится явно преобразовывать числовые зна-
чения в текстовое значение (получать текстовое представление числа).
Для этого из соответствующей числовой переменной вызывается метод
ToString().
Приняв на вооружение все перечисленное выше, можем смело приступить
к «прочтению» программного кода, представленного в листинге 8.1.
Листинг 8.1. Приложение с графическим интерфейсом
using System;
using System.Drawing;
using System.Windows.Forms;
// Класс для метки с образцом текста:
class MyLabel:Label{
/*
Конструктор класса. Аргументы - координаты левого верхнего
угла области метки и размеры области.
*/
public MyLabel(int x,int y,int w,int h){
Text="Образец текста"; // Текстовое значение метки
SetBounds(x,y,w,h); // Положение и размер метки
BorderStyle=BorderStyle.FixedSingle; // Тип границы области
// метки
// Способ выравнивания текста в метке:
TextAlign=ContentAlignment.MiddleCenter;
}
}
/*
Класс формы. В этом классе описано "практически все".
Класс создается наследованием класса Form.
*/
class MyForm:Form{
// Названия меню:
private string[] MN={"Действие","Тип шрифта","Стиль
шрифта","Размер шрифта"};
// Названия шрифтов:
private string[] FN={"Arial","Times","Courier"};
Программный код и выполнение программы 287
// Стили шрифтов:
private string[] FS={"Жирный","Курсив"};
// Названия кнопок:
private string[] BN={"Применить","Выход"};
// Минимальный размер шрифта:
private int min=10;
// Максимальный размер шрифта:
private int max=20;
// Метод для "вычисления" текстового массива
// целочисленных значений:
private string[] FSz(){
// Текстовый массив нужного размера:
string[] fs=new string[max-min+1];
// Оператор цикла для заполнения текстового
// массива:
for(int i=0;i fs[i]=(min+i).ToString(); // Преобразование числа в текст } return fs; // Результат метода - массив } // Метка с образцом текста: private MyLabel sample; // Кнопки: private Button[] Btns; // Переключатели (для выбора типа шрифта): private RadioButton[] RBtns; // Группа переключателей: private GroupBox FName=new GroupBox(); // Опции (для выбора стиля шрифта): private CheckBox[] CBtns; // Текстовое поле для ввода размера текста: private TextBox tsize; /* Группа экземпляров делегатов, используемых при обработке событий. */ private EventHandler[] BH; // Массив экземпляров делегатов // для кнопок private EventHandler RBH; // Экземпляр делегата // для кнопок-переключателей private EventHandler CBH; // Экземпляр делегата // для опций private EventHandler TBH; // Экземпляр делегата // для текстового поля // Свойство для определения размера шрифта: private int FSize{ get{ продолжение 288 Глава 8. Приложение с графическим интерфейсом: учебный проект Листинг 8.1 (продолжение) int size; // Локальная целочисленная переменная try{ // Блок обработки исключительных ситуаций // Попытка преобразовать текст текстового // поля в число: size=Int32.Parse(tsize.Text); // Если маленькое число, генерируем ошибку: if(size if(size>max){ // Если слишком большое число, // ограничиваем значение size=max; // Значение локальной переменной // Присваивание значения текстовому полю: tsize.Text=size.ToString(); } return size; // Результат аксессора - значение // свойства } catch{ // Обработка исключительной ситуации size=min; // Значение локальной переменной - по // минимуму tsize.Text=size.ToString(); // Заполнение текстового // поля return size; // Значение свойства в случае // исключительной ситуации } } } // Свойство для определения шрифта для // образца текста. // Свойство является объектом класса Font: private Font SFont{ get{ FontStyle fs=FontStyle.Regular; // Стиль шрифта. Начальное // значение if(CBtns[0].Checked) fs= fs|FontStyle.Bold; // Применяем // жирный // шрифт if(CBtns[1].Checked) fs|=FontStyle.Italic; // Применяем // курсивный // шрифт string fn=FN[0]; // Текстовое название шрифта. // Начальное значение // Перебор кнопок-переключателей для // определения положения переключателя: for(int i=1;i if(RBtns[i].Checked) fn=FN[i]; // Изменение названия // шрифта Программный код и выполнение программы 289 } // Создается объект шрифта: Font F=new Font(fn,FSize,fs); // Результат свойства: return F; } } /* Метод, который используется в качестве обработчика события выбора пункта меню, связанного с определением типа шрифта. */ public void setType(Object obj,EventArgs ea){ string menu; // Локальная текстовая переменная menu=(obj as MenuItem).Text; // Текст выбранного пункта меню // Оператор цикла для перебора // кнопок-переключателей: for(int i=0;i if(menu==RBtns[i].Text){ // Если текст пункта меню совпадает // с текстом кнопки, переключатель // устанавливается в выделенное положение: RBtns[i].Checked=true; return; // Завершается работа метода } } } /* Метод, который используется в качестве обработчика события выбора пункта меню, связанного с определением стиля шрифта. */ public void setStyle(Object obj,EventArgs ea){ int index; // Локальная целочисленная переменная index=(obj as MenuItem).Index; // Индекс выбранного пункта // в меню // Изменение (инверсия) статуса опции: CBtns[index].Checked=!CBtns[index].Checked; } /* Метод используется для обработки события выбора пункта меню, связанного с определением размера шрифта. */ public void setSize(Object obj,EventArgs ea){ string size; // Локальная текстовая переменная size=(obj as MenuItem).Text; // Текст выбранного пункта меню tsize.Text=size; // Присваивание нового значения продолжение 290 Глава 8. Приложение с графическим интерфейсом: учебный проект Листинг 8.1 (продолжение) // текстовому полю } /* Метод используется в качестве обработчика события щелчка на кнопке, в результате чего применяются настройки шрифта, выполненные в окне формы. */ public void OKButtonClick(Object obj,EventArgs ea){ sample.Font=SFont; // Применение шрифта, определяемого // свойством SFont } /* Метод используется в качестве обработчика щелчка на кнопке, предназначенной для завершения работы приложения. */ public void CancelButtonClick(Object obj,EventArgs ea){ Application.Exit(); // Завершение работы программы } /* Метод для создания главного меню. При вызове метода формируется главное меню оконной формы. Ссылка на объект этого меню возвращается в качестве результата. */ private MainMenu getMyMenu(){ // Создание объекта главного меню: MainMenu MyMenu=new MainMenu(); // Создание массива из объектов - пунктов меню: MenuItem[] mainMI=new MenuItem[MN.Length]; // Оператор цикла для перебора пунктов меню: for(int i=0;i mainMI[i]=new MenuItem(MN[i]); // Создание объекта // пункта меню // Добавление пункта меню в главное меню: MyMenu.MenuItems.Add(mainMI[i]); } /* Заполнение командами каждого из пунктов главного меню. Используется метод setMyMenuItem() для заполнения пунктов меню. Первый аргумент метода - объект заполняемого пункта меню. Второй аргумент метода - список текстовых значений-названий команд. */ setMyMenuItem(mainMI[0],BN); // Заполнение первого пункта меню // Регистрация обработчиков событий для выбора Программный код и выполнение программы 291 // команд первого пункта главного меню: for(int i=0;i mainMI[0].MenuItems[i].Click+=BH[i]; } // Заполнение второго пункта меню: setMyMenuItem(mainMI[1],FN); // Регистрация обработчиков событий для выбора // команд второго пункта главного меню: for(int i=0;i mainMI[1].MenuItems[i].Click+=RBH; } // Заполнение третьего пункта меню: setMyMenuItem(mainMI[2],FS); // Регистрация обработчиков событий для выбора // команд третьего пункта главного меню: for(int i=0;i mainMI[2].MenuItems[i].Click+=CBH; } // Заполнение четвертого пункта меню: setMyMenuItem(mainMI[3],FSz()); // Регистрация обработчиков событий для выбора // команд четвертого пункта главного меню: for(int i=0;i mainMI[3].MenuItems[i].Click+= TBH; } // Главное меню сформировано. // Возвращается результат: return MyMenu; } /* Метод для формирования пункта меню. Аргументами методами передаются объект для заполняемого пункта меню и список текстовых значений, которые служат названиями команд пункта меню. */ private void setMyMenuItem(MenuItem mm,string[] names){ // Массив объектов класса MenuItem для реализации // команд пункта меню: MenuItem[] mi=new MenuItem[names.Length]; // Заполняем пункт меню командами: for(int i=0;i mi[i]=new MenuItem(names[i]); // Создание объекта mm.MenuItems.Add(mi[i]); // Добавление элемента в меню } } // Конструктор класса: public MyForm(){ продолжение 292 Глава 8. Приложение с графическим интерфейсом: учебный проект Листинг 8.1 (продолжение) // Заголовок окна формы: Text="Работаем со шрифтами"; // Линейные размеры формы: Height=300; Width=400; // Тип границ оконной формы: FormBorderStyle=FormBorderStyle.FixedSingle; // Шрифт для элементов оконной формы: Font=new Font("Arial",8,FontStyle.Bold); // Создание метки с образцом текста: sample=new MyLabel(100,140,290,110); // Добавление метки в окно формы: Controls.Add(sample); // Создание массива для объектов кнопок: Btns=new Button[BN.Length]; // Заполнение массива: for(int i=0;i Btns[i]=new Button(); // Создание объекта кнопки Btns[i].Text=BN[i]; // Название кнопки Btns[i].SetBounds(10,140+i*40,80,30); // Положение и размеры // кнопки Controls.Add(Btns[i]); // Добавление кнопки в окно формы } // Массив для кнопок-переключателей: RBtns=new RadioButton[FN.Length]; // Перебираем элементы массива: for(int i=0;i RBtns[i]=new RadioButton(); // Создание объекта RBtns[i].Text=FN[i]; // Название кнопки-переключателя RBtns[i].Checked=(i==0); // Состояние переключателя // Положение и размер кнопки-переключателя: RBtns[i].SetBounds(10,30+30*i,100,20); // Добавление кнопки в группу переключателей: FName.Controls.Add(RBtns[i]); } // Название группы переключателей: FName.Text=MN[1]; // Положение и размер группы переключателей: FName.SetBounds(10,10,130,120); // Размещение группы переключателей // в окне формы: Controls.Add(FName); // Текстовая метка "Размер шрифта": Label lsize=new Label(); // Текстовое значение метки: lsize.Text=MN[3]+" (от "+min+" до "+max+"):"; Программный код и выполнение программы 293 // Положение и размеры области метки: lsize.SetBounds(150,20,180,20); // Добавление текстовой метки в окно формы: Controls.Add(lsize); // Создание текстового поле для ввода // размера текста: tsize=new TextBox(); // Начальное значение в текстовом поле: tsize.Text=min.ToString(); // Положение и размеры тестового поля: tsize.SetBounds(340,20,50,20); // Способ выравнивания текста в текстовом поле // (по правому краю): tsize.TextAlign=HorizontalAlignment.Right; // Добавление текстового поля в окно формы: Controls.Add(tsize); // Массив для кнопок-опций: CBtns=new CheckBox[FS.Length]; // Перебираем кнопки: for(int i=0;i CBtns[i]=new CheckBox(); // Создание объекта опции CBtns[i].Text="Применить стиль: "+FS[i]; // Текст опции CBtns[i].Checked=false; // Состояние опции CBtns[i].SetBounds(150,50+30*i,250,20); // Положение и размер // опции Controls.Add(CBtns[i]); // Добавление опции в окно формы } /* Блок с регистрацией обработчиков событий и сопутствующими командами. */ // Массив экземпляров делегатов: BH=new EventHandler[BN.Length]; BH[0]=OKButtonClick; // Экземпляр делегата для первой // кнопки BH[1]=CancelButtonClick; // Экземпляр делегата для второй // кнопки // Перебираем кнопки: for(int i=0;i Btns[i].Click+=BH[i]; // Регистрация обработчика для // кнопки } // Перебираем кнопки-переключатели: for(int i=0;i RBtns[i].CheckedChanged+=BH[0]; // Регистрация обработчика } продолжение 294 Глава 8. Приложение с графическим интерфейсом: учебный проект Листинг 8.1 (продолжение) // Присваиваем значение экземплярам делегатов: RBH=setType; // Экземпляр делегата для меню выбора // типа шрифта CBH=setStyle; // Экземпляр делегата для меню выбора // стиля шрифта TBH=setSize; // Экземпляр делегата для меню выбора // размера шрифта // Добавление главного меню в окно формы. // При вызове метода getMainMenu() используются // экземпляры делегатов // для обработчиков событий: Menu=getMyMenu(); // Применение шрифта к образцу текста: sample.Font=SFont; } } // Класс с главным методом программы: class FontApplyDemo { // Главный метод программы: public static void Main(){ // Отображаем окно формы: Application.Run(new MyForm()); } } Более детальный анализ некоторых фрагментов этого кода будет приве- ден несколько позже. Сейчас остановимся на том, как выполняется данная программа. Так, при запуске программы появляется окно, представленное на рис. 8.1. Рис. 8.1. Вид отображаемого при запуске программы окна формы Программный код и выполнение программы 295 Как уже отмечалось ранее, окно с названием Работаем со шрифтами содержит меню из четырех пунктов (Действие, Тип шрифта, Стиль шрифта и Размер шрифта), группу переключателей Тип шрифта на три положения (Arial, Times и Courier), поля с текстом Размер шрифта (от 10 до 20), двух опций (Применить стиль Жир- ный и Применить стиль Курсив), области образца текста с текстом Образец текста и двумя кнопками (Применить и Выход). Пункт меню Действие содержит две команды — Применить и Выход (рис. 8.2). Рис. 8.2. Команды пункта меню Действие Назначение команд такое же, как и одноименных кнопок. В пункте меню Тип шрифта три команды — Arial, Times и Courier (рис. 8.3). Рис. 8.3. Команды пункта меню Тип шрифта Названия команд не случайно совпадают с названиями переключателей в группе переключателей Тип шрифта. Выбор команды имеет такой же эф- фект, как и установка переключателя в одноименное положение. 296 Глава 8. Приложение с графическим интерфейсом: учебный проект В пункте меню Стиль шрифта всего две команды — Жирный и Курсив (рис. 8.4). Рис. 8.4. Команды пункта меню Стиль шрифта Выбор команды в пункте меню приводит к установке/отмене флажка соот- ветствующей опции в области окна формы. В отличие от группы переклю- чателей, изменение состояния опций к автоматическому изменению пара- метров шрифта не приводит. Для этого необходимо щелкнуть на кнопке Применить или выбрать команду Применить в пункте меню Действие. Это же замечание относится к командам пункта меню Размер шрифта (рис. 8.5). Рис. 8.5. Команды пункта меню Размер шрифта Список команд пункта меню Размер шрифта — это набор цифр в диапазоне от 10 до 20 включительно. Выбор команды в этом списке приводит к заполне- нию поля ввода соответствующим значением. Программный код и выполнение программы 297 Несколько следующих рисунков иллюстрируют функциональность окна формы. Так, на рис. 8.6 показано окно, у которого установлены опции при- менения жирного стиля и курсива, а в поле размера шрифта указано значе- ние 18 (настройки выполнены, но не применены). Рис. 8.6. Окно с выполненными настройками: для их применения щелкаем на кнопке Применить Для применения настроек щелкаем на кнопке Применить. Результат показан на рис. 8.7. Рис. 8.7. Результат применения настроек Изменение типа шрифта вступает в силу автоматически. На рис. 8.8 по- казан результат щелчка на переключателе Courier в группе переключателей Тип шрифта. 298 Глава 8. Приложение с графическим интерфейсом: учебный проект Рис. 8.8. При изменении типа шрифта изменения вступают в силу автоматически В принципе, поскольку поле ввода размера шрифта по своей природе тек- стовое, в него можно ввести все, что угодно, и не обязательно число. Такие ситуации обрабатываются корректно — вместо «непонятного» значения используется размер 10, причем выполняется автоматическая замена зна- чения в поле ввода. На рис. 8.9 показано окно формы с некорректным зна- чением в поле размера шрифта. Рис. 8.9. Окно перед применением настроек: в процессе выполнения настроек в поле размера введено некорректное значение После щелчка на кнопке Применить все корректные настройки вступают в силу, а в качестве размера шрифта используется значение 10 (рис. 8.10). Если в поле размера шрифта указать слишком большое (большее 20) зна- чение, при применении настроек оно «урезается» до 20. На рис. 8.11 в поле размера шрифта указано значение 10000. После применения настроек окно выглядит так, как показано на рис. 8.12. Программный код и выполнение программы 299 Рис. 8.10. Результат применения настроек с некорректным значением размера шрифта Рис. 8.11. Окно перед применением настроек: в поле размера шрифта введено слишком большое значение Рис. 8.12. Результат применения настроек со слишком большим значением размера шрифта 300 Глава 8. Приложение с графическим интерфейсом: учебный проект Интересно в данном случае то, что размер шрифта стал равен 20. Анало- гично обрабатывается ситуация, когда в поле размера шрифта указано слишком маленькое значение (меньшее 10). Разница в этом случае лишь такая, что применяется не «максимальный» шрифт 20, а «минимальный» шрифт 10. Наиболее значимые места программного кода Я стану этим... Вот этим... Нет, этим я не смогу. Впрочем, я стану другом короля! Из к/ф «Дон Сезар де Базан» В качестве финального штриха обсудим некоторые блоки или фрагменты кода, которые позволяют «зафиксировать» основные и «тонкие» места ис- пользованного нами алгоритма. Класс MyLabel нами уже упоминался. Объектная ссылка sample этого клас- са объявлена полем класса MyForm. Создание объекта класса выполняется в конструкторе класса MyForm командой sample=new MyLabel(100,140,290, 110). То есть область этой текстовой метки в окне формы имеют фиксиро- ванное положение и размер. Добавление метки в окно формы выполняется командой Controls.Add(sample). Здесь проиллюстрирован один достаточно продуктивный подход, который состоит в том, что для графических элементов с определен- ными характеристиками создается, путем наследования, специальный класс. Мы один раз в классе описываем характеристики и параметры элемента, а потом для создания элемента соответствующего типа и вида создаем объект данного класса. Хотя в нашем примере это не очень заметно, но на практике это очень удобно. Объект sample используется в методе OKButtonClick(). Метод содержит ко- манду sample.Font=SFont, которой свойству Font объекта sample в качестве значения присваивается значение свойства SFont. Эта же команда встре- чается в конструкторе класса MyForm (последняя команда). В конструк- торе команда нужна для того, чтобы для отображения образца текста по умолчанию использовался шрифт, соответствующий настройкам в окне. Метод OKButtonClick() является обработчиком события щелчка на кноп- ке Применить. Что касается свойства SFont, значение свойства формируется на основе настроек управляющих элементов в окне формы. Каждый раз, Наиболее значимые места программного кода 301 когда запрашивается это свойство (а это происходит при выполнении ме- тода OKButtonClick()), автоматически «считываются» настройки элемен- тов в окне формы и на их основе вычисляется нужный шрифт (создается объект шрифта). Что касается шрифта, применяемого в оконной форме, то он определяется командой Font=new Font("Arial",8,FontStyle.Bold), ко- торой свойству Font формы присваивается объект шрифта, создаваемый командой new Font("Arial",8,FontStyle.Bold). В данном случае речь идет о жирном шрифте типа Arial размера 8. Текстовые массивы MN, FN, FS и BN определяют, соответственно, названия пунктов главного меню, названия шрифтов, названия стилей шрифтов и названия кнопок. Эти массивы играют важную роль. Дело в том, что та- кие объекты, как кнопки Btns, радиокнопки (кнопки-переключатели) RBtns и опции CBtns, реализуются в виде массивов объектов (объектных пере- менных). Более того, внутренние команды пунктов главного меню также реализуются как массивы. И все соответствующие вычисления (в первую очередь те, что касаются количества элементов) выполняются на основе «базовых» текстовых массивов. ПРИМЕЧАНИЕ Несколько особо обстоят дела с текстовым массивом из «чисел». Массив возвращается как результат методом FSz(). В теле метода на основе значений целочисленных полей min и max создается тексто- вый массив размера max-min+1. Затем массив заполняется числами, преобразованными в текст, и возвращается в качестве результата. Поэтому, если нам нужен массив из текстовых представлений чисел в диапазоне от min до max, мы используем в качестве ссылки на такой массив инструкцию FSz(). В основном все эти действа происходят в конструкторе класса MyForm. Например, массив кнопок (массив объектных переменных) создается командой Btns=new Button[BN.Length]. Здесь размер массива кнопок со- впадает с размером массива названий кнопок, что вполне логично. Затем в операторе цикла индексная переменная i перебирает элементы кнопоч- ного массива, и за каждый цикл выполняется создание объекта (коман- да Btns[i]=new Button()), присваивание имени кнопке в соответствии с текстовым значением «базового» текстового массива (команда Btns[i]. Text=BN[i]), определение позиции и размеров кнопки (команда Btns[i]. SetBounds(10,140+i*40,80,30)) и добавление кнопки в окно формы (ко- манда Controls.Add(Btns[i])). Похожим образом все происходит и для кнопок-переключателей RBtns и опций CBtns, с поправкой на имя «базо- вого» текстового массива. Правда, у этих элементов задается еще свойство Checked, которое отвечает за состояние элемента (выделен или нет). Для оп- ций значение этого свойства устанавливается равным false (в начальный 302 Глава 8. Приложение с графическим интерфейсом: учебный проект момент опции не выделены), а для кнопок-переключателей значение свой- ства задается равным (i==0), в силу чего выделенным будет первый пере- ключатель (для которого индекс i равен нулю). Кнопки-переключатели необходимо объединить в группу, а уже потом группа переключателей добавляется в форму. Отдельные переклю- чатели добавляются не непосредственно в форму, а в группу пере- ключателей. В программе есть объект FName класса GroupBox. Метод Add() для отдельных кнопок-переключателей вызывается из объекта FName. А для добавления в форму группы, метод Add() с аргументом FName вызывается из объекта формы. Мы намеренно разнесли во времени и пространстве процесс создания гра- фических элементов и регистрацию обработчиков для элементов интер- фейса. В программе используются экземпляры делегата EventHandler BH (массив из экземпляров делегата для регистрации обработчиков щелчка на кнопках в области формы и команд первого пункта главного меню, ко- торые ссылаются на методы OKButtonClick() и CancelButtonClick()), RBH (экземпляр делегата для обработки выбора команд второго пункта меню со ссылкой на метод setType()), CBH (экземпляр делегата для обработки выбо- ра команд третьего пункта меню со ссылкой на метод setStyle()) и TBH (эк- земпляр делегата для обработки выбора команд четвертого пункта меню со ссылкой на метод setSize()). Для кнопок экземпляры делегата регистри- руются для события Click (происходит при щелчке на кнопке). Экземпляр делегата BH[0] регистрируется также для события CheckedChanged кнопок- переключателей (происходит при изменении статуса переключателя). По- скольку экземпляр делегата BH[0] регистрируется для кнопки Применить, изменение положения переключателей приводит к выполнению того же метода, что и щелчок на кнопке Применить. Прочие экземпляры делегата ис- пользуются при создании главного меню. И это отдельная история. Кульминацией процесса создания главного меню является команда Menu=getMyMenu() в конструкторе класса MyForm. Командой свойству Menu присваивается результат метода getMyMenu(). Несложно догадаться, что именно этим методом создается и возвращается в качестве результата главное меню формы. Метод в качестве результата возвращает объект класса MainMenu. В теле метода создается объект MyMenu класса MainMenu и массив mainMI объектов класса MenuItem. Это пункты главного меню. Каждый новый пункт главно- го меню добавляется методом Add() в коллекцию MenuItems объекта MyMenu. Метод Add() вызывается из коллекции MenuItems, которая является полем объекта MyMenu. Аргументом методу Add() передается добавляемый пункт меню (объект, соответствующий этому пункту). Наиболее значимые места программного кода 303 Заполнение командами каждого из пунктов главного меню выполняется с помощью метода setMyMenuItem(). Аргументами методу передаются объ- ект заполняемого пункта меню и список команд пункта меню (в виде тек- стового массива). ПРИМЕЧАНИЕ Метод setMyMenuItem() не возвращает результат. В теле метода созда- ется массив mi объектов класса MenuItem. Аргументом конструктору класса MenuItem передаются текстовые названия команд. Добав- ление команды меню в пункт меню выполняется через коллекцию MenuItems с помощью метода Add(). Кроме этого, для команд разных пунктов меню выполняется регистрация обработчиков событий. Здесь есть два важных момента. Во-первых, ко- манда вида mainMI[k].MenuItems[m] означает m+1-ю команду в k+1-м пункте главного меню, а событие Click для команды меню означает выбор пользо- вателем этой команды. Во-вторых, для всех пунктов меню, кроме началь- ного, для всех команд пункта меню в качестве обработчика регистрируется один и тот же метод. Поэтому такой метод должен уметь как-то различать разные команды в пределах пункта меню. В каждом методе-обработчике эта задача решается по-разному. Метод setType() вызывается для обработки выбора пункта меню, свя- занного с определением типа шрифта. В теле метода объект obj (аргу- мент), вызвавший событие, командой obj as MenuItem приводится к типу MenuItem и для этого объекта считывается свойство Text (название ко- манды). Затем с помощью оператора цикла ищется совпадение названия команды и названия кнопки-переключателя. Если совпадение найдено, устанавливается соответствующий переключатель. В результате для пе- реключателя происходит событие CheckedChanged, а на этот случай уже имеется обработчик. Метод setStyle() используется в качестве обработчика события выбора пункта меню, связанного с определением стиля шрифта. В этом случае определяется индекс команды пункта меню (свойство Index) и для опции в окне формы с таким же индексом статус меняется на противополож- ный — выделенная опция становится невыделенной, и наоборот. Метод setSize() используется для обработки события выбора пункта меню, связанного с определением размера шрифта. Здесь мы считываем название команды (свойство Text) и присваиваем его в качестве значения (свойство Text) текстовому полю (объект tsize-поле класса MyForm). Важную роль в программном коде играет свойство SFont. У свойства име- ется только get-аксессор, в котором на основе положения переключателей типа шрифта, состояния опций стиля шрифта и значения текстового поля 304 Глава 8. Приложение с графическим интерфейсом: учебный проект с размером шрифта формируется объект класса Font, который и возвраща- ется в качестве результата (значения свойства). При этом размер шрифта не просто считывается из текстового поля, но и обрабатывается. Для этого в программе предусмотрено свойство FSize. В единственном get-аксессоре этого свойства выполняется попытка преобразовать число в текст. За счет trycatch блока, если такая попытка неудачна, в качестве значения разме- ра шрифта используется минимально допустимое. Также отслеживаются случаи выхода значения размера шрифта за допустимые пределы. В случае если размер шрифта меньше минимально допустимого, искусственно ге- нерируется ошибка, которая перехватывается блоком trycatch. Слишком большие числовые значения отлавливаются с помощью условного опера- тора. В любом случае применяемый шрифт, если он не совпадает с перво- начально введенным пользователем, отображается в текстовом поле. ВМЕСТО ЗАКЛЮЧЕНИЯ Графический конструктор Пока это лекция. И даже скучная лекция. Из к/ф «В поисках капитана Гранта» В книгах Вступление и Заключение играют очень важную роль. Во Всту- плении обычно автор пытается убедить читателя, что именно эта книга чи- тателю нужна больше всего и именно из этой книги читатель почерпнет столько знаний, что прочие книги ему уже и не понадобятся. В Заключе- нии обычно дается краткое пояснение по поводу того, почему чуда не слу- чилось. Короче говоря, без Вступления и Заключения не обойтись никак. Мы постараемся отойти от канонов и употребить Заключение во благо, а не в наущение. Но мистическую связь Вступления и Заключения разрывать не будем. Во Вступлении мы самонадеянно утверждали, что к помощи гра- фического конструктора, встроенного в среду Visual C# Express, прибегать не будем. Здесь мы очень кратко покажем, как в графическом редакторе можно создать простенькое функциональное окно. Ну а пытливый чита- тель легко сможет экстраполировать подход и для создания более сложных приложений. 306 Вместо заключения. Графический конструктор Создание простого окна с кнопкой — Ну что, не передумали? — Мне выбирать не приходится. Из к/ф «Приключения Шерлока Холмса и доктора Ватсона. Знакомство» В качестве иллюстрации возможностей среды разработки Visual C# Express 2010 рассмотрим процесс создания простенького приложения с очень простым окном всего с одной кнопкой. Щелчок на кнопке приводит к тому, что окно закрывается, а приложение завершает работу. Итак, запускаем среду разработки Visual C# Express 2010. Для созда- ния нового проекта используем команду Создать новый проект из меню Файл. В качестве типа приложения указываем Приложение Windows Forms (рис. З.1). Рис. З.1. Создаем Windows-приложение По умолчанию новое приложение содержит окно формы (рис. З.2). Если это графическое окно выделить (выбрать) мышкой, можно путем перетаскивания границ изменить размеры этого окна по желанию пользо- вателя. Так же легко выполняются и прочие настройки окна формы. Для Создание простого окна с кнопкой 307 этого нам понадобится окно свойств. Отобразить окно можно с помощью команды ВидДругие окнаОкно свойств. Например, на рис. З.3 показано, как устанавливается значение Text для окна формы. Рис. З.2. Изменяем размеры оконной формы Рис. З.3. Свойство Text определяет заголовок окна формы 308 Вместо заключения. Графический конструктор Это свойство определяет заголовок окна. В окне свойств содержится также набор из огромного количества свойств оконной формы, которые опреде- ляют ее вид и функциональность. Исключительно легко в оконную форму добавляются всевозможные функциональные компоненты. Для этого на панели элементов выбирает- ся пиктограмма добавляемого элемента, и затем мышкой в области формы выделяется область, куда будет помещен элемент. Панель элементов можно открыть с помощью команды ВидДругие окнаПанель элементов. На рис. З.4 на панели элементов выбирается элемент Label, что соответству- ет текстовой метке. Рис. З.4. Выбираем объект Label для добавления в окно формы Иллюстрация процесса размещения текстовой метки в области окна фор- мы представлена на рис. З.5. По умолчанию кнопка имеет банальное содержимое, которое имеет смысл заменить. За содержимое текстовой метки ответственно свойство Text. В окне свойств задаем значение этого свойства для метки, как показано на рис. З.6. Создание простого окна с кнопкой 309 Рис. З.5. Размещение в окне формы текстовой метки Рис. З.6. Свойство Text текстовой метки определяет ее содержимое У каждого компонента свой набор свойств. Поэтому, изменяя на- стройки того или иного компонента, следует следить за тем, чтобы на- стройки выполнялись в окне свойств именно для этого компонента. Свойство Font определяет параметры шрифта, который применяется для отображения содержимого текстовой метки. На рис. З.7 показано, как на- страивается шрифт текстовой метки. 310 Вместо заключения. Графический конструктор Рис. З.7. Задаем свойства метки (текст и шрифт) Как мы и обещали, в оконную форму добавляем кнопку. Для этого в окне панели элементов необходимо выбрать элемент Button (рис. З.8). Рис. З.8. Выбираем для вставки в форму объект кнопки Button Создание простого окна с кнопкой 311 Процесс размещения кнопки в окне формы показан на рис. З.9. Рис. З.9. Добавление кнопки в окно формы Как и в случае текстовой метки, свойства кнопки придется настраивать. Для кнопки мы задаем свойство Text (название кнопки) и свойство Font (шрифт для отображения названия). Эти свойства настраиваются, как не- сложно догадаться, в окне свойств, открытом для кнопочного компонента (рис. З.10). Рис. З.10. Настройка параметров кнопки (текст кнопки и шрифт) На этом настройка внешнего вида формы закончена. Теперь еще необхо- димо «научить» кнопку реагировать на щелчок. Для этого в режиме гра- фического конструктора выполняем мышкой двойной щелчок на кнопке. 312 Вместо заключения. Графический конструктор В результате мы автоматически окажемся переброшенными к программ- ному коду обработчика щелчка на кнопке. Там вся оболочка уже есть, и нам предстоит добавить лишь непосредственно те команды, которые должны выполняться при щелчке на кнопке. Мы хотим, чтобы приложение в этом случае завершало работу. Поэтому вводим уже знакомую нам команду, представленную в листинге З.1. Листинг З.1. Команда, вводимая в обработчик щелчка на кнопке Application.Exit(); То место, куда вводится эта команда, показано и специально выделено в документе на рис. З.11. Рис. З.11. Добавляем программный код для обработки щелчка на кнопке В принципе, еще нужен программный код, который будет отображать оконную форму при запуске приложения. Но этот код генерируется авто- матически. Увидеть его можно, выполнив двойной щелчок на пиктограмме Program.cs в окне Обозреватель решений (рис. З.12). Самая главная команда этого кода выделена. Нам она тоже знакома (см. ли- стинг З.2). Листинг З.2. Команда, которой отображается форма (предлагается по умолчанию) Application.Run(new Form1()); Создание простого окна с кнопкой 313 Рис. З.12. Здесь ничего добавлять не нужно — все добавлено без нас Собственно, приложение готово к использованию. При запуске приложе- ния открывается окно, представленное на рис. З.13. Рис. З.13. При щелчке на кнопке Закрыть окно закрывается Если в этом окне щелкнуть на кнопке Закрыть, окно закроется. По тому же принципу создаются и более сложные оконные формы. Весь процесс сводится к размещению в окне формы нужных элементов, настройке их свойств и составлению программного кода обработчиков событий. Алфавитный указатель А главный, 32, 34, 58 Аксессор, 176, 184 обобщенный, 256 операторный, 144 Д перегрузка, 35, 41, 60 переопределение, 35, 85, 90, 164, Делегат, 32, 175, 193 172, 219 Деструктор, 64, 70 сигнатура, 60 статический, 32 З Замещение членов, 85, 90 Н Наследование, 72 И многоуровневое, 83 Индексатор, 32, 175, 184 Небезопасный код, 141 Инструкция безусловного перехода, 115 О Интерфейс, 85, 211, 227 Объект, 31, 56 Интерфейсная переменная, 85, 238 анонимный, 192, 197 Исключительная ситуация, 47, 50, Объектная переменная, 56, 57, 81, 116, 265 85, 134, 214, 238, 245 ООП, 8, 34, 193 К Оператор Класс, 30, 54 выбора, 110 абстрактный, 211, 218 перегрузка, 108, 143, 163 базовый, 73, 81 приведения типа, 157, 168, 173 обобщенный, 256, 259 присваивания, 101, 102, 107 оболочка, 99 тернарный, 101, 107 производный, 73, 81 условный, 48, 96, 107, 108, 116 Комментарий, 30 цикла, 47, 112, 113, 114, 116, 133 Константа, 41 Конструктор, 56, 64, 72, 168 П базового класса, 77 Переменная массива, 126, 134 создания копии, 68 Перечисление, 41, 96, 211 статический, 97 Поле, 32 Поток, 273 М Пространство имен, 33 Массив, 125 Метод, 32 Р абстрактный, 218 Рекурсия, 96 виртуальный, 90 Алфавитный указатель 315 С Свойство, 32, 175 Событие, 32, 175, 199, 203 Статический член, 93 Структура, 211, 214 У Указатель, 140 Ц Цикл, 47 Алексей Николаевич Васильев C#. Объектно-ориентированное программирование: Учебный курс Заведующий редакцией А . Кривцов Руководитель проекта А . Юрченко Ведущий редактор Ю . Сергиенко Литературный редактор О . Некруткина Художественный редактор К . Радзевич Корректор И . Тимофеева Верстка Л . Волошина ООО «Мир книг», 198206, Санкт-Петербург, Петергофское шоссе, 73, лит. А29. Налоговая льгота — общероссийский классификатор продукции ОК 005-93, том 2; 95 3005 — литература учебная. Подписано в печать 05.03.12. Формат 70х100/16. Усл. п. л. 25,800. Тираж 2000. Заказ 0000. Отпечатано по технологии CtP в ОАО «Первая Образцовая типография», обособленное подразделение «Печатный двор». 197110, Санкт-Петербург, Чкаловский пр., 15. Document Outline Вместо вступления. Язык программирования C# Краткий курс истории языкознания Особенности и идеология C# Программное обеспечение Установка Visual C# Express Немного о книге Благодарности От издательства Глава 1. Информация к размышлению: язык C# и даже больше Очень простая программа Несколько слов об ООП Еще одна простая программа Консольная программа Глава 2. Классы и объекты Описание класса Объектные переменные и создание объектов Перегрузка методов Конструкторы и деструкторы Наследование и уровни доступа Объектные переменные и наследование Замещение членов класса и переопределение методов Статические члены класса Глава 3. Основы синтаксиса языка C# Базовые типы данных и основные операторы Основные управляющие инструкции Массивы большие и маленькие Массивы экзотические и не очень Знакомство с указателями Глава 4. Перегрузка операторов Операторные методы и перегрузка операторов Перегрузка арифметических операторов и операторов приведения типа Перегрузка операторов отношений Глава 5. Свойства, индексаторы и прочая экзотика Свойства Индексаторы Делегаты Знакомство с событиями Элементарная обработка событий Глава 6. Важные конструкции Перечисления Знакомство со структурами Абстрактные классы Интерфейсы Интерфейсные переменные Глава 7. Методы и классы во всей красе Механизм передачи аргументов методам Аргументы без значений и переменное количество аргументов Передача типа в качестве параметра Использование обобщенного типа данных Обработка исключительных ситуаций Многопоточное программирование Глава 8. Приложение с графическим интерфейсом: учебный проект Общие сведения о графических элементах Программный код и выполнение программы Наиболее значимые места программного кода Вместо заключения. Графический конструктор Создание простого окна с кнопкой