Несколько ActiveX Control'ов в одном проекте
Поддержка апгрейдов
И поводом для этого урока послужил мой контрол Indicatop. Собственно говоря, контрол писался под конкретную задачу и ряд свойств не был просто-напросто предусмотрен. А когда Boris Rudoy выложил его на сайт, вот тут-то и посыпались просьбы улучшить, замечания об отсутствии ряда параметров и т.п. Пришлось сесть за компьютер и переработать некоторые вещи. Но что бы снова не впасть в немилость решил описать это в данном уроке - пусть каждый кто хочет - переделывает под себя.
Итак, создаем новый проект и называем его Indicator, а UserControl переименовываем в Ind. Зададим изначально одно свойство для UserControl'a: ScaleMode = 3 (pixel). Расположим на нем группу из 7 Label (Name = lblI, Index - от 0 до 6, Caption = "", BackColor = &H00FF0000&-синий). Первые четыре будут изображать вертикальные сегменты, а остальные - горизонтальные. Подготовительный период закончен. В принципе, можно было использовать Shape или PictureBox. Но я остановился именно на Label, так как у Shape при изменении цвета необходимо было обрабатывать кроме FillColor еще и BorderColor, а PictureBox, по-моему он просто "тяжеловат" для таких операций - не очень заметно на глаз, но ...
Продолжим создание контрола. Основными просьбами пользователей были масштабируемость и произвольная засветка отдельных элементов "электронных цифр" (например, чтобы можно было высветить "E" или "Г" - хотя это уже и не цифры). Определимся с набором свойств для данного контрола:
Название |
Описание |
Тип |
Значение по умолчанию |
Entries |
Высвечивание отдельных частей цифры |
String |
"1111111" |
Thickness |
Толщина сегмента (в пикселах) |
Long |
2 |
BackColor |
Фоновый цвет |
OLE_COLOR |
&H8000000F |
ForeColor |
Цвет цифр |
OLE_COLOR |
&HFF0000 |
NB! Обратите внимание, что свойству ForeColor контрола мы присваиваем свойство BackColor лейблов.
События определим стандартные: Click, DblClick, MouseDown, MouseUp, MouseMove и ... добавьте свои, какие Вам необходимы.
Далее с помощью визарда генерируем основу кода. Описание (Description) свойств и методов можно производить и в нем на четвертой страничке в соответствующем поле.
NB! Если вместо кириллических букв у Вас выписывается абракадабра - значит у Вас не зарегистрирован в Windows шрифт Tahoma. Сделайте это и многие другие блоки VB, которые опираются на него будут корректно показывать Вам свои кириллические записи. Для этого необходимо: открыть файл WIN.INI и в секции [FontSubstitutes] записать следующую строку Tahoma,0=Tahoma,204. После этого перезагрузите Windows.
Теперь займемся исправлением некоторых кодов. А для этого необходимо разобраться, что делает данное свойство. Итак свойство Entries: это строка, состоящая из нулей и единиц (битовый аналог), каждое значение которых говорит включен или выключен определенный сегмент цифры. Поэтому изменим соответствующим образом Propery Let для него:
Dim i%, n$
If VerificationEntries(New_Entries) = True Then
m_Entries = New_Entries
For i = 0 To 6
n = Mid(m_Entries, i + 1, 1)
lblI(i).Visible = -1 * Val(n)' умножаем на -1, т.к. True=-1
Next
Else
MsgBox "Error!"
Exit Property
End If
PropertyChanged "Entries"
Функция VerificationEntries - проверяет допустимость вводимых знаков и длину строки.
Свойство Thickness - определяет толщину сегмента цифры и Property Let озвучивается у него следующим образом:
Dim i&
m_Thickness = New_Thickness
For i = 0 To 3
lblI(i).Width = m_Thickness 'для вертикальных сегментов
Next
For i = 4 To 6
lblI(i).Height = m_Thickness 'для горизонтальных сегментов
Next
PropertyChanged "Thickness"
UserControl_Resize
Теперь самое время обратить внимание на массив лейблов (а их у нас 7). Чтобы свойство ForeColor работало с каждым лейблом, а не с каким-нибудь одним необходимо вставить цикл For-Next в Property Let для него, присваивая новое значение каждому лейблу отдельно. Аналогичная ситуация и в процедурах UserControl_ReadProperties и UserControl_WriteProperties.
И наконец события. Создавая с помощью мастера события мы присваивали их UserControl'у. В кодах это озвучивается примерно так:
Private Sub UserControl_Click()
RaiseEvent Click
End Sub
А если пользователь попадет на лейбл? Значит мы должны добавить процедуру и для него:
Private Sub lblI_Click(Index As Integer)
RaiseEvent Click
End Sub
Осталось чуть-чуть - придать масштабируемость нашему контролу. Это мы сделаем в UserControl_Resize.
'проверка на минимальные размеры
If (ScaleHeight - 3 * m_Thickness - 8 <= 0) Or (ScaleWidth - 2 * m_Thickness - 4 <= 0) Then
Size (3 * m_Thickness + 4) * Screen.TwipsPerPixelX, (5 * m_Thickness + 8) * Screen.TwipsPerPixelY
Exit Sub
End If
'установка размеров лейблов в зависимости от размеров контрола и толщины сегментов цифр
'2 и числа кратные 2, в данном случае, это 2 пиксела - расстояния между сегментами, чтобы они не сливались
lblI(0).Move 0, m_Thickness + 2, m_Thickness, ScaleHeight * 0.5 - 1.5 * m_Thickness - 4
lblI(1).Move
0, 0.5 * ScaleHeight + 0.5 * m_Thickness + 2, m_Thickness, _
ScaleHeight * 0.5 -
1.5 * m_Thickness - 4
lblI(2).Move
ScaleWidth - m_Thickness, m_Thickness + 2, m_Thickness, _
ScaleHeight * 0.5 -
1.5 * m_Thickness - 4
lblI(3).Move
ScaleWidth - m_Thickness, 0.5 * ScaleHeight + 0.5 * m_Thickness + 2, _
m_Thickness, ScaleHeight * 0.5 - 1.5 * m_Thickness - 4
lblI(4).Move m_Thickness + 2, 0, ScaleWidth - 2 * m_Thickness - 4, m_Thickness
lblI(5).Move
m_Thickness + 2, 0.5 * ScaleHeight - 0.5 * m_Thickness, _
ScaleWidth - 2 *
m_Thickness - 4, m_Thickness
lblI(6).Move
m_Thickness + 2, ScaleHeight - m_Thickness, ScaleWidth - 2 * m_Thickness - 4,
_
m_Thickness
Создаем тестировочный проект и "играем" с нашим контролом.
Наигрались? :)
А теперь давайте подумем о пользователе (программисте), который с Вашим контролом будет работать. Всем ли необходимо отдельное высвечивание сегментов? Мне кажется нет. Многим для своих задач достаточно выведение на экран только цифр. А удобно ли в данном случае для них свойство Entries? Хорошие вопросы...
Поэтому давайте на основе нашего контрола создадим еще один, который будет манипулировать только с цифрами, поддерживая все остальные свойства (кроме Entries) и события контрола Indicator. Итак в наш проект добавим еще один UserControl с помощью меню Project/Add User Control, и поместим на него наш контрол (NB! нескомпилированный). Назовем его Num. Зайдем в мастера и увидим, что появилась еще одна страничка в самом начале, позволяющая выбрать, какой ActiveX Control будет сейчас получать свои свойства, события и методы. Выберем строку Indicator: Num. На следующих шагах выберем свойства (кроме Entries) и события те же, что и у Ind и соответственно сделаем их привязку к Ind1. Добавим новое свойство Numeric (Тип Integer, значение по-умолчанию 0).
Зайдем в окно кодов и сделаем поправку для Property Let у свойства Numeric:
'проверка на вводимое значение
If Not IsNumeric(New_Numeric) Then Exit Property
m_Numeric = New_Numeric
'озвучивание цифр
Select Case m_Numeric
Case 0
Ind21.Entries = "1111101"
Case 1
Ind21.Entries = "0011000"
Case 2
Ind21.Entries = "0110111"
Case 3
Ind21.Entries = "0011111"
Case 4
Ind21.Entries = "1011010"
Case 5
Ind21.Entries = "1001111"
Case 6
Ind21.Entries = "1101111"
Case 7
Ind21.Entries = "0011100"
Case 8
Ind21.Entries = "1111111"
Case 9
Ind21.Entries = "1011111"
Case Else 'ну например двузначное число
Ind21.Entries = "0000000"
End Select
PropertyChanged "Numeric"
И сделаем запись в UserControl_Resize (позиционирование и масштабирование контрола):
Ind21.Move 0, 0, ScaleWidth, ScaleHeight
Разместим на той же тестировочной форме новый контрол Num и теперь "поиграем" с ним. Кстати, на панели контролов Вы увидите уже две кнопки для своих контролов. Здесь я не буду останавливаться на оформлении своего контрола, создании Property Page и т.п. Кто забыл - посмотрите предыдущие статьи. Можно приступать к компиляции.
В заключение хотелось бы немного поговорить о поддержке апгрейдов. В тему статьи это не особенно укладывается, но рано или поздно Вам придется с этим столкнуться. Щелкнем правой кнопкой мыши в окне проектов на Indicator и выберем меню Indicator Properties. В самом первом ярлыке General увидим, что CheckBox с названием Upgrade ActiveX Controls уже отмечен. Это говорит о том, что более поздние версии будут заменяться более свежими. А в ярлыле Component во фрейме Version Compatibility (совместимость версий) выберите Project Compatibility. При первом создании ActiveX Control'a текстовое поле под ним останется пустым. Однако, после компиляции, там появится название Вашего проекта. Т.е. все последующие изменения и компиляции контрола будут ссылаться на предыдущий контрол, для обеспечения совместимости.
NB! Если в новой версии Вы не включите какое-либо свойство, событие или метод, то обновив контрол рискуете (чаще всего на машине пользователя - Вы-то помните об изменениях) получить ошибку о неподдержании соответствующей части программы.
Как всегда в конце полный листинг нашего контрола.
1999