Начиная первый урок, я хотел показать, что пределов в создании ActiveX Control'ов - практически не существует. Пределы нам устанавливают только VB и объем наших знаний. Поэтапно проводя Вас от простого к более сложному, я и сам много чему научился. Ведь чтобы доступно преподать какие-то знания необходимо самому представлять этот процесс очень и очень ясно. Кроме того, необходимо было подобрать примеры, и не просто примеры, а чтобы они отражали суть статьи, и, кроме того, были бы еще нестандартными, и, что самое главное, нужными. А листинги. Сколько раз приходилось выверять их, чтобы не было ошибок, описок и т.п. Ведь если пользователь просто скопировав их запустит в своей программе, а они не будут работать ...
Это мой последний в этом цикле урок. Посмотрев, как делают другие авторы в заключительных статьях - сводят воедино все, что было дано в предыдущих, я решил не отставать от них. Но как оказалось - это достаточно скучно (ведь это все уже было!). Поэтому я решил сделать компромиссный вариант: будет и новое и старое. Да, кое-что я буду повторять в n-ый раз, кое-чему научу Вас новому. Однако особенностью данного, заключительного, урока будет полное пошаговое описание всех процессов создания ActiveX Control'a.
Что будем создавать сегодня? Контрол-контейнер произвольной формы, причем форму будет задавать сам программист, работающий с уже готовым контролом. Почему опять контейнер? Да просто потому, что превратить его затем в кнопку, лейбл или текстовое поле не представит особого труда (см. Урок 6). С какими трудностями мы столкнемся? Первая и основная — создание массива свойств пользовательского типа. Если честно говорить, сам я шел к этому не один месяц.
Итак, начнем. Откроем VB и создадим новый проект с именем FigureControl. Для UserControl установим следующие параметры: Name = FigCtrl, AutoRedrive = True, ScaleMode = 3 (Pixel).
NB! Большинство (если не все) API-функции работают с пикселами, а не твипами.
Установим свойства проекта. Для этого щелкнем правой клавишей в окне проектов на FigureControl и выберем меню FigureControl Properties... Или так: меню Project/ FigureControl Properties... Далее. В Project Description напишем "Контейнер произвольной формы". Под ярлыком Make установим Auto Increment, заполним графы во фрейме Version Information. Под ярлыком Compile выберем опцию компиляции Compile to P-Code.
Не откладывая в долгий ящик сразу же создадим тестировочный проект (меню File/Add Project... и выберем Standart EXE). Обзовем: Name = prjTest; для формы: Name = frmTest.
NB! Если у Вас VB6, то не забудте щелкнуть правой клавишей в окне проектов на prjTest и выбрать меню Set us Start Up. VB5 делает это автоматически.
Определимся со свойствами, методами и событиями контрола.
Свойства контрола:
Имя |
Описание |
Тип |
Значение по умолчанию |
BackColor |
Цвет фона при Gradient=False |
OLE_COLOR |
&H8000000F& |
Gradient |
Наличие/отсутствие градиентной заливки |
Boolean |
False |
GradientRed |
Красная составляющая цвета |
Integer |
0 |
GradientGreen |
Зеленая составляющая цвета |
Integer |
0 |
GradientBlue |
Синяя составляющая цвета |
Integer |
-1 |
GradientOrientation |
Направление цвета градиентной заливки |
constOrientation |
0 |
BorderColor |
Цвет границы |
OLE_COLOR |
&H808080 |
BorderThickness |
Толщина границы в пикселах |
Integer |
2 |
Caption |
Надпись |
String |
"FigureControl" |
Font |
Шрифт |
Font |
MS Sans Serif, 8 |
ForeColor |
Цвет надписи |
OLE_COLOR |
vbBlack |
Методы контрола:
Название |
Принимаемые значения |
Возвращаемый тип |
Описание |
AddPoint |
X As Long, Y As Long |
[Пусто] |
Добавление точки, определяющей контур контрола |
ShowFigure |
[Пусто] |
[Пусто] |
Вывод на экран вырезанной фигуры |
Refresh |
[Пусто] |
[Пусто] |
Обновление |
События контрола:
Название |
Принимаемые значения |
Описание |
Click |
[Empty] |
Щелчок по контролу |
DblClick |
[Empty] |
Двойной щелчок по контролу |
KeyDown |
KeyCode As Integer, Shift As Integer |
Нажатие клавиши |
KeyUp |
KeyCode As Integer, Shift As Integer |
Отпускание клавиши |
KeyPress |
KeyAscii As Integer |
Нажатие+отпускание клавиши |
MouseDown |
Button As Integer, Shift As Integer, |
Нажатие клавиши мыши |
MouseUp |
Button As Integer, Shift As Integer, |
Отпускание клавиши мыши |
MouseMove |
Button As Integer, Shift As Integer, |
Перемещение курсора над контролом |
Refresh |
[Empty] |
Обновить |
Ну вот, вроде со всем определились. Теперь пора бы и за создание взяться. Давайте начнем с самого трудного: с определения контуров будущего контрола. Если бы мы жестко прописали ряд возможных форм для нашего контрола, то особых трудностей это не составило бы. Просто мы определили бы какое-то свойство вроде FormForControl, через энум описали возможные варианты, а в коде программы жестко прописали бы каждую точку каждого варианта. Аналогичные манипуляции мы проводили, когда создавали ProgressBar в форме песочных часов (см. Урок 4). Все сложности начинаются, когда мы собираемся предоставить программисту самому рисовать форму контрола такую, какую он хочет. Проблема заключается в следующем: создание фигуры произвольной формы описывается двумя API-функциями и типом:
Declare Function SetWindowRgn Lib
"user32" (ByVal hWnd As Long, ByVal hRgn As Long, _
ByVal bRedraw As Boolean) As Long
Declare Function CreatePolygonRgn Lib
"gdi32" (lpPoint As POINTAPI, _
ByVal nCount As Long, ByVal nPolyFillMode As Long) As Long
Type POINTAPI
X As Long
Y As Long
End Type
Однако если мы попробуем создать свойство со ссылкой на пользовательский тип данных (а именно таковым воспринимает VB тип POINTAPI), то получим грозное сообщение о том, что это недопустимо. Желающие и неверующие могут проверить :-)
Натура программиста такова, что если не получается взять наскоком напрямую, он начинает искать обходные пути. Не скажу что таких путей много. Мой вариант - это создание коллекции классов. Однако, в процессе написания статьи, Boris Rudoy предложил более изящный вариант работы с массивами, который я и хочу предложить Вашему вниманию.
В разделе (General) объявим переменные
Private rgn() As POINTAPI
Private FirstAdding As Boolean
Private rgnItemX() As Long
Private rgnItemY() As Long
Добавим внутреннюю функцию, определяющую общее количество точек
Private Function PointCount() As Integer
PointCount = UBound(rgnItemX)
End
Function
И непосредственно сами методы добавления точек и вырезания фигуры.
Public Sub AddPoint(ByVal X As Long, ByVal Y As Long)
Dim i As Integer
i = UBound(rgnItemX)
If FirstAdding
Then’ если добавляется
первая точка
i = 0
FirstAdding = False
Else
i
= i + 1
End If
ReDim Preserve rgnItemX(i)’переопределяем массивы
ReDim Preserve rgnItemY(i)
rgnItemX(i) = X
rgnItemY(i) = Y
End Sub
Public Sub ShowFigure()
Dim i As Integer, count As Long, hRgn As Long
On Error Resume Next
count = PointCount + 1
ReDim rgn(count) As POINTAPI’инициализируем массив точек
For i = 1 To count
rgn(i).X =
rgnItemX(i - 1)
rgn(i).Y =
rgnItemY(i - 1)
Next
hRgn = CreatePolygonRgn(rgn(1), count, 0)’ рисуем
SetWindowRgn UserControl.hWnd, hRgn, True’ и вырезаем контрол
End Sub
Изменим цвет UserControl'a на какой-нибудь другой, отличный от серого, чтобы во время исполнения мы могли его видеть на серой форме.
Установим у тестировочной формы свойство ScaleMode = 3 (Pixel), AutoRedraw = True и разместим на ней наш прототип контрола. Запишем ей код:
Private Sub Form_Load()
'Рисуем точки контура
With FigCtrl1
.AddPoint .Width / 2, 0
.AddPoint .Width, .Height / 2
.AddPoint
.Width / 2, .Height
.AddPoint
0, .Height / 2
End With
FigCtrl1.ShowFigure
End Sub
Запускаем программу. Если все было сделано правильно, то контрол должен приобрести форму ромба.
Итак, самое сложное сделано: мы создали вывод контрола произвольной формы. Пойдем дальше. Не всегда вырезанный контрол имеет ровный край и это смотрится не очень презентабельно. Давайте обведем контуры. У нас для этого предусмотрено 2 свойства, отвечающие за цвет и толщину окантовки. Создадим коды для них с помощью Wizard’а (работа с визардом подробно описана в Уроке 1). А чтобы они заработали нужно совсем чуть-чуть кода. Допишем в ShowFigure в самый конец:
DrawWidth = m_BorderThickness
For i = 1 To count
If i
= count Then’ для соединения последней и
первой точек
Line (rgn(i).X, rgn(i).Y)-(rgn(1).X, rgn(1).Y),
m_BorderColor
Else’ для всех остальных точек
Line (rgn(i).X, rgn(i).Y)-(rgn(i + 1).X, rgn(i + 1).Y),
m_BorderColor
End If
Next
Попробуем в тестировочной форме.
Займемся теперь градиентной заливкой. Общие принципы мы рассматривали в Уроке 6. Но человек существо неудовлетворенное. Жесткие градации переходов цвета … Давайте предоставим пользователю самому устанавливать нужную расцветку. Обратимся опять к Wizard’у и добавим свойства, отвечающие за градиентную заливку. В Property Let каждого из вновь добавленных свойств введем ссылку на внутреннюю процедуру DrawControl, а для свойств ответственных за градиентный цвет вначале введем проверку типа
If New_GradientRed < 0 Or New_GradientRed > 255
Then
New_GradientRed = -1
End If
Займемся самой процедурой.
Private Sub DrawControl()
Cls’ очистка контрола от графики
If m_Gradient = True Then
Dim R%, G%, B%
Dim i%, NbrRects%, GradValue%,
GradColor&
NbrRects% = 127
ScaleMode = 3’
установка свойств контрола
DrawWidth = 2
DrawStyle = 6
AutoRedraw = True
For i = 1 To NbrRects
GradValue = 255 - (i * 2 - 1)
If m_GradientRed = -1 Then
R
= GradValue
Else
R
= m_GradientRed
End If
If m_GradientGreen = -1 Then
G
= GradValue
Else
G
= m_GradientGreen
End If
If m_GradientBlue = -1 Then
B
= GradValue
Else
B
= m_GradientBlue
End If
GradColor = RGB(R, G, B)
‘ непосредственный вывод заливки в зависимости от ориентации
Select Case m_GradientOrientation
Case 0
Line
(0, ScaleHeight * (i - 1) / NbrRects)-(ScaleWidth, ScaleHeight * i / NbrRects),
_
GradColor, BF
Case 1
Line
(ScaleWidth * (i - 1) / NbrRects, 0)-(ScaleWidth * i / NbrRects, ScaleHeight), _
GradColor, BF
Case Else 'если по ошибке поставят < 0 или >
1
Line (0, ScaleHeight * (i - 1) / NbrRects)-(ScaleWidth,
ScaleHeight * i / NbrRects), _
GradColor, BF
End Select
Next i
End If
ShowFigure’ вырезаем фигуру
End Sub
Не откладывая в дальний ящик, давайте создадим Property Page, где будут отражаться наши свойства (Name = ppFigureControl, Caption = “Figure Control” – именно Caption будет выводиться в качестве ярлыка). Т.к. GradientOrientation имеет тип constOrientation, то мастер его не увидит. Поэтому давайте используем ComboBox для вывода этого свойства (подробнее как это делается см. Урок 6). На этом можно было бы успокоиться, но давайте сделаем установку свойств GradientRed, GradientGreen и GradientBlue более удобной. Удалим текстовые поля, относящиеся к ним и расположим CheckBox и Scroll для каждого из этих свойств. Дадим имена по принципу chkRed и scrRed. Кроме того для каждой линейки прокрутки установим свойства: LargeChange = 10, Max = 255, Min=0, SmallChange = 1. Подробнее коды можно посмотреть в листинге.
Вернемся к нашему контролу. У нас осталось три свойства отвечающие за вывод надписи. С помощью того же Wizard’а создаем коды для них и в Property Let делаем ссылку на процедуру DrawControl. А в саму процедуру перед ShowFigure допишем:
CurrentX = (ScaleWidth - TextWidth(m_Caption))
/ 2
CurrentY = (ScaleHeight - TextHeight(m_Caption)) / 2
UserControl.ForeColor = m_ForeColor
UserControl.Print m_Caption
Добавим в проект форму About (меню Project/Add Form) и коды в проект
Public Sub About()
frmAbout.Show vbModal
End Sub
Теперь сделаем описание для наших свойств, событий и методов. Меню Tools/Procedure Attributes … кнопка Advanced. Для метода About в поле Procedure ID выберем опцию AboutBox. Для всех свойств в поле Property Category введем «Figure Control» для группирования их в окне свойств под ярлыком Categorized. Свойству Caption кроме всего прочего в поле Procedure ID установим опцию (Default). Естественно, не забудем для всех свойств, событий и методов в поле Description написать что же они выполняют.
Подготовим Help-файл и прикрепим его к контролу (подробнее см. Урок 9). И вот только теперь можно «трубить в фанфары»: ACTIVEX CONTROL – готов! Но и сейчас не поленитесь – «погоняйте» его в тестировочной форме во всех мыслимых режимах, а в немыслимых - будет это делать пользователь :-).
Не забудьте так же и о поддержке версий, так называемые апгрейды (кто не помнит как это делается см. Урок 5)
Здесь, как всегда, можно взять полный листинг.
Напоследок, хочу напомнить, что я не единственный, кто создает контролы. Пишите о своих находках и нюансах создания.
ПОДЕЛИСЬ – БОГАЧЕ СТАНЕШЬ!