Скелет элемента управления ActiveX
Перед добавлением кода следует изучить, что именно сделал мастер. Переключитесь в окно Project Explorer и выполните двойной щелчок на имени созданного элемента управления, чтобы открыть режим разработки Затем выполните двойной щелчок на форме элемента UserControl, чтобы открыть окно с кодом и просмотреть строки, вставленные мастером.
Программа 16.2. ActiveX Control
Private m_Caption As String
Private m_Effect As Integer
Private m TextAlignment As Integer
' Значения свойств по умолчанию:
Const m_def_Caption = "3D Label"
Const m_def_Effect = 2
Const m_def_TextAlignment = 4
' Переменные свойств:
' Объявления событий:
Event DblClick()
Event Click()
Event KeyUp(KeyCode As Integer, Shift As Integer)
Event KeyPress(KeyAscii As Integer)
Event KeyDown(KeyCode As Integer, Shift As Integer)
Event MouseUp(Button As Integer, Shift As Integer, X As Single, _
Y As Single)
Event MouseMove(Button As Integer, Shift As Integer, X As Single, _
Y As Single)
Event OLEStartDrag(Data As DataObject, AllowedEffects As Long)
Event OLESetData(Data As DataObject, DataFormat As Integer)
Event OLEGiveFeedback(Effect As Long, DefaultCursors As Boolean)
Event OLEDragOver(Data As DataObject, Effect As Long, _
Button As Integer, Shift As Integer, X As Single, _
Y As Single, State As Integer)
Event OLEDragDrop(Data As DataObject, Effect As Long, _
Button As Integer, Shift As Integer, X As Single, _
Y As Single)
Event Resize ()
Public Property Get Font() As Font
Set Font = UserControl.Font
End Property
Public Property Set Font(ByVal New_Font As Font)
Set UserControl.Font = New_Font
PropertyChanged "Font"
End Property
Public Property Get BorderStyle() As Integer
BorderStyle = UserControl.BorderStyle
End Property
Public Property Let BorderStyle(ByVal New_BorderStyle As Integer)
UserControl.BorderStyle() = New_BorderStyle
PropertyChanged "BorderStyle"
End Property
Public Property Get BackStyle() As BackgroundStyle
BackStyle = UserControl.BackStyle
End Property
Public Property Let BackStyle(ByVal New_BackStyle As _
BackgroundStyle)
UserControl.BackStyle() = New_BackStyle
PropertyChanged "BackStyle"
End Property
Public Property Get Appearance () As Integer
Appearance = UserControl.Appearance
End Property
Private Sub UserControl_DblClick()
RaiseEvent DblClick
End Sub
Private Sub UserControl_Click()
RaiseEvent Click
End Sub
Public Property Get Enabled() As Boolean
Enabled = UserControl.Enabled
End Property
Public Property Let Enabled(ByVal New_Enabled As Boolean)
UserControl.Enabled() = New_Enabled
PropertyChanged "Enabled"
End Property
Public Property Get ForeColor() As OLE_COLOR
ForeColor = UserControl.ForeColor
End Property
Public Property Let ForeColor(ByVal New_ForeColor As OLE_COLOR)
UserControl.ForeColor() = New ForeColor
PropertyChanged "ForeColor"
End Property
Public Property Get hDC() As Long
hDC = UserControl.hDC
End Property
Public Property Get hWnd() As Long
hWnd = UserControl.hWnd
End Property
Private Sub UserControl_KeyUp( KeyCode As Integer, Shift As Integer)
RaiseEvent KeyUp(KeyCode, Shift)
End Sub
Private Sub UserControl_Keypress(KeyAscii As Integer)
RaiseEvent Keypress(KeyAscii)
End Sub
Private Sub UserControl_KeyDown(KeyCode As Integer, Shift As Integer)
RaiseEvent KeyDown(KeyCode, Shift)
End Sub
Private Sub UserControl_MouseUp(Button As Integer, Shift As Integer, _
X As Single, Y As Single)
RaiseEvent MouseUp(Button, Shift, X, Y)
End Sub
Public Property Get MousePointer() As Integer
MousePointer = UserControl.MousePointer
End Property
Public Property Let MousePointer(ByVal New_MousePointer As Integer)
UserControl.MousePointer() = New_MousePointer
PropertyChanged "MousePointer"
End Property
Private Sub UserControl_MouseMove(Button As Integer, Shift As Integer, _
X As Single, Y As Single)
RaiseEvent MouseMove(Button, Shift, X, Y)
End Sub
Private Sub UserControl_OLEStartDrag(Data As Data0bject, _
AllowedEffects As Long)
RaiseEvent OLEStartDrag(Data, AllowedEffects)
End Sub
Private Sub UserControl_OLESetData(Data As DataObject, _
DataFormat As Integer)
RaiseEvent OLESetData(Data, DataFormat)
End Sub
Private Sub UserControl_OLEGiveFeedback(Effect As Long, _
DefaultCursors As Boolean)
RaiseEvent OLEGiveFeedback(Effect, DefaultCursors)
End Sub
Public Property Get OLEDropMode() As Integer
OLEDropMode = UserControl.OLEDropMode
End Property
Public Property Let OLEDropMode(ByVal New_OLEDropMode As Integer)
UserControl.OLEDropMode() = New_OLEDropMode
PropertyChanged "OLEDropMode"
End Property
Private Sub UserControl_OLEDragOver(Data As DataObject, _
Effect As Long, Button As Integer, Shift As Integer, _
X As Single, Y As Single, State As Integer)
RaiseEvent OLEDragOver(Data, Effect, Button, Shift, X, Y, State)
End Sub
Private Sub UserControl_OLEDragDrop(Data As Data0b]ect, Effect As Long, _
Button As Integer, Shift As Integer, X As Single, _
Y As Single)
RaiseEvent OLEDragDrop(Data, Effect, Button, Shift, X, Y)
End Sub
Public Sub OLEDrag()
UserControl.OLEDrag
End Sub
Public Property Get Picture() As Picture
Set Picture = UserControl.Picture
End Property
Public Property Set Picture(ByVal New Picture As Picture)
Set UserControl.Picture = New_Picture
PropertyChanged "Picture"
End Property
Private Sub UserControl_Resize ()
RaiseEvent Resize
End Sub
Public Property Get Caption() As String
Caption = m_Caption
End Property
Public Property Let Caption(ByVal New_Caption As String)
m_Caption = New Caption
PropertyChanged "Caption"
End Property
Public Property Get Effect() As Integer
Effect = m_Effect
End Property
Public Property Let Effect(ByVal New_Effect As Integer)
m_Effect = New_Effect
PropertyChanged "Effect"
End Property
Public Property Get TextAlignment() As Integer
TextAlignment = m_TextAlignment
End Property
Public Property Let TextAlignment(ByVal New_TextAlignment As Integer)
m_TextAlignment = New_TextAlignment
PropertyChanged "TextAlignment"
End Property
' Инициализация свойств
Private Sub UserControl_InitProperties()
Set Font = Ambient.Font
m_Caption = m_def_Caption
m_Effect = m_def_Effect
m_TextAlignment = m_def_TextAlignment
UserControl.BorderStyle = 1
UserControl.BackStyle = 1
End Sub
' Загрузка значений свойств
Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
Set Font = PropBag.ReadProperty("Font",Ambient.Font)
UserControl.BorderStyle = PropBag.ReadProperty ("BorderStyle", 0)
UserControl.BackStyle = PropBag.ReadProperty ("BackStyle", 1)
UserControl.Enabled = PropBag.ReadProperty ("Enabled", True)
UserControl.ForeColor = PropBag.ReadProperty ("ForeColor", _
&H80000012)
UserControl.MousePointer = PropBag.ReadProperty ("MousePointer", 0)
UserControl.OLEDropMode = PropBag.ReadProperty ("OLEDropMode", 0)
Set Picture = PropBag.ReadProperty ("Picture", Nothing)
m_Caption = PropBag.ReadProperty ("Caption", m_def_Caption)
m_Effect = PropBag.ReadProperty ("Effect", m_def_Effect)
m_TextAlignment = PropBag.ReadProperty ("TextAlignment", _
m_def_TextAlignment)
UserControl.BackColor = PropBag.ReadProperty ("BackColor", _
SH8000000F)
End Sub
' Сохранение значений свойств
Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
Call PropBag.WriteProperty("Font", Font, Ambient.Font)
Call PropBag. WriteProperty("BorderStyle",
UserControl.BorderStyle,0)
Call PropBag.WriteProperty("BackStyle", UserControl.BackStyle, 1)
Call PropBag.WriteProperty("Enabled", UserControl.Enabled, True) Call PropBag.WriteProperty ("ForeColor", UserControl.ForeColor, _
&H80000012)
Call PropBag. WriteProperty ("MousePointer", _
UserControl.MousePointer, 0)
Call PropBag. WriteProperty ("OLEDropMode", _
UserControl.OLEDropMode, 0)
Call PropBag.WriteProperty("Picture", Picture, Nothing)
Call PropBag. WriteProperty ("Caption", m_Caption, _
m_def_Caption)
Call PropBag.WriteProperty("Effect", m_Effect,
m_def_Effect)
Call PropBag. WriteProperty ("TextAlignment", _
m_TextAlignment, m_def_TextAlignment)
Call PropBag.WriteProperty ("BackColor", _
UserControl.BackColor, &H8000000F)
End Sub
' ВНИМАНИЕ! НЕ УДАЛЯЙТЕ И НЕ ИЗМЕНЯЙТЕ
' СЛЕДУЮЩУЮ ЗАКОММЕНТИРОВАННУЮ СТРОКУ
' MappingInfo=UserControl,UserControl,-1,BackColor
Public Property Get BackColor() As OLE_COLOR
BackColor = UserControl.BackColor
End Property
Public Property Let BackColor(ByVal New_BackColor As OLE_COLOR)
UserControl.BackColor() = New_BackColor
PropertyChanged "BackColor"
End Property
Программа довольно длинная, но не так сложна, как выглядит. Рассмотрим каждый раздел кода подробно, начиная с раздела объявлений.
Option Explicit
' Значения свойств по умолчанию
Private m_Caption As String
Private m_Effect As Integer
Private m_TextAlignment As Integer
Эти переменные содержат значения специальных свойств элемента управления. Свойства элемента управления отображены на закрытые переменные кода элемента управления. Как и в случае с компонентом ActiveX, чьи свойства доступны извне, эти свойства являются, фактически, простыми переменными. Позже будет видно, как элемент управления получает значение, введенное пользователем в окне свойств (или код во время выполнения), и связывает их с закрытыми переменными. (Вспомните манипулирование свойствами OLE-сервера с помощью процедур Property Let и Property Get, рассмотренное в предыдущей главе. Тот же самый подход работает и с элементами управления ActiveX, но об этом позже.).
Дальше следуют определения нескольких констант, которые соответствуют значениям определенным в окне Set Attributes мастера. Эти константы будут использоваться дальше в коде как начальные значения для различных свойств. Учтите, что нет необходимости запускать мастер для того, чтобы просто поменять эти значения. Можно отредактировать код элемента управления. Обратите также внимание имена констант основаны на именах свойств, и код можно легко редактировать.
' Значения свойств по умолчанию:
Const m_def_Caption = "3D Label"
Const m_def_Effect = 2
Const m_def_TextAlignment = 4
Дальше следуют объявления событий. Это события, которые были определены в первых двух окнах мастера и отображены на объект UserControl. Щелчок на элементе управления генерирует событие Click, которое передается приложению, как будто оно было сгенерировано самим элементом управления ActiveX. В списке определено несколько больше событий, но нет нужды повторяться.
' Объявления событий:
Event DblCllck()
Event Click()
Event KeyUp(KeyCode As Integer, Shift As Integer)
Event Keypress(KeyAscii As Integer)
Event KeyDown(KeyCode As Integer, Shift As Integer)
Event MouseUp(Button As Integer, Shift As Integer,
X As Single, Y As Single)
Event MouseMove(Button As Integer, Shift As Integer, _
X As Single, Y As Single)
Примечание
Если событие Click не было отображено на объект UserControl, то только UserControl будет видеть событие Click. Если нужно выполнять специальные действия, когда на элементе выполнен двойной щелчок, следует запрограммировать событие Click в коде элемента управления. Пользователь не сможет программировать событие Click, пока оно не будет инициировано из кода элемента управления.
В нашем элементе управления не требуется как-то по особенному использовать событие Click (и другие обычные события мыши и клавиатуры), поэтому предоставим их приложению, которое использует элемент управления ActiveX.
Установка и чтение значений свойств
Теперь рассмотрим несколько подпрограмм: по две для каждого свойства.
Программа 16.3. Процедуры свойства Caption
Public Property Get Caption() As String
Caption =
m_Caption
End Property
Public Property Let Caption(ByVal New Caption As String)
m_Caption = New Caption
PropertyChanged "Caption"
End Property
Каждое свойство определено двумя Public-процедурами:
• Property Let,
• Property Get.
Процедура Property Let вызывается каждый раз, когда свойство изменяется либо через окно свойств (во времени конструирования) либо из программы (во время выполнения). При изменении свойства работает код, состоящий из двух строк. Первая строка получает значение, передаваемое в параметре процедуры (которое является новым значением свойства), и присваивает его закрытой переменной, которая представляет свойство в элементе управления. Остальной код видит только локальное свойство m_Caption, а не фактическое свойство. Вторая строка сообщает Visual Basic, что свойство изменило значение. Метод PropertyChanged важен и должен быть включен в процедуру Property Let, так как именно так VB сохраняет любые изменения свойства, сделанные во время конструирования, чтобы эти значения имели эффект во время выполнения.
Процедура Property Get вызывается всякий раз, когда программа обращается к значению свойства. Эта процедура читает значение закрытой переменной m_Caption и присваивает его свойству Caption. Для каждого свойства должны быть определены процедуры Property Get и Property Let, включающие приведенные строки. Они составляют минимальный механизм для установки или чтения значений свойств.
Конечно, в эти процедуры можно добавлять и код проверки допустимости значений. Значение свойства TextAlignmen должно быть в диапазоне от 0 до 9. На данный момент элемент управления позволяет пользователю ввести любое значение этого свойства в окне свойств. Добавим проверку допустимости значения в процедуру Property Let для свойства TextAlignment. Сам код прост: он отвергает любые значения меньше 0 и больше 8.
Программа 16.4. Код проверки допустимости свойства TextAlignment в процедуре Property Let
Public Property Let TextAlignment(ByVal New_TextAlignment As _
Integer)
If m_TextAlignment >=0 And m_TextAlignitient<=8 Then
m_TextAlignment = New_TextAlignment
PropertyChanged "TextAlignment"
Else
MsgBox "Invalid value for this property"
' (Недопустимое значение для этого свойства)
End If
End Property
Оператор If проверяет допустимость введенного значения, и, если новое значение находится вне допустимого диапазона, попытка изменить свойство отклоняется. Измените процедуру Property Let согласно приведенной схеме и затем переключитесь на тестовую форму проекта. Выберите элемент управления FLEXLabel на форме, откройте окно свойств и установите свойство TextAlignment в заведомо недопустимое значение (13 или 1000, например). При попытке изменить свойство, установив его в недопустимое значение, элемент управления выдаст предупреждение и отклонит изменения. Пока не совсем понятно, как добиться, чтобы допустимые значения свойства отображались в раскрывающемся списке, как в других элементах управления VB? Это требует написания еще небольшого количества кода, о чем чуть позже.
После процедур Property Let и Property Get
для всех свойств идет код инициализации некоторых переменных.
Программа 16.5. Код инициализации
' Инициализация свойств элемента управления
Private Sub UserControl_InitProperties ()
Set Font = Ambient.Font
m_Caption = m_def_Caption
m_Effect = m_def_Effect
m_TextAlignment = m_def_TextAlignment
UserControl.BorderStyle = 1
UserControl.BackStyle = 1
End Sub
Операторы подпрограммы InitProperties() присваивают начальные значения закрытым переменным, которые представляют свойства элемента управления. Константы m_def_Caption, m_def_TextAlignment и m_def_Effect были определены ранее в программе. При помещении этого элемента управления на форму Visual Basic читает значения переменных m_Caption, m_TextAlignment и m_Effect и выводит их в соответствующих строках окна свойств элемента.
Сохранение и чтение значений свойств
Далее в тексте идут две интересные подпрограммы:
• ReadProperties;
• WriteProperties.
При изменении каких-либо свойств через окно свойств новые значения должны быть где-то сохранены. Зачем?
Приложение может (и обычно так и делает) изменять значения некоторых свойств во время выполнения. Но когда приложение останавливает свою работу и возвращается обратно в режим конструирования, свойства, измененные во время выполнения, должны принять те значения, которые они имели до старта программы - не значения по умолчанию, а те значения, которые они имели в режиме конструирования.
Visual Basic предусматривает специальный объект для сохранения всех значений свойств - PropertyBag. Этот объект предусматривает два метода: один - для сохранения значения свойства, другой - для его чтения. Разработчику элемента управления необязательно знать все подробности сохранения значений. Visual Basic сохраняет значения свойств и при запросе предоставляет их.
Первый метод имеет следующий синтаксис:
WriteProperty propertyName, value, defaultValue
где
propertyName - имя свойства (например, Effect), value
может иметь буквальное значение(1 или "some sizzling effect"), но почти всегда - это имя закрытой переменной, которая содержит значение свойства, defaultValue - значение свойства по умолчанию.
Примечание
Зачем нужно указывать одновременно и собственно значение и значение по умолчанию при вызове метода? Дело в том, что VB сравнивает эти два параметра, и, если значения совпадают, то VB не выполняет сохранение (для ускорения процесса сохранения и восстановления значений свойства). При последующем запросе значения свойства с помощью метода ReadProperty Visual Basic просто возвращает значение по умолчанию.
Метод ReadProperty имеет следующий синтаксис:
ReadProperty propertyName, defaultValue
где propertyName — имя свойства, defaultValue — значение этого свойства, сохраненное ранее в объекте PropertyBag. В коде события Write Properties объекта UserControl необходимо вызвать метод WriteProperty для каждого из свойств. Аналогично, в коде процедуры ReadProperties для каждого свойства необходимо вызвать метод ReadProperty. Ниже приведен код процедур Write Properties и ReadProperties сгенерированный мастером для элемента управления FLEXLabel.
Программа 16.6. Процедура ReadProperties элемента управления
' Загрузка значений свойств
Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
Set Font == PropBag.ReadProperty("Font", Ambient.Font)
UserControl.BorderStyle = PropBag.ReadProperty ("BorderStyle", 0)
UserControl.BackStyle = PropBag.ReadProperty ("BackStyle", 1)
UserControl.Enabled = PropBag.ReadProperty("Enabled", True)
UserControl.ForeColor = PropBag.ReadProperty ("ForeColor", _
&H80000012)
UserControl.MousePointer = PropBag.ReadProperty ("MousePointer", 0)
UserControl.OLEDropMode = PropBag.ReadProperty ("OLEDropMode", 0)
Set Picture = PropBag.ReadProperty ("Picture", Nothing)
m_Caption = PropBag.ReadProperty("Caption", m_def_Caption)
m_Effect = PropBag.ReadProperty("Effect", m_def_Effect)
m_TextAlignment = PropBag.ReadProperty ("TextAlignment", _
m_def_TextAlignment)
UserControl.BackColor = PropBag.ReadProperty ("BackColor", _
&H8000000F)
End Sub
Программа 16.7. Процедура WriteProperties элемента управления
' Сохранение значений свойств
Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
Call PropBag.WriteProperty("Font", Font, Ambient.Font)
Call PropBag.WriteProperty ("BorderStyle", UserControl.BorderStyle, 0)
Call PropBag.WriteProperty("BackStyle", UserControl.BackStyle, 1)
Call PropBag.WriteProperty("Enabled", UserControl.Enabled, True)
Call PropBag.WriteProperty("ForeCoior", UserControl.ForeColor, _
&H80000012)
Call PropBag.WriteProperty ("MousePointer", _
UserControl.MousePointer, 0)
Call PropBag.WriteProperty ("OLEDropMode", _
UserControl.OLEDropMode, 0)
Call PropBag.WriteProperty ("Picture", Picture, Nothing)
Call PropBag.WriteProperty("Caption", m_Caption, m_def_Caption)
Call PropBag.WriteProperty("Effect", m_Effect, m_def_Effect)
Call PropBag. WriteProperty ("TextAlignment", _
m_TextAlignment, m_def_TextAlignment)
Call PropBag.WriteProperty ("BackColor", _
UserControl.BackColor, &H8000000F)
End Sub
Передача событий
Последний раздел кода отображает различные события элемента управления на эквивалентные события объекта UserControl. Когда пользователь щелкает мышью на элементе управления ActiveX, Windows отправляет событие Click объекту UserControl. Разработчик элемента управления может обработать это событие в самом элементе управления (в этом случае приложение, которое использует элемент, не видит события Click), или передать событие приложению-контейнеру (в этом случае разработчик приложения, использующего элемент управления, сможет обработать сообщение), или сделать и то, и другое (сначала обработать событие в элементе управления, а затем передать его контейнеру).
Передать событие приложению можно с помощью метода
RaiseEvent. Событие Click объекта UserControl определено таким образом.
Private Sub UserControl_Click()
RaiseEvent Click
End Sub
Код остальных событий практически идентичен. Эти события вызывают оператор ReiseEvent для передачи события приложению-контейнеру. Если событие имеет параметры, например KeyPress, эти параметры перечисляются в круглых скобках после имени события.
Все, что сделал мастер — это вставил достаточно простой код. За исключением методов ReadProperty и WriteProperty, все остальное должно быть более или менее знакомо большинству программистов на Visual Basic. ActiveX — элемент управления, следовательно, не намного сложнее по сравнению со стандартным проектом. Осталось добавить еще несколько строк кода. В конце концов, необходимо указать элементу управления, как выравнивать надпись и обрисовывать ее с трехмерным эффектом.