Инициирование событий из кода класса
Это достаточно сложная тема и ее можно было бы благополучно пропустить. Инициирование событий из кода класса — не очень распространенная практика. Так как классы не имеют видимого интерфейса пользователя, то не существует внешних событий, реагируя на которые можно вызывать их обработчики. В следующей главе описано, как генерировать ошибки из кода элементов управления ActiveX. Элементы управления ActiveX имеют видимый интерфейс и должны реагировать на многие внешние события, такие как события мыши и клавиатуры. Как станет ясно, инициировать события из элементов управления ActiveX достаточно просто.
Совсем другая ситуация с ActiveX DLL. Классы инициируют события, основанные на внутренних событиях, таких как лимит времени. Модифицируем класс CTimer так, чтобы он вызывал события Minute и Hour каждый раз, когда минута или час прошли. Доработанный проект CTimer вместе с его тестовым проектом находится в папке EVNTimer в папке этой главы на компакт-диске.
Так как класс CTimer не может отслеживать время непрерывно, необходимо ему помочь, воспользовавшись элементом управления Timer. Но модуль класса не имеет формы, и как тогда можно использовать элемент управления Timer? Мы добавим форму к проекту, но отображаться она не будет. Это будет скрытая форма с элементом управления Timer. Модуль класса будет иметь доступ к элементу Timer на форме и перехватывать его событие Timer. Это один из способов использования элемента управления ActiveX в модуле класса.
1. В окне Project Explorer выберите компонент CTimer и переименуйте его на EventTimer, как показано на рис. 15.3. Затем откройте меню Project, выберите команду Add Form (Добавить форму) и добавьте новую форму в проект модуля класса.
Примечание
Убедитесь, что новая форма появилась под компонентом
EventTimer, а не TestProject. Если форма добавилась к тестовому проекту, удалите ее и повторите процесс.
2. Откроите форму Form1 в режиме конструирования и поместите на нее экземпляр элемента управления Timer. Установите его свойство Enabled в True, а его свой ство Interval в 10000. Это значение соответствует 10 секундам. Не нужно, чтобы TimerClass тратил слишком много компьютерного времени на обработку событий Timer, так что установите большой промежуток времени ожидания.
На рис 15.3 показан проект EventTimer в среде разработки Visual Basic. Обратите внимание на компоненты в окне Project, на Form1 в окне Design и на окно Code класса. Новый класс называется EventTimerClass, а проект называется EventTimer. Открывая этот пример проекта, не забудьте выбрать проект теста и добавить ссылку на EventTimer к нему.
Наша задача - перехватить событие Timer от элемента управления. Timer внутри модуля класса и использовать его для генерирования событий Minute и Hour. Событие Minute первый раз происходит через 60 секунд после запуска таймера и каждые 60 секунд после этого. Аналогично, событие Hour происходит первый раз через 60 минут после начала работы класса и через каждые 60 минут после этого.
![](image/iniciirovanie-sobytij-iz-koda-klassa_1.gif)
Рис. 15.3. Проект EventTimer подобен проекту CTimer, но он использует внутренний таймер для отсчета времени
3. Чтобы обеспечить доступ к элементу управления на другой форме, необходимо создать переменную формы в самом классе. Вставьте следующие объявления в окно программного кода класса.
Dim cFrrn As Formi
Dim WithEvents eTimer As Timer
Ключевое слово WithEvents
сообщает Visual Basic, что элемент управления должен содержать свойства, методы и события (вот почему он назван eTimer). Эти переменные оживут, если им поставить в соответствие объекты. Это должно произойти внутри события инициализации класса.
4. Вставьте следующие строки в событие Class_Initialize:
Private Sub Class_Initialize ()
Set cFrm = New Form1
Load cFrm
Set eTimer = cFrm.Timer1
End Sub
Первый оператор связывает новый экземпляр объекта Formi с переменной cFrm. Затем загружается новая форма. С этого времени реализуется доступ к элементам управления, размещенным на форме. Последняя строка делает переменную eTimer
эквивалентной элементу управления Timer1 на форме Form1. Теперь класс может иметь доступ к элементу управления Timeri на форме Formi с помощью имени eTimer, также как и Form1 имеет доступ к нему с помощью имени Timer1. Два выражения эквивалентны, но нельзя получить доступ к элементу управления Timer как Form1.Timer1 из модуля класса.
Если открыть раскрывающийся список объектов в окне программного кода модуля класса, то можно увидеть объект eTimer. Так как объектная переменная eTimer объявлена с использованием ключевого слова WithEvents,
то есть возможность программировать его события. Вот почему его имя появилось в списке объектов. Выберите объект eTimer
в списке объектов и затем откройте список событий в окне Code. Появится имя события Timer. Выберите его, после чего можете программировать событие eTimer_Timer(), что эквивалентно программированию события Timer1_Timer.
5. Добавьте строки программного кода 15.5 в обработчик события eTimer_Timer.
Программа 15.5. Инициирование событий из кода класса
Private Sub eTimer_Timer()
Static seconds As Long
Static minutes As Long
Static hours As Long
Dim RaiseMinutes As Boolean, RaiseHours As Boolean
If Not Counting Then Exit Sub
RaiseMinutes = False
RaiseHours = False
seconds = seconds + eTimer.Interval / 1000
If seconds = 60 Then
minutes = minutes + 1
seconds = 0
RaiseMinutes = True
If minutes = 60 Then
hours = hours + 1
minutes = 0
RaiseHours = True
End If
End If
If RaiseHours Then
RaiseEvent Hour
Elself RaiseMinutes Then
RaiseEvent Minute
End If
End Sub
Булева переменная Counting
объявлена в секции объявлений формы и служит признаком работы таймера. Если таймер остановлен, то не нужно обрабатывать событие Timer. Эта переменная устанавливается в True внутри метода StartCounting и устанавливается в False внутри метода StopCounting.
Эта подпрограмма вызывается каждые 10 секунд и увеличивает число прошедших секунд,
прибавляя к ним 10. Каждые 60 секунд она увеличивает число минут на 1, и каждые 60 минут она увеличивает число часов на 1. Если переменная minutes равна 0 (это значит, что она достигла значения 60 и сброшена в 0), должно быть инициировано событие Hour. Если это не так, то проверяется значение переменной seconds.
Если она равна 0, то должно быть инициировано событие Minute. В результате этого инициируются 59 последовательных событий Minute, затем событие Hour, после чего процесс повторяется. Так как нельзя выдать сразу два события, то событие Minute пропускается, когда инициируется событие Hour. Ясно, что событие Hour означает наличие и события Minute.
6. Для того чтобы надлежащим образом инициировать события, необходимо объявить их имена. Добавьте следующие строки в окно кода модуля класса (вне любой процедуры):
Event Minute ()
Event Hour ()
Полный программный код EventTimerClass показан ниже. Обратите внимание на сходство и отличия с простым классом CTimer. Процедура Property Get и метод класса не изменились. Добавлен только код для доступа к элементу управления Timer на невидимой форме и запрограммировано событие Timer элемента Timer для инициирования соответствующих событий.
Программа 15.6. Листинг EventTimerClass
Dim cFrm As Formi
Dim WithEvents eTimer As Timer
Dim totallnterval As Double
Dim Tl As Double
Dim Counting As Boolean
Event Minute()
Event Hour()
Public Sub StartCounting ()
Tl = Time
Counting = True
End Sub
Public Sub StopCounting()
totallnterval = totallnterval + Time - Tl
Counting = False
End Sub
Property Get ElapsedTime() As Double
ElapsedTime = totallnterval
End Property
Public Sub ResetTimer()
totallnterval = 0
End Sub
Private Sub Class_Initialize ()
Set cFrm = New Formi
Load cFrm
Set eTimer = cFrm.Timer1
End Sub
Private Sub eTimer_Timer()
Static seconds As Long
Static minutes As Long
Static hours As Long
Dim RaiseMinutes As Boolean, RaiseHours As Boolean
If Not Counting Then Exit Sub
RaiseMinutes = False
RaiseHours = False
seconds = seconds + eTimer.Interval / 1000
If seconds = 60 Then
minutes = minutes + 1
seconds = 0
RaiseMinutes = True
If minutes = 60 Then
hours = hours + 1
minutes = 0
RaiseHours = True
End If
End If
If RaiseHours Then
RaiseEvent Hour
Elself RaiseMinutes Then
RaiseEvent Minute
End If
End Sub