InfoCity
InfoCity - виртуальный город компьютерной документации
Реклама на сайте







Размещение сквозной ссылки

 

Как создать ActiveX Control


Михаил Эскин


(заключительная статья)


 

Я очень рад, что мои статьи, судя по письмам, оказались нужными очень многим. Вполне возможно, что еще столько же прочитали их, нашли что-то для себя, но поленились написать мне. Я не в обиде. Это нормальное состояние. Начиная первую статью, я хотел показать, что пределов в создании ActiveX Control'ов - практически не существует. Пределы нам устанавливают только VB и объем наших знаний. Поэтапно проводя Вас от простого к более сложному, я и сам много чему научился. Ведь чтобы доступно преподать какие-то знания необходимо самому представлять этот процесс очень и очень ясно. Кроме того, необходимо было подобрать примеры, и не просто примеры, а чтобы они отражали суть статьи, и, кроме того, были бы еще нестандартными, и, что самое главное, нужными. А листинги. Сколько раз приходилось выверять их, чтобы не было ошибок, описок и т.п. Ведь просто скопировав их и запустив в своей программе, если они не будут работать ...

Это моя последняя в этом цикле статья. Посмотрев, как делают другие авторы в заключительных статьях - сводят воедино все, что было дано в предыдущих, я решил не отставать от них. Но как оказалось - это достаточно скучно (ведь это все уже было!). Поэтому я решил сделать компромиссный вариант: будет и новое и старое. Да, кое-что я буду повторять в n-ый раз, кое-чему научу Вас новому. Однако особенностью данной, заключительной, статьи будет полное пошаговое описание всех процессов создания ActiveX Control'a.

NB! - [аббревиатура от лат. Nota Bene!] обрати внимание! - Это обозначение будет встречаться там, где могут возникнуть определенные сложности.

Что будем создавать сегодня? Контрол-контейнер произвольной формы, причем форму будет задавать сам программист, работающий с уже готовым контролом. Почему опять контейнер? Да просто потому, что превратить его затем в кнопку, лейбл или текстовое поле не представит особого труда. С какими трудностями мы столкнемся? Первая и основная — создание массива свойств пользовательского типа. Если честно говорить, сам я шел к этому не один месяц.

Итак, начнем. Откроем 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

 

Gradient

Наличие/отсутствие градиентной заливки

Boolean

False

GradientRed

Начальный цвет заливки

OLE_COLOR

vbWhite

GradientGreen

Конечный цвет заливки

OLE_COLOR

vbBlack

GradientBlue

     

GradientOrientation

     

BorderColor

Цвет границы

OLE_COLOR

 

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, X As Single, Y As Single

Нажатие клавиши мыши

MouseUp

Button As Integer, Shift As Integer, X As Single, Y As Single

Отпускание клавиши мыши

MouseMove

Button As Integer, Shift As Integer, X As Single, Y As Single

Перемещение курсора над контролом

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’а. А чтобы они заработали нужно совсем чуть-чуть кода. Допишем в 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

Попробуем в тестировочной форме.

Займемся теперь градиентной заливкой. Но человек существо неудовлетворенное. Жесткие градации переходов цвета … Давайте предоставим пользователю самому устанавливать нужную расцветку. Обратимся опять к 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-файл и прикрепим его к контролу. И вот только теперь можно «трубить в фанфары»: ACTIVEX CONTROL – готов! Но и сейчас не поленитесь – «погоняйте» его в тестировочной форме во всех мыслимых режимах, а в немыслимых - будет это делать пользователь :-).

Не забудьте так же и о поддержке версий, так называемые апгрейды (кто не помнит как это делается см. статью 5)

По традиции - листинг

Напоследок, хочу напомнить, что я не единственный, кто создает контролы. Пишите о своих находках и ньюансах создания. ПОДЕЛИСЬ – БОГАЧЕ СТАНЕШЬ!

[Назад][Содержание]


Реклама на InfoCity

Яндекс цитирования



Финансы: форекс для тебя








1999-2009 © InfoCity.kiev.ua