Мир ActiveX Control'ов многолик и многообразен. Встречаются совершенно различные по внешнему виду (и даже без него) и по внутреннему содержанию. Одни используют для построения стандартные ActiveX Control'ы, другие предпочитают создавать "с нуля". Одни контролы облегчают коммуникацию, другие производят внутри себя сложные вычисления, выдавая "на-гора" уже готовый результат, а третьи служат просто для украшения программы.
ActiveX Control'ы - это программы в программе. И, завершив создание его, получаешь удовлетворение нисколько не меньшее, чем завершив большой проект. Мы садимся писать контролы, когда хотим облегчить себе работу в будущем, просто используя готовый интерфейс и набор свойств, событий и методов. В большинстве случаев, начало создания контрола является спонтанным событием. Просто-напросто замечаешь, что в программе появляется два-три куска идентичных кодов, завязанных на интерфейс. И тогда, выделяешь их в отдельный контрол, попутно наделив дополнительными свойствами и методами (на будущее).
К созданию нынешнего ActiveX Control'а меня подтолкнул Д.Соловьев. Он создает программу, завязанную на проверку знаний иностранных слов. Причем в одном месте, выводить эти слова необходимо с выделением окончания. Однако, как оказалось, ни Label, ни TextBox не позволяют производить разбивку слова по цветам или шрифтам. Эрзац-решением стало использование для этой цели RichTextBox'а. Однако идея уже "проклюнулась", и был создан контрол SelTextEx. Его интересной особенностью является использование в методе, выводящим текст, массива пользовательского типа.
Итак, создайте новый проект ActiveX Control. Назовем его SelectedTextEx. Созданный по умолчанию UserControl переименуем в SelTextEx. Кроме того установим свойство AutoRedraw = True. Размеры уменьшите до разумных :)
Неплохо сразу же добавить проект для тестирования (названия на Ваше усмотрения).
С помощью ActiveX Control Interface Wizard создадим "болванку", включив всего одно собственное свойство - Caption. Данное свойство будет несколько отличаться от аналогичных у других контролов тем, что оно не будет непосредственно выводить свое значение, а будет являться как бы его хранилищем. (Тип, естественно, String; значение по умолчанию -
"SelTextEx").
Теперь самое время заняться пользовательским типом данных. Для того, чтобы мы могли увидеть его в тестировочной программе, его необходимо объявить как Public. Давайте рассмотрим, с помощью чего мы можем выделить участки текста: ну во-первых с помощью изменения шрифта и во вторых с помощью цвета. Кроме того, необходимо указать с какого знака начинается каждое следующее выделение. В итоге мы получим следующий пользовательский тип данных:
Public Type SText
Start As Integer
Font As StdFont
ForeColor As OLE_COLOR
End Type
Все основные "интриги" начинаются с метода, руководящего печатью разноцветной и разнофонтовой надписи на нашем контроле. Назовем его SelectedPart. И в качестве передаваемого параметра используем динамический массив пользовательского типа
Public Sub SelectPart(CountEx() As SText)
Для начала очистим контрол от предыдущей графики методом
Cls.
Cls
А затем в цикле For ... Next произведем все необходимые нам настройки шрифта для каждой из объявленных нами закрашиваемых частей
For i = LBound(CountEx) To UBound(CountEx)
Здесь мы должны будем высчитать с какой частью слова будем работать и сбросить ее во внутреннюю переменную. Однако перед этим, сделаем "маленькое значение по умолчанию", т.е. если мы прямо не укажем значение Start, то будем считать, что это начало слова, следовательно оно будет равно единице. Длину же выделяемой части будем высчитывать из стартовой позиции следующей выделяемой части. Однако, если эта часть последняя, то присвоим ей длину всего слова (свойства Caption), тем самым страхуя себя на случай, если выделений не будет вовсе.
If CountEx(i).Start = 0 Then CountEx(i).Start = 1
If i = UBound(CountEx) Then
txt = Mid(m_Caption, CountEx(i).Start, Len(m_Caption))
Else
txt = Mid(m_Caption, CountEx(i).Start, CountEx(i + 1).Start - CountEx(i).Start)
End If
Далее займемся шрифтом. Здесь также необходимо сделать проверку на значение по умолчанию. И если мы явно не объявили шрифт, то присвоим его текущим значениям UserControl'а. Ну а дальше инициализируем объект
Font.
If CountEx(i).Font Is Nothing Then
Set CountEx(i).Font = UserControl.Font
End If
Set Font = CountEx(i).Font
NB! Мы можем так же присвоить значение по умолчанию свойству Font, того контейнера, где лежит наш контрол. В этом случае это будет выглядеть так:
Set CountEx(i).Font = Ambient.Font
NB! Работая с Font, мы объявляем его с ключевым словом Set, т.к. он является объектом.
Последнее из оставшегося в типе это ForeColor. Значения по умолчанию ему устанавливать не будем, т.к. если мы явно его не укажем, ему будет присвоено значение 0, что всегда соответствует черному цвету.
ForeColor = CountEx(i).ForeColor
Ну а теперь непосредственно печатаем, обработанную нами часть слова
Print txt;
NB! Точка с запятой в конце позволяют "дописывать" в конец напечатанному следующие части слова, а не переносят печать на новую строку.
Теперь самое время заняться тестированием. Добавьте новый проект (если Вы этого еще не сделали) и разместите на форме наш контрол и 2 кнопки. Первая будет выводить текст, с предварительной инициализацией всех свойств "по полной программе", а вторая - используя только самые необходимые их изменения. При инициализации Font не забудьте использовать ключевое слово
Set.
Загрузить пример к статье.