Опубликован: 13.07.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 24:

Пользовательские компоненты

Тестирование компонента AlarmClock

  • Поместите новый компонент AlarmClock из панели Toolbox на форму Form1 приложения ComponentTest, выделите его экземпляр alarmClock1 и через панель Properties в режиме Events создайте обработчик для события Alarm
  • Обработчик, добавленный в класс Form1 приложения ComponentTest, заполните следующим кодом
private void alarmClock1_Alarm(object sender, AlarmClock.AlarmEventArgs e)
        {
            // Если оператор выключения будильника поставить после
            // вызова модального диалогового окна, то генерация
            // событий прекратиться только после закрытия
            // очередного модального окна, поскольку только тогда
            // управление дойдет до этого оператора
            alarmClock1.Enabled = false;// Останавливаем системный таймер
    
            int hour = alarmClock1.CurrentTime.Hour;
            int minute = alarmClock1.CurrentTime.Minute;
            int second = alarmClock1.CurrentTime.Second;
            MessageBox.Show(String.Format("Сработал будильник!!!\n"
                + "Текущее время: {0} ч. {1} м. {2} с.", hour, minute, second),
                "Доцент Снетков В.М.");
        }
Листинг 24.15. Обработчик события Alarm в тестирующем классе Form1
  • Добавьте в конец конструктора формы Form1 код установки времени срабатывания будильника и запуска системного таймера
// Конструктор формы
        public Form1()
        {
            InitializeComponent();
    
            // Заполнение списка планетами
            for (int i = 0; i <= planets1.MaxIndex; i++)
            {
                listPlanets.Items.Add(String.Format(
                    "{0}) {1}", i, planets1[i]));
            }
    
            listPlanets.SelectedIndex = 0;
    
            // Устанавливаем время срабатывания будильника 
            // через 5 сек. после запуска системного таймера
            alarmClock1.AlarmTime = DateTime.Now.AddSeconds(5d);
            alarmClock1.Enabled = true;// Запускаем системный таймер
        }
Листинг 24.16. Конструктор формы Form1 файла Form1.cs приложения ComponentTest
  • Запустите приложение и убедитесь, что обработчик нашего события Alarm срабатывает через 5 секунд после запуска системного таймера, генерируя окно с примерно следующим сообщением


Добавление в компонент пользовательского события расширенным способом

Рассмотрим другой способ работы с событиями, использующий базовое поле типа делегата. Для этого модифицируем наш компонент таким образом, чтобы будильник срабатывал два раза: по первому и второму событию. Но второе событие определим по иному.

  • Перейдите в режим редактирования компонента AlarmClock.cs [Design] и добавьте из панели Toolbox двойным щелчком кнопки мыши еще один системный таймер Timer
  • Выделите экземпляр таймера timer2 и в панели Properties установите свойство Interval=1000
  • Создайте для объекта timer2 обработчик события Tick с именем TimerHandlerExt, который переместите в отдельную часть класса AlarmClock в файле AlarmClock.cs. Заполните эту часть следующим кодом
// Часть 5
namespace MyCompany.MyComponents
{
    // Часть класса с определением обработчика события Tick 
    // системного таймера System.Windows.Forms.Timer timer2
    partial class AlarmClock
    {
        // Объявляем внутренние поля
        bool alarmFiredExt = false; // Состояние будильника (запущен/незапущен)
        DateTime alarmTimeExt = DateTime.Now; // Хранит время запуска будильника
    
        // Свойство для чтения и установки времени запуска будильника
        public DateTime AlarmTimeExt
        {
            get { return alarmTimeExt; }
            set
            {
                if (value != alarmTimeExt)
                {
                    alarmTimeExt = value;
                    alarmFiredExt = false;
                }
            }
        }
    
        // Свойство для проверки состояния хода второго
        // системного таймера, его остановки и запуска
        public bool EnabledExt
        {
            get { return timer2.Enabled; }
            set
            {
                timer2.Enabled = value;
                if (value)
                    alarmFiredExt = false;
            }
        }
        
        // Обработчик события срабатывания второго системного таймера
        private void TimerHandlerExt(object sender, EventArgs e)
        {
            DateTime now; // Текущее системное время
            AlarmEventArgs args;
    
            // В режиме разработки не выполнять
            if (!this.DesignMode)
            {
                now = DateTime.Now;
                // Если будильник не запущен и пришла пора запускать
                if (!alarmFiredExt && now >= alarmTimeExt)
                {
                    // Создаем объект для передачи аргументов в событии
                    args = new AlarmEventArgs();
                    // Заполняем объект текущим временем
                    args.Time = now;
                    // Вызываем метод диспетчеризации события AlarmExt
                    this.OnAlarmExt(args);
    
                    // Поднимаем флаг "Будильник запущен"
                    alarmFiredExt = true;
                }
            }
        }
    }
}
Листинг 24.17. Часть класса с обработчиком таймера timer2 в файле AlarmClock.cs
  • Создайте часть класса компонента, объявляющего пользовательское событие на базе внутреннего поля-ссылки на экземпляр делегата, который будет содержать список зарегистрированных обработчиков этого события
// Часть 6 Extension
namespace MyCompany.MyComponents
{
    // Часть класса с определением собственного события
    // стандартным способом без использования базового поля
    partial class AlarmClock
    {
        // Закрытое поле-ссылка на экземпляр делегата
        // Для объявления поля решили использовать уже существующий 
        // делегат, но можно объявить и другой делегат с той же сигнатурой
        private AlarmHandler m_AlarmExt;
    
        // Объявляем пользовательское событие
        // с возможностью контроля доступа к нему
        public event AlarmHandler AlarmExt
        {
            // Эта функция будет вызвана при  попытке 
            // добавления обработчика в список вызова события
            add
            {
                // Здесь что-то можно проконтролировать, например, что
                // произошла попытка добавить обработчик в список вызова события
                Console.WriteLine("Добавлен обработчик в список события AlarmExt");
    
                // Расширяем список объекта-делегата, ссылающегося на обработчики события
                m_AlarmExt += value;
            }
            // Эта функция будет вызвана при попытке 
            // изъятия обработчика из списка вызова события
            remove
            {
                // Здесь что-то можно проконтролировать, например, что
                // произошла попытка изъять обработчик из списка вызова события
                Console.WriteLine("Изъят обработчик из списка события AlarmExt");
    
                // Удалим обработчик из списка объекта-делегата
                m_AlarmExt -= value;
            }
        }
    
        // Метод диспетчеризации события. Виртуальный и защищенный
        // для возможности переопределения в будущих потомках
        protected virtual void OnAlarmExt(AlarmEventArgs args)
        {
            // Проверяем наличие зарегистрированных обработчиков 
            // и генерируем событие через поле-делегат
            if (m_AlarmExt != null)
                m_AlarmExt(this, args);
        }
    }
}
Листинг 24.18. Часть файла AlarmClock.cs объявления события с контролем списка делегата
  • Откомпилируйте библиотеку наших компонентов, выполнив команду Build/Build MyComponents
  • Перейдите в файл Form1.cs [Design] и добавьте в конструктор класса тестового приложения код настройки второго системного таймера экземпляра компонента alarmClock1
// Конструктор формы
        public Form1()
        {
            InitializeComponent();
    
            // Заполнение списка планетами
            for (int i = 0; i <= planets1.MaxIndex; i++)
            {
                listPlanets.Items.Add(String.Format(
                    "{0}) {1}", i, planets1[i]));
            }
    
            listPlanets.SelectedIndex = 0;
    
            // Устанавливаем время срабатывания будильника 
            // через 5 сек. после запуска системного таймера
            alarmClock1.AlarmTime = DateTime.Now.AddSeconds(5d);
            alarmClock1.Enabled = true;// Запускаем системный таймер
    
            // Настраиваем второй системный таймер компонента
            alarmClock1.AlarmTimeExt = DateTime.Now.AddSeconds(10d);
            alarmClock1.EnabledExt = true;// Запускаем системный таймер
        }
Листинг 24.19. Конструктор формы Form1 файла Form1.cs приложения ComponentTest
  • На форме Form1 приложения ComponentTest выделите экземпляр alarmClock1 и через панель Properties в режиме Events создайте обработчик для события AlarmExt
  • Обработчик заполните следующим кодом
private void alarmClock1_AlarmExt(object sender, AlarmClock.AlarmEventArgs e)
        {
            alarmClock1.EnabledExt = false;// Останавливаем второй системный таймер
    
            int hour = alarmClock1.CurrentTime.Hour;
            int minute = alarmClock1.CurrentTime.Minute;
            int second = alarmClock1.CurrentTime.Second;
            MessageBox.Show(String.Format("Сработал будильник!!!\n"
                + "Текущее время: {0} ч. {1} м. {2} с.", hour, minute, second),
                "Доцент Снетков В.М.");
    
            // Для генерации сообщения в консольный вывод
            alarmClock1.AlarmExt -= alarmClock1_AlarmExt;
        }
Листинг 24.20. Обработчик события AlarmExt в тестирующем классе Form1
  • В панели Solution Explorer вызовите контекстное меню для узла приложения ComponentTest и выполните команду Properties
  • В появившемся окне настроек оболочки выберите вкладку Application и в раскрывающемся списке Output type включите окно консольного вывода Console Application

  • Запустите приложение и убедитесь, что оба способа определения пользовательского события работают одинаково, но в последнем способе мы можем контролировать процесс добавления обработчиков


Иван Циферблат
Иван Циферблат
Россия, Таганрог, 36, 2000