ActiveX

Урок 10 (заключительный)

 

Начиная первый урок, я хотел показать, что пределов в создании 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,
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’а (работа с визардом подробно описана в Уроке 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)

 

Здесь, как всегда, можно взять полный листинг.

Скачать пример

 

Напоследок, хочу напомнить, что я не единственный, кто создает контролы. Пишите о своих находках и нюансах создания.

ПОДЕЛИСЬ – БОГАЧЕ СТАНЕШЬ!

 Назад

Hosted by uCoz