| Китай |
Разработка комбинированного компонента
Тестирование скроллирующей кнопки ArrowButton
-
Откройте
файл Form2.cs в режиме View Designer
-
Откомпилируйте
проект пользовательской библиотеки UserControls,
чтобы в панели Toolbox появился разработанный компонент ArrowButton
-
Поместите
на Form2 четыре компонента ArrowButton
-
Переведите
форму в режим View Code и
настройте программно (в
отличие от декларативного способа через панель Properties )
свойство ScrollButton компонентов
на разные значения в конструкторе класса Form2
namespace MyCompany.StudName
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent(); // Выполнение кода, сгенерированного
// визуальным конструктором оболочки
// в декларативном режиме проектирования
// и расположенного в файле Form2.Designer.cs
// Настройка ориентации стрелок на скроллирующих кнопках
arrowButton1.ScrollButton = ScrollButton.Right;
arrowButton2.ScrollButton = ScrollButton.Left;
arrowButton3.ScrollButton = ScrollButton.Up;
arrowButton4.ScrollButton = ScrollButton.Down;
}
}
}
Листинг
19.17.
Настройка ориентации стрелок в конструкторе класса Form2
-
Запустите
приложение и посмотрите на результат, который будет таким
Упражнение 3. Комбинирование элементов в единый компонент NumericScan
Теперь мы готовы сконструировать аналог библиотечного компонента NumericUpDown - поле со списком, только с горизонтальным расположением скролирующих кнопок.
-
В панели Solution
Explorer вызовите контекстное меню
для узла UserControls и
добавьте к проекту командой Add/User
Control форму
для размещения нового комбинированного компонента с именем NumericScan.
Отметьте для себя, что в качестве базового в заготовке класса
наследуется UserControl
-
Перенесите
на форму NumericScan.cs в режиме View Designer из
панели Toolbox два наших компонента ArrowButton и
один библиотечный компонент TextBox. На
данном этапе особо не заботьтесь о размещение экземпляров
компонентов на форме. Мы это действие выполним позже программным
способом. А пока расположите объекты компонента примерно
так
Далее мы часть настроек выполним декларативно через панель Properties, а часть - программно в конструкторе компонента.
-
Через
панель Properties выполните декларативные
настройки объектов в соответствии с приведенной таблицей
свойств
-
Через
панель Properties в режиме Events зарегистрируйте
обработчики для составляющих объектов в соответствии с таблицей
Все остальные настройки составляющих объектов и самого компонента выполним программно.
-
Переведите
редактирование файла NumericScan.cs в режим View
Code и добавьте в конструктор компонента код
вычисления размеров формы
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
namespace MyCompany.StudName
{
public partial class NumericScan : UserControl
{
public NumericScan()
{
InitializeComponent();
// Вычисляем и устанавливаем размеры компонента
// Четыре высоты текущего шрифта
this.Width = 4 * this.Font.Height;
// Предпочтительная высота текстового поля +
// высота горизонтальных линеек прокрутки
this.Height = txtBox.PreferredHeight +
SystemInformation.HorizontalScrollBarHeight;
}
// При завершении ввода клавишей Enter или потери фокуса
private void TextBoxOnTextChanged(object sender, EventArgs e)
{
}
// При нажатии любой клавиши, когда объект имеет фокус ввода
private void TextBoxOnKeyDown(object sender, KeyEventArgs e)
{
}
// Щелчки на скроллирующих кнопках
private void ButtonOnClick(object sender, EventArgs e)
{
}
}
}
Листинг
19.18.
Вычисление и установка размеров компонента NumericScan
-
Добавьте
в класс NumericScan закрытые поля для сохранения
настроек компонента, а также оберните их общедоступными
свойствами
public partial class NumericScan : UserControl
{
// Объявили базовые закрытые поля для открытых свойств
int iDecimalPlaces = 0; // Количество знаков после запятой
decimal mValue = 0; // Значение текстового поля
decimal mIncrement = 1; // Шаг изменения
decimal mMinimum = 0; // Минимальное вещественное значение
decimal mMaximum = 100; // Максимальное вещественное значение
// Свойства доступа к полям
public int DecimalPlaces
{
get { return iDecimalPlaces; }
set { iDecimalPlaces = value; }
}
public decimal Value
{
get { return mValue; }
set { txtBox.Text = (mValue = value).ToString(); }
}
public decimal Increment
{
get { return mIncrement; }
set { mIncrement = value; }
}
public decimal Minimum
{
get { return mMinimum; }
set
{
// Контроль нижней границы диапазона
if (Value < (mMinimum = value))
Value = mMinimum;
}
}
public decimal Maximum
{
get { return mMaximum; }
set
{
// Контроль верхней границы диапазона
if (Value > (mMaximum = value))
Value = mMaximum;
}
}
..........................................
}
Листинг
19.19.
Поля для хранения настроек компонента NumericScan и свойства доступа
Обратите внимание, что свойство Value в аксессоре set выполняет двойное присваивание: текстовому полю и полю класса.
-
Начните
с ввода ключевого слова override и переопределите
в классе NumericScan унаследованную виртуальную
функцию GetPreferredSize() вычисления начальных размеров
компонента при создании его экземпляра
// Срабатывает автоматически и устанавливает начальные размеры
// экземпляра компонента this.Width, this.Height при его
// создании или помещении на форму в режиме проектирования
public override Size GetPreferredSize(Size proposedSize)
{
return new Size(4 * this.Font.Height, // Ширина
txtBox.PreferredHeight +
SystemInformation.HorizontalScrollBarHeight);
}
Листинг
19.20.
Вычисление начальных размеров компонента
-
Начните
с ввода ключевого слова override и переопределите
в классе NumericScan унаследованную виртуальную
функцию OnResize() вычисления локализации и размеров дочерних
объектов компонента
// Срабатывает автоматически, позиционирует
// дочерние объекты и устанавливает их размеры
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
txtBox.Location = new Point(0, 0); // В левом верхнем углу
txtBox.Size = new Size(this.Width, txtBox.PreferredHeight); // По всей ширине
btnLeft.Location = new Point(0, txtBox.Height); // Позиция
btnRight.Location = new Point(this.Width / 2, txtBox.Height); // Позиция
btnLeft.Size = btnRight.Size = new Size(this.Width / 2,
this.Height - txtBox.Height); // Одинаковый размер
}
Листинг
19.21.
Размещение дочерних объектов в функции OnResize()
Поддержку интерфейсного вида компонента мы обеспечили. Теперь нужно согласовать логику работы кнопок и текстового поля. Начнем с обработки события щелчка на кнопках. Для этого мы предусмотрели общий обработчик ButtonOnClick(). В нем нужно распознать нажатую кнопку и изменить значение текстового поля, а также сохранить это значение во внутреннем поле mValue класса. Но прежде объявим событие, которое будет информировать клиента, который будет создавать экземпляр компонента, об изменении значения поля компонента, а также функцию, которая будет инициировать генерацию этого события.
-
Объявите
в классе NumericScan с помощью библиотечного
делегата EventHandler событие с именем ValueChanged и
определите функцию OnValueChanged() диспетчеризации этого
события. Функцию диспетчеризации объявите виртуальной на
случай дальнейшего наследования нашего компонента
// Объявили пользовательское событие
public event EventHandler ValueChanged;
// Ввели свою функцию диспетчеризации события ValueChanged
// изменения величины прямым вводом в текстовое поле
protected virtual void OnValueChanged(EventArgs args)
{
// Последовательно проверяем выход величины за левую и правую
// границы, и если выходит за границу, то обрезаем по границе
Value = Math.Min(mMaximum, mValue);
Value = Math.Max(mMinimum, mValue);
// Округляет вещественный денежный тип до заданного
// количества значащих цифр после запятой
Value = Decimal.Round(mValue, iDecimalPlaces);
// Генерируем событие
if (ValueChanged != null)
ValueChanged(this, args);
}
Листинг
19.22.
Добавление в класс NumericScan события и функции его диспетчеризации
-
Задайте
в теле обработчика ButtonOnClick()
код, который будет контролировать изменения внутреннего и
текстового полей компонента при щелчках на кнопках, а также
извещать подписавшегося клиента об этих изменениях
// Щелчки на скроллирующих кнопках
private void ButtonOnClick(object sender, EventArgs e)
{
// Повышаем полномочия ссылки на объект скролирующей кнопки
ArrowButton btn = (ArrowButton)sender;
// Создаем пробную переменную
decimal tmpValue = mValue;
// Идентифицируем кнопку и меняем значение поля
if (btn == btnLeft)
{
if ((tmpValue -= mIncrement) < mMinimum)
return;
}
else // Других кнопок нет
{
if ((tmpValue += mIncrement) > mMaximum)
return;
}
// Обновляем внутреннее и текстовое поля
this.Value = tmpValue;
// Генерируем событие, извещающее подписавшегося
// клиента о произошедшем изменении значения счетчика
OnValueChanged(EventArgs.Empty);
}
Листинг
19.23.
Обработчик щелчков на кнопках компонента
-
Заполните
обработчик TextBoxOnTextChanged() кодом,
который будет обновлять внутреннее поле при его корректном
изменении или восстанавливать из него корректное значение
текстового поля при неправильном заполнении
// При завершении ввода клавишей Enter или потери фокуса
private void TextBoxOnTextChanged(object sender, EventArgs e)
{
if (txtBox.Text.Length == 0)
return;
// При неудачной попытке преобразования восстанавливаем старое
decimal tmpValue;
if (!Decimal.TryParse(txtBox.Text, out tmpValue))
txtBox.Text = mValue.ToString();
else
mValue = tmpValue;
}
Листинг
19.24.
Обработчик для обновления внутреннего поля из текстового
Когда компонент теряет фокус, возбуждается событие Leave, которое инициируется методом диспетчеризации OnLeave(). В нашем случае тоже нужно обновить состояние компонента и известить подписавшегося клиента.
// При уходе с компонента
protected override void OnLeave(EventArgs e)
{
base.OnLeave(e);
// Возбуждаем событие и обновление состояния компонента
OnValueChanged(EventArgs.Empty);
}
Листинг
19.25.
Переопределение виртуального метода OnLeave() потери компонентом фокуса
-
Заполните
обработчик TextBoxOnKeyDown() кодом, который
будет контролировать нажатие клавиши Enter в
текстовом поле и обновлять состояние компонента
// При нажатии любой клавиши, когда объект имеет фокус ввода
private void TextBoxOnKeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Enter:
OnValueChanged(EventArgs.Empty);
break;
}
}
Листинг
19.26.
Обработчик нажатия клавиши Enter


