netlib.narod.ru | < Назад | Оглавление | Далее > |
В программе CheckBoxWithLabel определено два массива: один с текстовыми строками для четырех флажков, а второй содержит соответствующие значения перечисления FontStyle. Увы, эти массивы определены в разных областях программы. Если изменить порядок элементов в одном массиве, не изменив порядка в другом, программа будет работать неправильно.
Более того: сравните, как свойство Controls индексируется в методе OnPaint программы CheckBoxDemo и как оно индексируется в методе CheckBoxOnCheckedChanged программы CheckBoxWithLabel. В первой программе индексы находятся в диапазоне от 0 до 3. Во второй первым дочерним элементом формы является Label, и, таким образом, он имеет индекс 0. Элементы управления CheckBox индексируются с 1 до 4.
Если изменить в CheckBoxWithLabel конструктор, так чтобы элемент Label делался дочерним элементом формы после элемента CheckBox, программа будет работать неправильно. Я уж не говорю о том, что индексирование элементов массива Controls в зависимости от порядка их создания — плохой стиль программирования. Для программы с несколькими элементами управления это может быть не так ужасно. Но если элементов много, это может стать настоящим кошмаром.
Отслеживать все создаваемые элементы управления можно несколькими способами. Например, хранить объекты — элементы управления в полях, как в программе TwoButtons. Можно устанавливать отдельный обработчик событий для каждого элемента. Другой подход — использовать для уникальной идентификации свойства (или что-нибудь вроде этого) каждого элемента. Конечно, свойство Text обычно уникально идентифицирует элемент управления, и в обработчике события можно задействовать текст элемента в инструкции switch для определения того, какой элемент вызвал событие. Но при этом, если вам когда-либо понадобится изменить текст элемента управления, вам придется менять как код, присваивающий свойство Text элемента, так и конструкцию switch–case в обработчике события.
Итак, для идентификации объекта свойства Text недостаточно. А что было бы достаточно? Класс Control содержит свойство, которое можно установить равным любому подходящему объекту:
Свойства Control (выборочно)
Тип | Свойство | Доступ |
object | Tag | Чтение/запись |
Это свойство специально предназначено для идентификации элементов управления. При создании элемента управления можно присвоить свойству Tag любой объект. К примеру, приведу частичное определение элемента управления CheckBox, предназначенного для выбора цвета:
chkbox.Text = "Magenta"; chkbox.Tag = Color.Magenta;
В обработчике события прежде всего следует получить элемент управления CheckBox, вызвавший событие:
CheckBox chkbox = (CheckBox)obj;
а затем привести свойство Tag к объекту Color:
Color clr = (Color)Tag;
Если Tag не является объектом Color, будет сгенерировано исключение. Пример использования свойства Tag я приведу в программе AutoScaleDemo чуть позже.
Что приятно в объектно-ориентированном программировании вообще (и в Windows Forms в частности), это то, что для идентификации элемента управления вы легко можете добавлять к нему все, что угодно.
В следующей программе создается новый класс — наследник CheckBox, предназначенный для вывода стилей шрифта. В новом классе добавляется всего одно поле — fontstyle типа FontStyle. Вы можете видеть (в конце листинга), что определение этого нового класса требует минимального объема кода.
CustomCheckBox.cs
//----------------------------------------------- // CustomCheckBox.cs (C) 2001 by Charles Petzold //----------------------------------------------- using System; using System.Drawing; using System.Windows.Forms; class CustomCheckBox: Form { public static void Main() { Application.Run(new CustomCheckBox()); } public CustomCheckBox() { Text = "Custom CheckBox Demo"; int cyText = Font.Height; int cxText = cyText / 2; FontStyle[] afs = { FontStyle.Bold, FontStyle.Italic, FontStyle.Underline, FontStyle.Strikeout }; Label label = new Label(); label.Parent = this; label.Text = Text + ": Sample Text"; label.AutoSize = true; for (int i = 0; i < 4; i++) { FontStyleCheckBox chkbox = new FontStyleCheckBox(); chkbox.Parent = this; chkbox.Text = afs[i].ToString(); chkbox.fontstyle = afs[i]; chkbox.Location = new Point(2 * cxText, (4 + 3 * i) * cyText / 2); chkbox.Size = new Size(12 * cxText, cyText); chkbox.CheckedChanged += new EventHandler(CheckBoxOnCheckedChanged); } } void CheckBoxOnCheckedChanged(object obj, EventArgs ea) { FontStyle fs = 0; Label label = null; for (int i = 0; i < Controls.Count; i++) { Control ctrl = Controls[i]; if (ctrl.GetType() == typeof(Label)) label = (Label) ctrl; else if (ctrl.GetType() == typeof(FontStyleCheckBox)) if (((FontStyleCheckBox) ctrl).Checked) fs |= ((FontStyleCheckBox) ctrl).fontstyle; } label.Font = new Font(label.Font, fs); } } class FontStyleCheckBox: CheckBox { public FontStyle fontstyle; }
В конструкторе определяется массив значений FontStyle. При создании каждого объекта FontStyleCheckBox его полю fontstyle присваивается значение типа FontStyle. В программе не используется массив string. Вместо этого значение FontStyle преобразуется в строку, а свойству Text присваивается эта строка. Хотя массив строк по-прежнему необходим, по крайней мере оба массива определяются в одном фрагменте программы и при необходимости могут быть изменены одновременно.
Метод CheckBoxOnCheckedChanged циклически обрабатывает все элементы управления в массиве Controls и определяет тип каждого из них. Для определения типа объекта можно применять метод GetType, кроме того, можно использовать оператор typeof языка С# с любым именем класса для определения его типа. Если элемент управления имеет тип FontStyleCheckBox, объект приводится к этому типу, и если свойство Checked равно true, метод обращается к его полю fontstyle. Если элемент имеет тип Label, объект Label сохраняется, и обработка события завершается присвоением его свойству Font нового шрифта. Можно изменять значения FontStyle и порядок создания элементов и установления их родительской формы, при этом переделок кода не потребуется.
netlib.narod.ru | < Назад | Оглавление | Далее > |