О создании модулей класса и собственных элементов ActiveX.
Перевод выполнил Владимир Никаноров (GardenStone) При публикации обязательна ссылка на первоисточник и на ресурс HiProg.com WithEvents Revisited Создание класса SpinButtons By Ken Getz в выпуске MOD за ноябрь 1999 я написал статью об использовании WithEvents при создании инструмента, представляющего собой комбинацию поля и списка (textbox/listbox), который осуществлял поиск текста в списке через набор в текстовом поле. Цель этой статьи (помимо представления компонента с удобным интерфейсом) - демонстрация того, как можно перехватывать события из любого места, и реагировать на них в своей программе. Такой подход делает возможным разделение пользовательского интерфейса объектов и программную обработку событий - предоставляя возможность создания ваших собственных групп элементов управления, как если бы вы создали ваш собственный элемент ActiveX в Visual Basic. Я получил много писем от читателей, воодушевленных идеями, представленными в этой статье. Из-за невозможности ответить всем, я решил написать другую статью, использующую ту же технику и в этой статье я покажу как создавать кнопки для инкрементации значения (spin buttons). Spin buttons представляют собой один из вариантов контроля при вводе значений пользователями: изменением значения даты. Сама идея простая: Понятие "spin button" включает две кнопки, одна со стрелкой вверх и другая - вниз, подключенные к текстовому полю. Нажатие кнопки со стрелкой вверх увеличивает значение в текстовом поле; нажатие кнопки со стрелкой вниз уменьшает величину. Хотя, теоретически, Вы могли бы использовать этот механизм для текстовых значений, но, главным образом, Spin buttons применяют при вводе даты и значений целых чисел. Надо ли озадачиваться созданием собственных Spinbuttons, когда есть элементы управления, выполняющие эту функцию? Первое: большинство свойств существующих элементов управления непривлекательны тем, что вы, как правило, не можете изменить их. Ну, а ваш собственный элемент управления Вы можете создать на свой вкус. К тому же, если вы работаете на Access или VB, то вам не обойтись без использования элемента ActiveX, а применение стандартного элемента ActiveX может увеличить потери с точки зрения ресурсов, скорости, и размера приложения. Кроме того,так как вы пишете собственный код, вы можете распоряжаться им по своему усмотрению и при перовой необходимости его изменить. Например, Spin buttons, который Вы создадите на основе материала статьи позволит Вам ограничивать область значений и, при желании, позволят возвращаться в начало или конец разрешенного диапазона, как только Вы достигнете конечной/начальной точки. Reviewing the Concepts Разбор концепции Когда вы помещаете элемент управления на форму, а затем описываете процедуры, обрабатывающие события генерируемые элементом управления, вы сталкиваетесь, если угодно, с некоторого рода магией предоставленной вам Access'ом. Вы задаетесь вопросом, каким образом модуль класса, в котором вы написали свой код знает о событиях генерируемых в классе элемента управления (за каждым из существующих элементов управления лежит свой модуль класса) Фактически, приоткрывая завесу, когда вы помещаете элемент (control) на форму, сама форма за вас, посредством WithEvents, устанавливает связь между формой и элементом управления. Когда вы размещаете на форме кнопку cmdTest, главное приложение (Access) в действительности добавляет скрытые строчки кода в модуль класса формы : Private WithEvents cmdTest As CommandButton А это в свою очередь вы и можете заметить: в левом списке окна редактора модуля, который показывает все объекты, для которых вы можете написать событийные процедуры, появляется cmdTest, а в правом списке окна редактора модуля - список событий, доступных для этого объекта. Для того, что бы реагировать на другие события, кроме установленных для любого из элементов управления формы (или отчета, если вы программируете в среде Access), вам понадобится самим предусмотреть эту связь; для чего нужно будет определить переменную, которая бы “указывала” на элемент управления или другой объект, генерирующий эти события. В этом примере, как и в приведенном в прошлой статье, вы увидите технику, позволяющую реагировать на события элементов управления форм, вне программных модулей этих форм. Такая техника позволяет практически имитировать всю функциональность элементов ActiveX, вместо действительного создания нового элемента ActiveX. Introducing the SpinButtons Class Я создал класс, SpinButtons, который и позволяет использовать простую комбинацию кнопки/текстовое поле. Единожды создав класс и установив для него несколько свойств, вы можете им пользоваться. Чтобы увидеть класс SpinButtons в действии, взгляните на форму frmSpinTest (см. Приложение 1). (SpinButtons.cls, а также ссылки на другие исходные коды и базы данных для скачивания указанны в конце этой статьи.) Приложение 1: Класс SpinButtons облегчает добавление spin-button в любую форму Форма использует все свойства класса SpinButtons. в Приложении 2 приведен список свойств и методов для этого класса. Member | Примечание | AllowKeys | Принимает значения Boolean. Если True, Возвращает (+) и (-) ключи для увеличения/уменьшения значения в текстовом поле, соответственно. Если False, то вы должны использовать кнопки для изменения значения в текстовом поле. | AllowWrap | Принимает значения Boolean. Если True, сбрасывает значение на минимум при нажатии на кнопку "вверх", и или установит значение на максимальное при достижении минимального при нажатии на кнопку "вниз". Если никакое из свойств (Min и Max) не определены, AllowWrap не даст никакого эффекта. | Control | TextBox. Ссылка использующая текстовое поле для отображения изменения величины. Свойство, что называется, not optional. | Delay | Тип Long. Если установлено значение не равное нулю, меняет значение поля, при нажатой кнопке, с указанным периодом (в миллисекундах).Access, по умолчанию, может установить этот период для вас слишком малым, Вы можете установить это значении равным 100 или более, для медленной прокрутки.. | DownButton | CommandButton. Ссылка на кнопку, уменьшающую значение в текстовом поле. Свойство опционально, вы не сможете использовать мышь для уменьшения значения в текстовом поле если не определите кнопку для этого свойства. | Interval | Тип Long. Устанавливает величину, на которую будет происходить приращение/уменьшение величины в текстовом поле при нажатии на управляющие кнопки. Значение по умолчанию равно 1. | Max | Свойство, тип Variant. Если AllowWrapping True, определяет максимальное значение текстового поля, перед переходом к минимальному значению. Если Min и Max не были установлены, и AllowWrap True, это свойство не имеет никакого эффекта. | Min | Свойство, тип Variant. Если AllowWrapping True, определяет минимальное значение текстового поля, перед переходом к максимальному значению. Если Min и Max не были установлены, и AllowWrap True, это свойство не имеет никакого эффекта. | UpButton | Свойство CommandButton. Ссылка на кнопку, увеличивающую значение в текстовом поле. Свойство опционально, вы не сможете использовать мышь для уменьшения значения в текстовом поле если не определите кнопку для этого свойства. | Value | Свойство, тип Variant . Устанавливает и возвращает значения текстового поля связанного с классом SpinButtons. Хотя вы можете работать со значением поля и из самой формы, это свойство помогает формировать класс и вы можете не волноваться об имени этого элемента управления. | Init | Метод. Вы можете вызвать Init метод для индивидуальной работы со свойствами и их установками. Это метод ввода параметров каждого из возможных свойств класса. Для получения дополнительной информации смоьтрите the Parameter Tip для VBA, или просмотрите исходный код. | SpinDown | Событие. Применяется при изменении значения поля в сторону уменьшения. Событие возвращает текущее значение поля и параметр Boolean Cancel. При установке параметра в True - отмена изменения. См. форму примера. | SpinUp | Событие. Применяется при зменении значения поля в сторону увеличения. Событие возвращает текущее значение поля и параметр Boolean Cancel. При установке параметра в True - отмена изменения. См. форму примера. | Change | Событие. Выполняется, когда значение текстового поля было изменено. Если не играет роли, в какую сторону изменилось значение, а важен сам факт его изменения, используйте это событие. Ко времени выполнения события, значение уже изменено, и отменить событие невозможно. Оно применяется для текущего значения текстового поля, которое уже изменено. | Приложение 2: Методы и свойства класса SpinButtons. Использование SpinButtons Для использования класса SpinButtons вам нужно импортировать модуль класса в свой проект. Кроме того, вам понадобится форма содержащая все элементы управления необходимые для реализации спин-кнопок, а именно поле и две кнопки. (При желании, вы можете скопировать и вставить группу элементов созданных в форме frmSpinTest примера Access 2000 project. Если вы пойдете этим путем, то, вероятно, захотите настроить размер поля и его шрифта.) Если Вы сами создали кнопки в Access, то, очевидно, пожелаете изменить свойство AutoRepeat на Yes таким образом, чтобы при удержании кнопки вниз, например, повторять изменения в значениях. Хотя вы можете использовать эту технику (использование WithEvents для перехвата событий вызванных другими классами) во всех приложениях, применяющих Microsoft Forms (Excel, Word, PowerPoint, и т.д.), вы, скорее всего, не захотите этого делать ради этого примера. Так как в указанных средах отсутствует возможность реализации кнопки к автоповтору, нет и способа вызвать это событие неоднократно. Этот пример лучше всего подойдет для Microsoft Access версий Access 97 или Access 2000. (Но, если вы захотите использовать код примера в Access 97, вам нужно будет удалить весь код из класса SpinButtons, связанный с генерацией событий, то есть удалить объявление Event и все вызовы метода RaiseEvent.) Когда Вы закончите создание формы, Вы должны записать код, чтобы описать объект SpinButtons и сцепить его с формой. Для этого добавьте код типа тот показанный в Приложении 3 к вашей форме. (Конечно, замените типовые имена элементов управления вашими именами, если Вы создали новые.) Private sb As SpinButtons Private Sub Form_Load() Set sb = New SpinButtons ' Set the required properties individually. Set sb.Control = Me.txtValue Set sb.DownButton = Me.cmdDown Set sb.UpButton = Me.cmdUp End Sub Private Sub Form_Unload(Cancel As Integer) Set sb = Nothing End Sub Приложение 3: Код инициализации объекта SpinButtons и привязки его к форме. Если хотите, можете установить сразу все свойства, воспользовавшись методом Init: sb.Init Control:=Me.txtValue, UpButton:=Me.cmdUp, DownButton:=Me.cmdDown Как это работает Точно так же как класс, который был описан в более ранней статье, SpinButtons класс выполняет ту же работу, захватывая посредством WithEvents связи с элементами управления на вашей форме. Таким образом вы принудительно создаете список событий для элементов управления, и они будут "закодированы" для каждой из кнопок, каждый раз, когда Вы захотите использовать класс SpinButtons, вы просто определяете ссылки на элементы управления, и класс SpinButtons выполняет остальную часть работы. Используя эту методику, Вы можете иметь столько экзепляров класса SpinButtons на вашей форме, сколько Вам нужно. Класс содержит объявления для его внутренних представлений для трех элементов управления: Private WithEvents mtxt As TextBox Private WithEvents mcmdUp As CommandButton Private WithEvents mcmdDown As CommandButton Вы должны связать переменные с фактическими элементами управления. Можно установить свойства индивидуально, или вызвать метод Init. Как только Вы установите свойства для элементов управления, SpinButtons класс сохранит ссылки в переменных, объявленных с ключевым словом WithEvents , и заставит выполнять соответствующие свойства [Событийные процедуры]. Например, как показано на Приложении 4 процедура Property Set для UpButton свойства. Public Property Set UpButton(cmd As CommandButton) On Error GoTo HandleErrors Set mcmdUp = cmd mcmdUp.OnClick = "[Event Procedure]" mcmdUp.OnKeyPress = "[Event Procedure]" ExitHere: Exit Property HandleErrors: Select Case Err.Number Case Else Err.Raise Err.Number, "SpinButtons.UpButton", Err.Description End Select Resume ExitHere End Property Приложение 4: Процедура Property Set для свойства UpButton. Как только Вы установили свойства управления, SpinButtons класс может реагировать на события кнопок OnClick и KeyPress и может изменять значение связанного текстового поля. (Если Вы помните по предыдущей статье - или если Вы не читали ее - Access просто не будет поднимать любое из событий объекта, если не находит " [Событийную процедуру] " текст в связанном со свойством событии. Эта оптимизация делает формы быстрее для выполнения, чем это сделал бы сам Ассеss. Если нет никакой потребности вызывать событие , ничего не происходит. Это означает, что ваш код должен всегда устанавливать свойства для событий правильно, как это делает класс. Access - единственное из использующих VBA приложение, который,насколько я знаю, реализует это поведение/причуду.) Reacting to Host Events Как только Вы установили связь через WithEvents, Вы можете реагировать на события, вызванные элементом управления, действия которого "управляются" объектом SpinButtons. Например, когда Вы нажимаете на кнопку на вашей форме, кнопка вызовет событие OnClick . Код в классе SpinButtons реагирует на событие, потому что Вы описали это, используя ключевое слово WithEvents и связав переменную mcmdUp с элементом управления, через код представленный на Приложении 5. Private Sub mcmdUp_Click() On Error GoTo HandleErrors Call SpinUp ExitHere: Exit Sub HandleErrors: Select Case e Err.Number Case Else Err.Raise Err.Number, "SpinButtons.mcmdUp_Click", Err.Description End Select Resume ExitHere End Sub Приложение 5: Когда Вы нажимаете на кнопку на вашей форме, кнопка вызывает событие OnClick, и код в SpinButtons классе реагирует на событие. Процедура SpinUp вызывает внутреннюю процедуру Spin,посредством кода, увеличивая значение в связанном текстовом поле. Если Вас это заинтересовало, можете "порыться" в процедуре Spin в коде примера - как это работает - но здесь это объясняться не будет. (Процедура Spin обрабатывает первичные данные, по выяснению вида данных в текстовом поле, принятия решения, основанном на типе данных, и принимает во внимание, надо увеличить значение или вернуться назад, к значению начальной точки, основанному на значениях свойства, которые Вы установили.) Вызов событий Чтобы сделать группу элементов управления более подобной элементу ActiveX по действию, класс SpinButtons вызывает три события, с которыми вы работаете: SpinUp, SpinDown, и Change. SpinButtons класс вызывает события SpinUp и SpinDown перед изменением текста в текстовом поле, что позволяет Вам отменить изменение. После изменения значения вызывается событие Change (то есть, настолько позже, что бы можно было успеть отменить событие). Чтобы вызывать события класса, Вы должны сначала объявить их: Public Event Change(Value As Variant) Public Event SpinUp(Value As Variant, Cancel As Boolean) Public Event SpinDown(Value As Variant, Cancel As Boolean) В этом примере, событие Change было объявлено, для передачи текущее значение связанного элемента управления объекту, ожидающему событие. События SpinUp and SpinDown передают текущее значение, и параметр, позволяющий Вам отменить событие. Если Вы присвоите True параметру Cancel внутри событий SpinUp или SpinDown, изменения просто не будет. Затем, Вы захотите вызвать события, используя инструкцию RaiseEvent. Например, класс SpinButtons использует следующие строчки кода частной процедуры Spin, вызывая события SpinUp and SpinDown: Select Case Sgn(intInterval) Case 1 RaiseEvent SpinUp(varData, fCancel) Case -1 RaiseEvent SpinDown(varData, fCancel) Case 0 ' Do nothing. End Select If fCancel Then GoTo ExitHere Этот код сначала определяет признак (сигнатуру, знак) интервала (1 или -1), и вызывает, соответственно, событие SpinUp или SpinDown. Если значение в fCancel, после вызова события, является True, выполнение кода заканчивается без изменения значения в связанном элементе управления. Использование WithEvents обеспечивает синхронную связь между "генератором" события и всеми объектами , ожидающими событие. То есть, когда Вы выполняете вышенаписанный код, то вызывающий событие код гарантированно приостанавливается в ожидании ответа от каждого класса, ожидающего событие; код в SpinButtons классе продолжит выполнение только после того, когда все процедуры обработки события, присоединенные ключевым словом WithEvents не закончат отвечать на события. Таким образом , вам гарантируется то, что код, обрабатывающий возвращаемое значение переменной fCancel не будет работать, пока процедура события в модуле формы не закончит выполняться. Это означает, конечно, что Вы не должны помещать в процедуры событий отнимающих много времени инструкций, реагирующих на события, поднятых классом SpinButtons (или любым другим). Вообразите , что случилось бы, если Вы имея чувствительный к времени код в классе SpinButtons, в некоторой событийной процедуре, реагируя на событие, вызванное SpinButtons классом, открыли MessageBox в течение выполнения процедуры. Класс SpinButtons был бы вынужден ОЖИДАТЬ, пока MessageBox не закроется. Важно, что вы знаете об этом поведении, как и то, что происходит, если что-либо приводит к остановке в классе, вызывающем события. Reacting to SpinButtons Events В вашей форме, используемый класс SpinButtons позволяет Вам добавлять процедуры для любых из событий SpinUp, SpinDown, и Change. Например, типовая форма реагирует на событие SpinDown и отмену события, если значение управления текстового поля - меньше или равное 10. А теперь Вы используете свойство Min класса SpinButtons, чтобы воспроизвести то же самое поведение: ' Sample SpinDown event procedure. ' вы не будете использовать этот код. Private Sub sb_SpinDown( Value As Variant, Cancel As Boolean) If Value <= 10 Then Cancel = True End Sub Conclusion Это просто и полезно - отделить интерфейс пользователя от его поведения, используя WithEvents. Это позволит Вам создавать модули класса, которые Вы можете легко перемещать из одного проекта в другой, обеспечивая функциональные возможности,обходясь добавлением пары строк в программы к каждой форме. Налицо выгода от создания собственного элемента ActiveX. Руководство Разработчика Access 2000, Настольное Издание (Том I), из которого этот код был использован, имеет еще несколько примеров, использующих эту же методику - как только я начал, то уже не мог остановиться. Если Вы хотите создать форму с двумя списками и четырьмя кнопками, что бы перемещатьть элементы назад и вперед между этими двумя списками; или если Вы хотите захватить на форме событие Resize и масштабировать элементы управления на форме, чтобы соответствовать новым размерам формы; или позволить пользователям выбирать стартовое местоположение на листе для печати логотипа, эта методика делает всё простым для исполнения. И если Вы занимаетесь разработкой форм, подумайте, где разместить код. Если Вы многократно используете похожий код на различных формах, то, наверняка, захотите извлечь код, присоединенных к нему соответствующих событий, и создать класс, который отделит интерфейс пользователя от кода. Это сделает ваши разработки в дальнейшем проще, быстрее. Сколько обещаний подобных этому Вы давали себе при проектировании форм? This article, and its code, was excerpted and modified from a section of Access 2000 Developer's Handbook, Desktop Edition (Volume I) [SYBEX, 1999], by Ken Getz, Paul Litwin, and Mike Gilbert, with the permission of the publisher. Для получения дополнительной информации посетите http://www.developershandbook.com/, или http://www.sybex.com/. The files referenced in this article are available for download. Ken Getz, a senior consultant with MCW Technologies, splits his time between programming, writing, and training. Ken is co-author of several books for developers, including Access 2000 Developer's Handbook [SYBEX, 1999] (with Paul Litwin and Mike Gilbert) and VBA Developer's Handbook [SYBEX, 1997] (with Mike Gilbert). He also co-wrote the training materials, travels around the United States teaching, and recorded training videos on Access 97 and VB6 for Application Developers Training Company. Ken is currently at work on Visual Basic Language Developer's Handbook from SYBEX. In addition, Ken is a Contributing Editor for Microsoft Office & Visual Basic for Applications Developer magazine. Просмотров: 17705 Ваш коментарий будет первым | | |