Урок 4
Контрол, написанный "с нуля"
Добавление собственной Property Page
Четвертый урок посвящен созданию ActiveX Control'а "с нуля", поэтому и пояснения я постараюсь делать поподробнее. В качестве примера возьмем ProgressBar. "У-у-у", - скажете Вы. - "Их уже столько создано…" Тогда сразу же возникает резонный вопрос: "А почему так много?" По всей видимости, то, что уже существует по каким-либо параметрам не подходит для программистов. Элементарный вроде бы контрол, всего три свойства: минимальное, максимальное и текущее значения. Но он несет в себе одну важную функцию - скрашивает ожидание пользователя, когда по каким-либо причинам происходит задержка в работе программы. Так давайте отвлечем его: почему это должна быть обязательно синяя полоска, и почему она обязательно должна бежать именно слева - направо? И, в конце концов, давайте вообще откажемся от полоски как от таковой. Давайте создадим ProgressBar в форме песочных часов. Да это сложнее, но зато в конце получим более яркий эффект.
Открываем новый проект, назовем его pbHourglass, а сам UserControl переименуем в pbHrglss.
Итак, шаг первый: вырезаем из ActiveX Control'a песочные часы. Для этого будем использовать две API-функции: CreatePolygonRgn и SetWindowRgn, а также тип POINTAPI. Скопируем их с помощью API Text Viewer. Там же в глобальных объявлениях запишем нашу переменную Private rgnPts() As POINTAPI. Внутри процедуры UserControl_Resize объявим количество точек, рисующих фигуру и переопределим массив.
NB! Так как массив начинается с 0, то переменная, несущая информацию о количестве точек, естественно, будет на 1 меньше.
numAngle = 17
ReDim rgnPts(0 To numAngle) As POINTAPI
Далее определим координаты каждой точки по осям X и Y, и запишем их в массив. Для того чтобы сделать зависимой нашу фигуру от размеров контрола дадим координаты в относительных величинах, используя свойства UserControl'a - ScaleWidth и ScaleHeight.
rgnPts(0).X = 0
rgnPts(0).Y = 0
rgnPts(1).X = ScaleWidth
rgnPts(1).Y = 0
rgnPts(2).X = ScaleWidth
rgnPts(2).Y = ScaleHeight * 0.1
…ну и так далее, вплоть до rgnPts(17) - подробнее смотри в листинге. Рисуем фигуру:
hRgn = CreatePolygonRgn(rgnPts(0), numAngle + 1, WINDING)
А теперь вырезаем:
SetWindowRgn UserControl.hWnd, hRgn, True
Давайте пока на этом остановимся и перейдем ко второму шагу, созданию свойств нашего ActiveX Control'a. С тремя основными мы уже определились вначале, это Max, Min, Value. Наверно неплохо бы было создать свойство Percent, несущее в себе информацию о проценте выполненного действия. Сделаем это свойство "только для чтения" (Read Only). Ну и, конечно, хотелось бы "побаловаться" с цветом. Поэтому добавим еще 3 свойства, отвечающие за фоновый цвет, цвет окантовки и цвет "песка": BackColor, BasisColor и SandColor, соответственно. Сделаем свойство BackColor контрола равным &H00FFFFC0& (голубой цвет). А затем с помощью мастера создадим "болванку" (не забыли как это делается?). Для BackColor определим свойство BackColor от UserControl'a, а остальные свойства наберем согласно приведенной табличке:
Name |
Type |
Default Value |
Max |
Long |
100 |
Min |
Long |
0 |
Value |
Long |
0 |
Percent |
String |
vbNullString |
BasisColor |
OLE_COLOR |
&HFF0000 |
SandColor |
OLE_COLOR |
&H80FF& |
Откроем лист кодов и посмотрим, что у нас получилось. Из процедуры UserControl_WriteProperties смело удаляем строку, касающуюся Percent (у нас ведь свойство только для чтения). А Property Get Percent изменим следующим образом:
Percent = Format((m_Value - m_Min) * 100 / (m_Max - m_Min), "0") & "%"
NB! Функция Format используется для округления числа до целого, а вычитание m_Min производится для тех случаев, когда минимальное значение, устанавливаемое пользователем/программистом, не равно нулю.
Теперь напишем основную процедуру нашего контрола - DrawControl, которая и будет рисовать как сам ProgressBar, так и движение процесса.
Private Sub DrawControl()
'очищаем контрол от предыдущей графики
Cls
'напоминаем установки контрола
ScaleMode = 3 ' пикселы
DrawWidth = 2 ' толщина рисуемых линий
DrawStyle = 6 ' Inside Solid
AutoRedraw = True ' автоперерисовка контрола
'рисуем песок
If m_Value <> m_Max Then
'верхняя часть песочных часов. ScaleHeight умножаем на 0.5 т.к. используем только половину контрола.
'Рисуем прямоугольник, контрол-то уже вырезан.
Line (0, (m_Value - m_Min) * ScaleHeight * 0.5 / _
(m_Max - m_Min))-(ScaleWidth, ScaleHeight * 0.5), m_SandColor, BF
'струя песка.
Line (ScaleWidth * 0.45, ScaleHeight * 0.5)-(ScaleWidth * 0.54, ScaleHeight * 0.95), m_SandColor, BF
'нижняя часть
Line (0, ScaleHeight - ((m_Value - m_Min) * ScaleHeight * 0.5 / _
(m_Max - m_Min)))-(ScaleWidth, ScaleHeight * 0.95), m_SandColor, BF
End If
'рисуем верх и низ базисным цветом
Line (0, 0)-(ScaleWidth, ScaleHeight * 0.05), m_BasisColor, BF
Line (0, ScaleHeight * 0.95)-(ScaleWidth, ScaleHeight), m_BasisColor, BF
'окантовка так же базисным цветом. Окантовка проходит по тем же точкам, что и вырезался контрол
Line (ScaleWidth, 0)-(ScaleWidth, ScaleHeight * 0.1), m_BasisColor
Line -(ScaleWidth * 0.9, ScaleHeight * 0.25), m_BasisColor
Line -(ScaleWidth * 0.7, ScaleHeight * 0.45), m_BasisColor
'… ну и так далее по всем точкам.
'обновляем
Refresh
End Sub
Раз у нас все изменения завязаны на процедуру DrawControl, то добавим ee в Property Let каждого свойства последней строкой. Добавим ее так же и в UserControl_Resize.
NB! Имя Property, которое описывается первым в окне кодов - будет первым выделяться в окне свойств при попадании фокуса на контрол во время разработки. Поэтому давайте переместим Property Let и Property Get свойства Value перед другими свойствами в окне кодов.
Закончив с написанием кодов, займемся "уборкой в квартире", т.е переходим к заключительному третьему шагу создания ActiveX Control'a. Напишем комментарии к созданным нами свойствам, установим свойство Value как Default (не забыли? меню Tools/Procedure Attributes… кнопка Advanced и в поле Procedure ID выберем опцию (Default)). Создадим frmAbout, добавим в окно кодов
Public Sub About()
frmAbout.Show vbModal
End Sub
и все в том же Tools/Procedure Attributes… --> кнопка Advanced --> поле Procedure ID устанавливаем опцию AboutBox.
"Аппетит приходит во время еды!" AboutBox с Вашей фамилией автора - это, конечно, ласкает самолюбие, но… Вот в других контролах есть такая опция (Custom), нажимаешь ее и появляется окошко с закладками, а там сразу все твои свойства. Удобно? - Удобно! Сделаем? А почему бы и нет. Показываю как это сделать проще всего. Меню Add-Ins/Add-In Manager…Выбираем Property Page Wizard. Открываем его. Мастер у нас умный, поэтому, прочитав, что есть свойства As OLE_COLOR, он сразу же предлагает стандартную страничку по цветам StandartColor. Пометим ее CheckBox. А теперь создадим свою страничку. Нажмем кнопку Add и в диалоговом окне введем свое название.
NB! Название не должно совпадать с именем контрола или проекта и должно состоять из одного слова. Позже, открыв Property Page, в свойстве Caption, мы сможем задать любое другое название, которое захотим увидеть в названии ярлыка.
Поэтому дадим ему что-то нейтральное типа General, или как в данном случае можно назвать ProgressBar. ОК! Так же пометим его и с помощью кнопок слева поднимем в первую строку списка.
NB! Перемещать странички мы можем и позже. Для этого выбирается в окне свойств UserControl'a свойство PropertyPages. Там же Вы сможете ознакомиться, какие еще стандартные странички предлагает нам VB.
В следующем шаге выберем наши свойства и поместим их в правое окно под ярлыком ProgressBar. Кстати, если щелкнуть на ярлыке StandartColor то увидим, что наши свойства, завязанные на цвет - уже там.
Кнопка Finish. В окне проектов появилась новая папка Property Pages, а в ней наша страничка ProgressBar. Откройте ее и исследуйте, я думаю на будущее это Вам пригодится.
Теперь самое время создать тестировочный проект, чтобы посмотреть, что у нас получилось (Это в статье - когда автор рассказывает об уже созданных и отработанных свойствах и методах. В жизни же, обычно, тестировочный проект создается гораздо раньше, для проверки работоспособности отдельных блоков). Добавляем проект Standart EXE, называем его prjTest, переименовываем форму в frmTest. Разместим на форме наш ActiveX Control, Label с именем lblPercent и командную кнопку Command1. Теперь осталось придумать, чем бы занять наш ProgressBar. Допустим, это будет цикл перебора значений. Запишем код:
' делаем их невидимыми - пока нет для них работы.
Private Sub Form_Load()
pbHrglss1.Visible = False
lblPercent.Visible = False
End Sub
Private Sub Command1_Click()
Dim i&
With pbHrglss1
' устанавливаем значения (эти 2 строки можно не писать, а просто установить их в окне свойств)
.Min = 0
.Max = 300
' делаем видимыми
.Visible = True
lblPercent.Visible = True
' запускаем цикл
For i = .Min To .Max
.Value = i
lblPercent.Caption = .Percent
DoEvents ' для обеспечения доступа к программе, например, работа кнопки отмена процесса
Next
Beep ' сигнал об окончании цикла
' снова делаем невидимыми - работа окончена
.Visible = False
lblPercent.Visible = False
End With
End Sub
Протестируем. Работает! Ну и напоследок еще одна "мелочевка". Поиграем с окном свойств для нашего контрола. Откроем frmTest, выделим pbHrglss1, и в окне свойств по очереди будем выбирать: (About) - работает!, (Custom) - замечательно!, BackColor - как и положено выпадает цветное меню, Max … Стоп! Хотели ввести цифры, а выходит все равно (Custom). Аналогичная ситуация и с Min и с Value. Давайте поправим. Закрываем форму, открываем pbHrglss и снова в меню Tools/Procedure Attributes… --> кнопка Advanced. В поле Name выбираем Max и что же мы видим? Наш Property Page Wizard слегка перестарался: в поле Use this Page in Property Browser он, по умолчанию, установил ProgressBar. Снимем эту опцию и установим (None). Аналогичную операцию проведем для свойств Min и Value. Вернемся в frmTest и попробуем снова. Вот теперь порядок.
Можно компилировать наш ActiveX Control. Для желающих: увеличив количество точек, по которым вырезается контрол, Вы можете сделать его более изящным за счет плавности линий.
Как всегда в конце статьи полный листинг.
1999 г