makarevich mfc activex

105
Макаревич Л. Г. ActiveX с нуля Практическое руководство Для студентов 3-го курса факультета АВТФ направления 542200 Информатика и вычислительная техника

Upload: venera7

Post on 12-Mar-2015

425 views

Category:

Documents


5 download

TRANSCRIPT

Page 1: MAKAREVICH mfc ActiveX

Макаревич Л. Г.

ActiveX с нуля Практическое руководство

Для студентов 3-го курса факультета АВТФ направления 542200 Информатика и вычислительная техника

Page 2: MAKAREVICH mfc ActiveX

Введение Эта работа предназначена для тех студентов, кто умеет писать приложения еа Visual C++ с использованием MFC, сталкивался с элементами ActiveX и хочет создавать свои элемент ActiveX. Вначале Вы познакомитесь с общими принципами COM - моделей (Component Object Model) и поймете то место, которое занимают ActiveX в этой модели, затем Вы практически построите вместе с автором простой элемент ActiveX c использованием MFC, постепенно усложняя его, примените все основные приемы создания элементов ActiveX ( в приложении 1 приводятся исходные тексты файлов построенного ActiveX ). Далее рассматриваются созданные с помощью MFC классы, и описывается их содержимое. В работе также приводится материал, обобщающий все приемы построения ActiveX c помощью MFC. После выполнения практических действий по созданию элемента ActiveX Вы получите навыки в построении элементов с помощью MFC в среде Visual C++ 5.0, 6.0. ActiveX – это набор технологий, которые позволяют программным компонентам взаимодействовать друг с другом по сети или на локальной машине вне зависимости от того, на каком языке они написаны. Строятся ActiveX на основе COM – модели. COM (Component Object Model) – модель многокомпонентных объектов, определяет и реализует механизм, который позволяет программным компонентам взаимодействовать с объектами. Программный объект представляет собой набор информации и методов, позволяющих получить доступ к ней. COM – компонент позволяет получить доступ к информации исключительно через наборы методов, образующих интерфейсы. Объект, называемый сервером, организует доступ к COM – объекту, реализуя один или несколько интерфейсов. Пользователь COM – объекта ( клиент ) получает доступ к объекту через указатели на эти интерфейсы. Клиент может иметь свободный доступ к объекту вне зависимости от языка реализации объекта. Объект будет вести себя в соответствии с его интерфейсами, даже если он выполняется в другом процессе или на другой машине, на другой операционной системе, написан на любом языке программирования или у него изменилась версия и он более новый или старый, чем тот, который вызывается клиентом. Определяя интерфейсы как способ связи между объектами и их клиентами, COM эффективно решает проблему версии. При создании новой версии элемента Вы просто добавляете новый интерфейс к объекту, оставляя старые без изменения. Поэтому клиенты, использующие старые интерфейсы, будут спокойно работать с более новыми объектами, не вызывая новых интерфейсов. COM – это платформо-независимая, распределенная, объектно-ориентированная система для создания двоичных программных компонентов, которые могут взаимодействовать между собой. Так как COM – компоненты являются независимыми от языка, то они могут взаимодействовать с любыми программами, реализованными на других языках. Кроме того, они могут выполняться в любом адресном пространстве: как в том, где запущен клиент, так и в другом процессе на той же машине или даже на другой машине.

Page 3: MAKAREVICH mfc ActiveX

COM – это основа для построения составных документов (OLE), ActiveX и т.д. (для углубленного теоретического изучения советуем обратиться к литературе, приводимой в конце работы). Для того, чтобы понять COM, надо иметь в виду, что это не объектно-ориентированный язык, а двоичный стандарт, определяющий, как COM-объекты взаимодействуют с другими объектами. Язык для реализации COM – объектов должен поддерживать указатели и вызывать функции через указатели. COM определяет природу COM-объекта. COM- объект – это набор данных, доступ к которым осуществляется исключительно через набор методов, образующих интерфейсы. COM определяет базовые интерфейсы, которые обеспечивают методы, общие для всех COM – технологий. Кроме того, COM обеспечивает набор API-функций, которые требуются для всех компонентов, а также позволяют компонентам взаимодействовать через сеть и обеспечивает защиту программных систем. COM – компонент поддерживает двоичный стандарт, поэтому он может быть реализован с помощью любого языка программирования. Объектно-ориентированные языки идеально подходят для создания COM-элементов.

Контрольные элементы ActiveX ActiveX строится на основе COM. ActiveX используются как строительные блоки при создании пользовательского интерфейса. Сам элемент всегда реализуется внутри сервеpa. Сервер является динамически подключаемой библиотекой (DLL), подгружаемой во время работы приложения, и находится в файле с расширением .OCX. Итак, ActiveX – это OLE-сервер, который может быть использован в любом OLE - контейнере. Заметим, что использование ActiveX возможно только внутри процесса OLE – контейнера, поскольку элементы ActiveX представляют собой обычные библиотеки DLL и выполняются в рамках клиентского процесса, который их использует. Правда, сейчас почти все приложения реализуют свойства OLE – контейнера. При этом контрольные элементы должны обеспечивать следующую функциональность: Свойства и методы

Контрольный элемент обеспечивает часть своих возможностей через набор интерфейсов, обеспечивающих свойства и методы. Контейнер может передавать свои свойства в контрольный элемент.

События

Контрольный элемент также может использовать интерфейсы для того, чтобы информировать клиента о событиях, связанных с элементом. Клиент может реагировать на эти события.

Визуальное представление

Контрольный элемент может поддерживать позиционирование и размещение в контейнере. Контейнер позиционирует контрольный элемент и определяет его размер.

Page 4: MAKAREVICH mfc ActiveX

Управление клавиатурой

Контрольный элемент может реагировать на клавиатурные комбинации, так что он может обрабатывать ввод от пользователя. Контейнер должен управлять клавиатурным вводом для всех встроенных контрольных элементов.

Сохранение состояния

Контрольный элемент может сохранять свое состояние. Клиент управляет сохранением состояния встроенных элементов.

Регистрация и лицензирование.

Контрольный элемент обычно поддерживает саморегистрацию на машине, где он запускается, и прописывает необходимую информацию в реестр. Контрольный элемент может быть пролицензирован для того, чтобы запретить неразрешенное использование.

Некоторые из указанных черт касаются и элементов ActiveX, и контейнеров. Построение элементов ActiveX Создание элементов ActiveX до недавнего времени считали очень сложной задачей, посильной только для специалистов высокого класса. Но использование Visual C++ позволяет легко создать элементы ActiveX c применением библиотеки MFC или библиотеки шаблонов ATL (Active Template Library ).

Использование MFC Ниже описывается создание элементов ActiveX с использованием MFC. Элементы ActiveX создаются в проектах специального типа. Каркас приложения подготавливается при помощи инструментального средства MFC ActiveX ControlWizard, а затем в него добавляется код, определяющий специфику элемента c применением средства ClassWizard. Перечислим основные моменты создания ActiveX-элемента управления:

• создание остова проекта; • создания графического образа инструментальной кнопки, задающей

элемент управления; • добавление новых свойств; • добавление новых методов; • добавление событий; • корректировка базовой страницы свойств и добавление новых страниц; • связывание данных.

Когда Вы используете MFC ActiveX Control Wizard для создания своего ActiveX, Вы получаете стартовое приложение с встроенной функциональностью, с базовыми чертами ActiveX. Стартовая программа включает исходные CPP и H – файлы, а также все файлы, которые Вы можете выбрать по желанию (файлы для проверки лицензии, файлы помощи и т.д.).

Page 5: MAKAREVICH mfc ActiveX

Постановка задачи Начнем строить простой элемент ActiveX, постепенно его усложняя. Пусть наш элемент будет отображать или эллипс, или прямоугольник, или треугольник. Фигура, отображаемая на рисунке, будет определена как свойство элемента ActiveX. На рис 1. показан элемент управления в работе. Назовем наш элемент MyCtrl. Выполнив построение нашего контрольного элемента, протестируем его, построим приложение с использованием его, а затем более глубоко посмотрим на сгенерируемые тексты и попробуем разобраться в них.

Рис. 1. Пример элемента управления вдействии

Построение остова для элемента MyCtrl Для создания элемента ActiveX воспользуемся Мастером построения приложений – ControlWizard. С его помощью создается проект с базовым набором классов и ресурсов. Такой базовый элемент отображается в виде эллипса. Никакие свойства, события, методы для взаимодействия с родительским окном не включаются в базовый проект. Для того, чтобы создать такой базовый элемент, выполните следующие шаги:

Page 6: MAKAREVICH mfc ActiveX

Рис. 2. Создание элемента управления MyCtrl.

1. В меню File выберите пункт New. 2. В появившемся диалоговом окне New выберите закладку Projects (рис 2.). 3. Отметьте, что Вы хотите построить MFC ActiveX ControlWizard. 4. Введите имя проекта, для нашего элемента - MyCtrl. 5. Укажите путь для расположения файлов проекта. 6. После щелчка на кнопке OK появится первое окно мастера MFC ActiveX

ControlWizard (рис.3), в котором можно указать число элементов в проекте ( один проект может содержать несколько элементов управления ), требуется ли поддержка лицензирования и нужны ли комментарии и поддержка файла справки.

7. Во втором окне мастера ( рис. 4) можно изменять имена классов, но обычно это не делается.

Рис. 3. Первое окно мастера MFC ActiveXControlWizard

Рис. 4. Второе окно MFC ActiveX ControlWizard.

Page 7: MAKAREVICH mfc ActiveX

Для нашего примера использованы установки, предлагаемые по умолчанию. После нажатия на кнопку Finish проект базового элемента будет создан. Давайте посмотрим, какие файлы-заготовки будут созданы. Класс Файлы Комментарии CMyCtrlApp MyCtrl.h

MyCtrl.cpp Реализует главную DLL-библиотеку. Обычно, этот код не меняется. Класс CmyCtrlApp является производным от класса COleControlModule.

CMyCtrlCtrl MyCtrlCtrl.h MyCtrlCtrl.cpp

Реализует основные функции элемента. Эти файлы модифицируются для изменения поведения элемента. Класс CmyCtrlCtrl является производным от класса COleControl.

CMyCtrlPropPage MyCtrlPpg.h MyCtrlPpg.cpp

Обеспечивает шаблон для построения страницы свойств элемента. Этот код изменяется для указания свойств элемента в странице свойств. Класс CMyCtrlPropPage производный от класса ColePropertyPage.

ControlWizard создает и некоторые другие файлы, которые также будут модифицироваться в дальнейшем. Файл Комментарий MyCtrl.odl Этот текстовый файл содержит информацию о контрольном

элементе. Когда Вы добавляете свойства, события и методы в контрольный элемент, содержимое файла меняется. Этот файл используется при создании библиотеки MyCtrl.tlb, который добавляется в исполняемый код как ресурс.

MyCtrl.rc Стандартный ресурсный файл. Содержит шаблон для страницы свойств.

MyCtrl.bmp Рисунок, представляющий элемент. Обычно изменяется пользователем.

MyCtrl.ico Иконка, которая появляется в диалоге About. ClassWizard создает также стандартные файлы: MyCtrl.clw, MyCtrl.def, MyCtrl.dsp, MyCtrl.dsw, Resource.h, Stdafx.h, Stdafx.cpp.

Добавление кода в файлы, созданные мастером. Стартовая программа включает все файлы, необходимые для построения контрольного элемента. Эти файлы совместимы с Class Wizard, и Вы можете использовать его для определения событий, свойств и методов, некоторые из которых уже реализованы в MFC. Ваш проект уже включает реализованную функциональность для элемента: это и метод для отрисовки элемента, и сериализацию, и карты для определения событий, изменить и расширить их Вы сможете, используя Class Wizard. При вставке своего кода обращайте внимание на комментарии, вставленные мастером. Они поясняют, куда надо вставлять код, например, CMyCtrlCtrl::CMyCtrlCtrl() {

Page 8: MAKAREVICH mfc ActiveX

InitializeIIDs(&IID_DMyCtrl, &IID_DMyCtrlEvents); // TODO: Initialize your control's instance data here. // Добавьте инициализацию Вашего элемента здесь. } CMyCtrlCtrl::~CMyCtrlCtrl() { // TODO: Cleanup your control's instance data here. } Обратите внимание, что базовый класс для нашего контрольного элемента – класс СOleControl.

Изменение растрового изображения элемента управления. При создании кода элемента управления ClassWizard создает изображение элемента управления в палитре, помещая его в файл MyCtrl.bmp. По умолчанию это изображение имеет вид, представленный на рис. 5. Для изменения этого изображения Вы можете использовать редактор битмар-изображений.

1. Откройте вкладку Resource.

Рис. 5. Изображение элемента управленияпо умолчанию

2. Откройте папку Bitmap. 3. Дважды щелкните мышкой на IDB_MYCTRL для запуска редактора bitmap. 4. Измените картинку, как Вам нравится (рис. 6). 5. В меню File выберите Save для сохранения изображения.

Page 9: MAKAREVICH mfc ActiveX

Рис. 6. Растровое изображение элемента управления.

6. Закройте редактор ресурсов.

Изменение диалога “About…” для элемента управления. ClassWizard создает шаблон для диалога “About…”. Он добавляет также и метод AboutBox, который может быть вызван из контейнера, куда вставляется элемент управления. В качестве иконки в диалоге создается иконка по умолчанию. Для модификации диалога Вы можете выполнить следующие действия: 1. Во вкладке Resource откройте папку Icon. 2. Дважды щелкните мышью на IDI_ABOUTDL и измените иконку по Вашему

желанию.

Page 10: MAKAREVICH mfc ActiveX

3. Во вкладке Resource откройте папку Dialog и измените диалог с

IDD_ABOUTBOX_MYCTRL по Вашему желанию (рис. 7).

Рис. 7. Изменение диалога "About..."

Добавление свойств пользователя к элементу управления. К элементу ActiveX можно добавить свойства двух видов: базовые и пользовательские. Базовые свойства уже предусмотрены в базовом классе COleControl. Добавим к элементу управления два пользовательских свойства: Shape – вид отображаемой в элементе фигуры, Selected – определяет состояние элемента ( включен/выключен). Для добавления свойства Shape необходимо выполнить следующие действия: 1. Выберите в меню View пункт ClassWizard. 2. Выберите в открывшемся диалоге закладку Automation. 3. В выпадающем списке Class Name выберите класс CmyCtrlCtrl (рис. 8). 4. Щелкните кнопку Add Property. Появится диалог Add Property (рис. 9). 5. Введите имя свойства Shape в поле External Name, установите тип свойства

short, выберите переключатель Get/Set methods для того, чтобы это свойство было доступным для управления из контейнера.

Page 11: MAKAREVICH mfc ActiveX

6. Для добавления свойства Selected надо выполнить почти аналогичные действия. Отличия заключаются в том, что тип свойства – BOOL, и в том,

что сделаем это свойство недоступным для изменения извне, то есть уберем метод set. Для отмены метода set просто удалите имя функции set в поле Set function. Тогда свойство становится доступным только для чтения.

Рис. 8. Добавление свойств элемента

7. При выходе из окна мастера ClassWizard c помощью щелчков на кнопках OK генерируются три новые функции в классе элемента управления: short CMCtrlCtrl::GetShape(); void CMCtrlCtrl::SetShape(short nNewValue); BOOL CMCtrlCtrl::GetSelected();

Обратите внимание на изменение текста в файле CMyCtrlCtrl.cpp. В карте диспетчеризации (если Вы не знаете, что это такое, не пугайтесь – после общего знакомства с процессом создания элемента ActiveX мы рассмотрим тексты файлов более подробно) появились дополнительные строчки: BEGIN_DISPATCH_MAP(CMyCtrlCtrl, COleControl) //{{AFX_DISPATCH_MAP(CMyCtrlCtrl) DISP_PROPERTY_EX(CMyCtrlCtrl, "Shape", GetShape, SetShape, VT_I2) DISP_PROPERTY_EX(CMyCtrlCtrl, "Selected", GetSelected, SetNotSupported, VT_BOOL) //}}AFX_DISPATCH_MAP DISP_FUNCTION_ID(CMyCtrlCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE) END_DISPATCH_MAP() Каждое добавленное свойство приводит к появлению нового макроса DISP_PROPERTY_EX в карте диспетчеризации.

Page 12: MAKAREVICH mfc ActiveX

Для отражения состояния выбора и формы элемента управления необходимо к объявлению его класса вручную добавить переменные. В файл MyCtrlCtrl.h

добавьте объявления:

Рис. 9. Добавление свойства Shape.

BOOL m_bSelected; short m_nShape; Эти переменные надо инициализировать разумными значениями в конструкторе класса CMyCtrlCtrl: m_bSelected = FALSE; m_nShape = 0; А теперь изменим добавленные мастером функции Get/Set: short CMyCtrlCtrl::GetShape() { return m_nShape; } void CMyCtrlCtrl::SetShape(short nNewValue) { m_nShape = nNewValue; SetModifiedFlag(); InvalidateControl(); } BOOL CMyCtrlCtrl::GetSelected() { return m_bSelected; }

Устойчивость свойства. Для установки свойства элемента во время разработки к функции DoPropExchange необходимо добавить обращение к функции PX_.

Page 13: MAKAREVICH mfc ActiveX

Функция DoPropExchange используется для сериализации или инициализации значений свойств. Для каждого свойства, которое может инициализироваться или сохраняться после установки, должна вызываться соответствующая функция PX_. Существует множество функций PX_, соответствующих разным типам свойств. Имя функции Тип свойства PX_Blob( ) Данные большого двоичного объема. PX_Bool( ) Булевское значение. PX_Color( ) Значение цвета ( тип OLE_CONTROL). PX_Currency( ) Значение денежной единицы. PX_Double( ) Значение типа double.

PX_Font( ) Шрифт ( указатель на структуру FONTDESC).

PX_Float( ) Значение типа float.

PX_IUnknown( ) Объект с интерфейсом, производным от UNKNOWN.

PX_Long( ) Значение типа long. PX_Picture( ) Рисунок ( ссылка на CpictureHolder). PX_Short( ) Значение типа short. PX_String( ) Значение типа CString. PX_ULong( ) Значение типа unsigned long. PX_UShort( ) Значение типа unsigned short.

Для нашего элемента будем сохранять и устанавливать свойство Shape, поэтому добавим в функцию DoPropExchange следующую строку: PX_Short(pPX, _T("Shape"), m_nShape, 0);

Добавление событий. Элемент будет обрабатывать сообщения от мыши. Мы сейчас добавим событие, которое возникает при нажатии на левую кнопку мыши в области контрольного элемента и передается в контейнер. При этом свойство Select меняется на противоположное значение, а форма в элементе изменяет цвет заливки. События добавляются к элементу управления с помощью ClassWizard.

Page 14: MAKAREVICH mfc ActiveX

Чтобы добавить событие Select: 1. Щелкните на вкладке ActiveX Events в диалоге ClassWizard (рис. 10).

Рис. 10. Добавление событий ActiveX.

2. Щелкните на кнопке Add Event (рис. 11). 3. Введите в поле External name имя свойства Select. При этом мастер

устанавливает внутреннее имя (имя функции, вызывающей запуск события)

Рис. 11. Добавление события Select.

Page 15: MAKAREVICH mfc ActiveX

FireSelect. Событие должно иметь параметр. Укажем его в списке Parameter list: это IsSelected типа BOOL. Щелкните кноку OK.

4. Добавьте обработчик сообщения WM_LBUTTONDOWN, из которого будет запускаться событие. Для этого выберите вкладку Message Map(рис. 12).

В списке Messages выберите событие WM_LBUTTONDOWN и нажмите кнопку Add Function. 5. Внесите изменения в функцию OnLButtonDown: void CMyCtrlCtrl::OnLButtonDown(UINT nFlags, CPoint point) { COleControl::OnLButtonDown(nFlags, point); m_bSelected = !m_bSelected; InvalidateControl();

Рис. 12. Добавление обработчика событияWM LBUTTONDOWN

FireSelect(m_bSelected); }

Рисование элемента управления. Отрисовка элемента управления осуществляется в методе OnDraw. Параметры этого метода содержат указатель на контекст устройства, размеры прямоугольника, определяющего границы элемента и прямоугольник , в пределах которого необходимо выполнить перерисовку. В общем случае внешний вид элемента может зависеть от некоторых свойств элемента, в нашем случае – от свойств Shape и Select, связанных с переменными m_bSelected и m_nShape:

Page 16: MAKAREVICH mfc ActiveX

void CMyCtrlCtrl::OnDraw( CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid) { CPen pen; CBrush foreBrush, backBrush; CPoint points[3]; pdc->SaveDC(); pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); backBrush.CreateSolidBrush(TranslateColor(AmbientBackColor())); foreBrush.CreateSolidBrush(GetSelected() ? RGB(255, 0, 0) : RGB(0, 255, 0)); pdc->FillRect(rcBounds, &backBrush); pdc->SelectObject(&pen); pdc->SelectObject(&foreBrush); switch (m_nShape) { case 0: pdc->Ellipse(rcBounds); break; case 1: pdc->Rectangle(rcBounds); break; case 2: points[0].x = rcBounds.left; points[0].y = rcBounds.bottom - 1; points[1].x = (rcBounds.left + rcBounds.right - 1) / 2; points[1].y = rcBounds.top; points[2].x = rcBounds.right - 1; points[2].y = rcBounds.bottom - 1; pdc->Polygon(points, 3); break; } pdc->RestoreDC(-1); } Обратите внимание на использование функции AmbientBackColor. Это одна из функций, которые обеспечивают получение свойств окружения контейнера. В нашем случае фон элемента будет совпадать с фоном контейнера. Функция AmbientBackColor возвращает значение типа OLE_COLOR. Для его преобразования в RGB-значение используется функция TranslateColor, также определенная в классе COLEControl.

Добавление интерфейса окна свойств. Для управления свойствами элемента необходимо создать одну или более страниц свойств, с помощью которых можно устанавливать эти свойства. Эти

Page 17: MAKAREVICH mfc ActiveX

страницы свойств используются приложениями в режиме разработки( например, редактором диалоговых окон в Developer Studio). Для нашего элемента управления добавим один элемент в страницу свойств: выпадающий список для задания формы, рисуемой в элементе управления. 1. Откройте вкладку Resource. Откройте папку Dialog и щелкните мышью на IDD_PROPPAGE_MYCTRL, чтобы запустить редактор диалоговых окон. 2. Вставьте статический элемент для текста – Форма. 3. Вставьте элемент типа ComboBox, укажите для него стиль – Drop List ( это

важно, иначе переменная типа int не сможет быть присвоена этому элементу управления с помощью мастера ClassWizard). Задайте идентификатор IDC_SHAPE для этого элемента.

4. Введите значения в список ( Ellipse, Rectangle, Triangle ). При вводе значений в конце строки необходимо использовать комбинацию клавиш Ctrl+Enter.

Связывание страницы свойств со свойствами элемента управления. Переменная для новой страницы свойств может быть добавлена с помощью

ClassWizard, необычным является способ связывания этой переменной ( переменной класса CMyCtrlPropPage) cо свойством элемента управления MyCtrl.

Рис. 13. Связывание элемента управления со свойством спомощью ClassWizard

1. В диалоге ClassWizard щелкните на вкладке Member Variables. 2. Выберите в поле Class name класс CMyCtrlPropPage.

Page 18: MAKAREVICH mfc ActiveX

3. Щелкните на кнопку Add Variable. 4. Заполните поля в диалоге Add Member Variable. Обратите внимание на поле

Optional property name. В поле надо вставить имя свойства в элементе управления.

В результате мастер ClassWizard в дополнение к любым вызовам функций DDX_ и DDV_ вставляет в функцию DoDataExchange класса страницы свойств набор вызовов функций DDP_. В нашем случае это будет строка: DDP_CBIndex(pDX, IDC_CBSHAPE, m_nShape, _T("Shape") ); На рис. 14 и 15 приводится внешний вид вкладок для нашего элемента управления в разных контейнерах: в программе TestContainer и Developer

Studio.

Рис. 14. Страница свойств элементауправления MyCtrl в TestContainer

Рис. 15. Страница свойств элемента вDevStudio.

Создание файла типа MyCtrl.ocx для элемента MyCtrl. Построение элемента управления ActiveX выполняется также, как и всех программных модулей в DevStudio. При компиляции и последующей сборке программного модуля автоматически происходит регистрация элемента управления в системе.

Тестирование элемента управления. Чтобы протестировать элемент управления, можно использовать приложение ActiveX Control Test Container, входящее в состав DevStudio и поставляемое вместе с Visual C++. Для запуска этой программы можно использовать соответствующий пункт в меню Tools. Можно запустить эту же программу из главного меню системы (Пуск – Программы – Microsoft Visual C++ - Microsoft Visual C++ Tools - ActiveX Control Test Container. Более удобным способом отладки элементов управления является установка этого приложения, находящегося в файле tstcon32.exe, в качестве исполняемой программы отладки через команду Settings меню Project в DevStudio. В этом случае можно задавать точки останова и управлять выполнением программы элемента управления с помощью отладчика.

Page 19: MAKAREVICH mfc ActiveX

После запуска программы - ActiveX Control Test Container вставьте свой

контрольный элемент, используя команду Edit – Insert New Control (рис. 16).

Рис. 16. Вставка элемента MyCtrl в TestContainer.

Рис. 17. Тестирование элемента MyCtrl.

Затем Вы можете изменить указанное свойство в странице свойств – форму изображаемой фигуры. Для вызова страницы свойств в меню Edit Вы можете выбрать пункт Properties ( рис. 17 ). Проверить, как элемент реагирует на событие мыши Вы можете, щелкнув левой кнопкой мыши на элементе. Вы можете убедиться, что элемент меняет цвет отрисованной формы.

Распространение элемента управления. Для распространения элемента управления надо ответить на два вопроса. Первый связан с тем, какие файлы должны поставляться пользователю. Второй – как установить контрольный элемент, чтобы его можно было использовать. Пользователю контрольного элемента достаточно иметь только файл с расширением .ocx. На самом деле, этот файл является библиотекой dll, содержащей код элемента. Для установки элемента ActiveX его надо зарегистрировать. Регистрацию можно выполнить, используя программу regsrvr32.exe, поставляемую вместе с DevStudio и свободно распространяемую: regsrvr32.exe /s <имя элемента управления>.ocx Можно также в программе, использующей элемент управления, загрузить файл OCX как обычную DLL, а потом вызвать функцию DLLRegisterServer, описанную в файле приложения MyCtrl.cpp.

Page 20: MAKAREVICH mfc ActiveX

Если Вы используете лицензирование, то в число поставляемых разработчикам файлов должен быть включен файл с расширением .lic. Конечным пользователям этот файл не нужно поставлять.

Добавление новых свойств в элемент управления MyCtrl.

Добавление базового свойства. В тексте программы, приведенном выше, цвет фигуры, изображаемой в элементе в невыбранном состоянии – красный, а в выбранном состоянии – зеленый. Давайте позволим цвет фигуры в выбранном состоянии задавать как свойство, причем используем для установки этого свойства – базовое ForeColor. Для добавления базового свойства ForeColor выполните следующие действия. 1. В меню View выберите ClassWizard. 2. Выберите вкладку Automation. 3. Из списка Class name выберите CMyCtrlCtrl. 4. Щелкните на кнопке Add Property. 5. В диалоге Add Property из списка External name выберите ForeColor. 6. В разделе Implementation выберите Stock ( базовый). 7. Щелкните на кнопке OK. ClassWizard изменит файлы класса MyCtrlCtrl и MyCtrl.odl. Значение свойства ForeColor можно модифицировать из контейнера, используя методы GetForeColor и SetForeColor, входящие в класс COleControl. Значение свойства ForeColor поддерживается классом COleControl. Функция SetForeColor вызывает после установки значения свойства автоматически функцию OnForeColorChanged, которая, в свою очередь вызывает метод InvalidateControl, что ведет к перерисовке элемента управления.

Установка значения базового свойства по умолчанию. Класс COleControl реализует следующий механизм для получения значений по умолчанию для базовых свойств. В методе COleControl::OnResetState вызывается функция COleControl::DoPropExchange, которая запрашивает контейнер о некоторых его свойствах (ForeColor, BackColor и т. п.) и устанавливает значение этого свойства по умолчанию. Для получения значения текущего базового свойства можно воспользоваться методами класса COleControl, в нашем случае GetForeColor().

Добавление пользовательского свойства цвета. Для добавления свойства цвета фигуры в невыбранном состоянии выполните следующие действия. 1. Выберите в меню View пункт ClassWizard. 2. Выберите в открывшемся диалоге закладку Automation. 3. В выпадающем списке Class Name выберите класс CMyCtrlCtrl (рис. 8). 4. Щелкните кнопку Add Property. Появится диалог Add Property (рис. 9). 5. Введите имя свойства SelectColor в поле External Name, установите тип

свойства OLE_COLOR, выберите переключатель Get/Set methods для того, чтобы это свойство было доступным для управления из контейнера.

Page 21: MAKAREVICH mfc ActiveX

6. Добавьте переменную типа OLE_COLOR в файл MyCtrlCtrl.h. 7. Добавьте строку PX_Long(pPX, _T("SelectColor"), (long&) m_selectColor, RGB(255,0,0)); в функцию void CMyCtrlCtrl::DoPropExchange(CPropExchange* pPX): void CMyCtrlCtrl::DoPropExchange(CPropExchange* pPX) { ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor)); COleControl::DoPropExchange(pPX); PX_Short(pPX, _T("Shape"), m_nShape, 0); PX_Long(pPX, _T("SelectColor"), (long&) m_selectColor, RGB(255,0,0)); } 8. Внесите изменения в методы GetSelectColor() и

SetSelectColor(): OLE_COLOR CMyCtrlCtrl::GetSelectColor() { return m_selectColor; } void CMyCtrlCtrl::SetSelectColor(OLE_COLOR nNewValue) { m_selectColor = nNewValue; SetModifiedFlag(); InvalidateControl(); }

Изменение метода Draw. Внесенные изменения показаны жирным шрифтом. void CMyCtrlCtrl::OnDraw( CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid) { CPen pen; CBrush foreBrush, backBrush; CPoint points[3]; pdc->SaveDC(); pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); ULONG foreColor = TranslateColor(GetForeColor()); backBrush.CreateSolidBrush(TranslateColor(AmbientBackColor())); foreBrush.CreateSolidBrush(GetSelected() ? foreColor : TranslateColor(m_selectColor)); pdc->FillRect(rcBounds, &backBrush); pdc->SelectObject(&pen); pdc->SelectObject(&foreBrush); switch (m_nShape)

Page 22: MAKAREVICH mfc ActiveX

{ case 0: pdc->Ellipse(rcBounds); break; case 1: pdc->Rectangle(rcBounds); break; case 2: points[0].x = rcBounds.left; points[0].y = rcBounds.bottom - 1; points[1].x = (rcBounds.left + rcBounds.right - 1) / 2; points[1].y = rcBounds.top; points[2].x = rcBounds.right - 1; points[2].y = rcBounds.bottom - 1; pdc->Polygon(points, 3); break; } pdc->RestoreDC(-1); }

Добавление страницы базовых свойств. Для того, чтобы вставить дополнительные страницы базовых свойств, не надо создавать дополнительные классы. Вы должны вручную изменить раздел страниц свойств в файле MyCtrlCtrl.cpp. BEGIN_PROPPAGEIDS(CMyCtrlCtrl, 2) PROPPAGEID(CMyCtrlPropPage::guid) PROPPAGEID(CLSID_CColorPropPage) END_PROPPAGEIDS(CMyCtrlCtrl) Здесь CLSID_CcolorPropPage – идентификатор страницы для задания цвета. Обратите внимание на значение 2 в первой строке раздела – это количество страниц свойств. Откомпилируйте и постройте новый контрольный элемент и проверьте, как он работает с использованием ActiveX Control Test Container. Задайте свойства цвета в странице свойств. Проверьте, как меняется цвет фигуры при щелчке на ней левой кнопки мыши.

Добавление текста в элемент управления. Добавим базовое свойство Caption для вывода текстового сообщения в элемент управления, а также базовое свойство Font для задания шрифта для этого сообщения. 1. Выберите в меню View пункт ClassWizard. 2. Выберите в открывшемся диалоге закладку Automation. 3. В выпадающем списке Class Name выберите класс CMyCtrlCtrl (рис. 8). 4. Щелкните кнопку Add Property. Появится диалог Add Property (рис. 9). 5. Выберите имя свойства Caption в поле External Name, выберите

переключатель Stocks. 6. Щелкните кнопку Add Property. Появится диалог Add Property (рис. 9).

Page 23: MAKAREVICH mfc ActiveX

7. Выберите имя свойства Font в поле External Name, выберите переключатель Stocks.

ClassWizard создает код для добавления свойств, изменяя CMyCtrlCtrl класс и MyCtrl.odl файл. Карта диспетчеризации приобретает следующий вид: BEGIN_DISPATCH_MAP(CMyCtrlCtrl, COleControl) //{{AFX_DISPATCH_MAP(CMyCtrlCtrl) DISP_PROPERTY_EX(CMyCtrlCtrl, "Shape", GetShape, SetShape, VT_I2) DISP_PROPERTY_EX(CMyCtrlCtrl, "Selected", GetSelected, SetNotSupported, VT_BOOL) DISP_PROPERTY_EX(CMyCtrlCtrl, "SelectColor", GetSelectColor, SetSelectColor, VT_COLOR) DISP_STOCKPROP_FORECOLOR() DISP_STOCKPROP_CAPTION() DISP_STOCKPROP_FONT() //}}AFX_DISPATCH_MAP DISP_FUNCTION_ID(CMyCtrlCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE) END_DISPATCH_MAP() Макросы DISP_STOCKPROP_FORECOLOR(), DISP_STOCKPROP_CAPTION(), DISP_STOCKPROP_FONT() разрешают использование базовых свойств. Для доступа к ним можно использовать методы Get/Set, определенные в классе COleControl, а также методы OnTextChanged(), OnFontChanged(), OnForeColorChanged(), которые вызываются при выполнении метода Set. Все методы можно переопределить. Получить значение свойства Caption можно не только с помощью метода GetText(), но и используя метод InternalGetText. Последний рекомендуется использовать всегда, когда возвращаемый текст нельзя модифицировать, так как этот метод возвращает ссылку на константную строку: const CString& strCaption = InternalGetText();

Добавление страницы свойств для шрифта. Измените вручную раздел страниц свойств в файле MyCtrlCtrl. BEGIN_PROPPAGEIDS(CMyCtrlCtrl, 3) PROPPAGEID(CMyCtrlPropPage::guid) PROPPAGEID(CLSID_CColorPropPage) PROPPAGEID(CLSID_CFontPropPage) END_PROPPAGEIDS(CMyCtrlCtrl) Здесь CLSID_CСolorPropPage – идентификатор страницы для задания цвета, а CLSID_CFontPropPage - идентификатор страницы для задания шрифта. Обратите внимание на значение 3 в первой строке раздела – это количество страниц свойств.

Добавление элемента для задания текста. 1. Откройте вкладку Resource. 2. Выберите папку Dialog. Откройте диалог для основной страницы свойств

элемента управления. 3. Вставьте статический элемент с заголовком Текст. 4. Добавьте элемент редактирования, задав ему идентификатор IDC_CAPTION.

Page 24: MAKAREVICH mfc ActiveX

5. Свяжите контрольный элемент со свойством Сaption. Для этого в диалоге

ClassWizard в поле Class name выберите имя CMyPropPage, вкладку Member variables, в списке идентификаторов элементов выберите идентификатор IDC_CAPTION, щелкните на кнопке Add variable. Впишите имя переменной, выберите тип и категорию переменной и задайте имя свойства.

Рис. 18. Связь переменной сосвойством

В классе MyCtrlPpg добавлена автоматически переменная m_Caption, а также изменен текст метода DoDataExchange: void CMyCtrlPropPage::DoDataExchange(CDataExchange* pDX) { //{{AFX_DATA_MAP(CMyCtrlPropPage) DDP_CBIndex(pDX, IDC_CBSHAPE, m_nShape, _T("Shape") ); DDX_CBIndex(pDX, IDC_CBSHAPE, m_nShape); DDP_Text(pDX, IDC_CAPTION, m_Caption, _T("Caption") ); DDX_Text(pDX, IDC_CAPTION, m_Caption); //}}AFX_DATA_MAP DDP_PostProcessing(pDX); }

Изменение метода OnDraw. Для выдода тектового сообщения добавьте следующие строки в метод OnDraw(показано жирным шрифтом): void CMyCtrlCtrl::OnDraw( CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid) { CPen pen; CBrush foreBrush, backBrush; CPoint points[3]; pdc->SaveDC();

Page 25: MAKAREVICH mfc ActiveX

pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); ULONG foreColor = TranslateColor(GetForeColor()); backBrush.CreateSolidBrush(TranslateColor(AmbientBackColor())); foreBrush.CreateSolidBrush(GetSelected() ? foreColor : TranslateColor(m_selectColor)); pdc->FillRect(rcBounds, &backBrush); pdc->SelectObject(&pen); pdc->SelectObject(&foreBrush); switch (m_nShape) { case 0: pdc->Ellipse(rcBounds); break; case 1: pdc->Rectangle(rcBounds); break; case 2: points[0].x = rcBounds.left; points[0].y = rcBounds.bottom - 1; points[1].x = (rcBounds.left + rcBounds.right - 1) / 2; points[1].y = rcBounds.top; points[2].x = rcBounds.right - 1; points[2].y = rcBounds.bottom - 1; pdc->Polygon(points, 3); break; } CFont *pOldFont; TEXTMETRIC tm; //pdc->SetTextColor(TranslateColor(m_textColor)); //pdc->SetBkMode(TRANSPARENT); CString strCaption = GetText(); pOldFont = SelectStockFont(pdc); pdc->GetTextMetrics(&tm); pdc->SetTextAlign(TA_CENTER|TA_TOP); pdc->ExtTextOut((rcBounds.left + rcBounds.right)/2,(rcBounds.top + rcBounds.bottom - tm.tmHeight)/2, ETO_CLIPPED, rcBounds, strCaption, strCaption.GetLength(),NULL); pdc->RestoreDC(-1); } Если Вы хотите, добавьте как свойство (TextColor) цвет текста, добавьте переменную m_textColor, измените методы GetTextColor и setTextColor, добавьте строки в метод OnDraw, позволяющие рисовать текст указанного цвета ( в приведенном выше примере эти строки закомментарены).

Page 26: MAKAREVICH mfc ActiveX

Добавление элемента управления в приложение. Давайте попробуем использовать наш контрольный элемент в приложении. Создадим приложение, базирующееся на диалоге, используя Application Wizard. Назовем приложение TestMyCtrl. Отметим свойство приложения использовать элементы ActiveX. В этом случае приложение является контейнером. Во вкладке Resource View выберем папку Dialog и добавим в него наш элемент управления.

Рис. 19. Вставка в проект элементауправления

Для этого выполним следующие действия: 1. В меню Project выберем пункт Add To Project, а затем пункт Components and

Controls. 2. В открывшемся диалоге выберем папку Registered ActiveX Controls, найдем

в ней наш элемент управления (рис. 19). 3. Щелкните на кнопке Insert. В появившемся диалоге (рис.20) Вам будет

предложено вставить в проект класс и файлы из перечисленных в списке. Вставьте эти файлы для того, чтобы посмотреть, как они выглядят и какие методы для доступа к ним Вы можете использовать. В принципе Вы можете не добавлять эти классы в проект.

Page 27: MAKAREVICH mfc ActiveX

4. Обратите внимание на появление в панели Controls значка – картинки нашего элемента. Вставьте элемент в диалог, щелкните правой кнопкой

мыши на элементе и выберите пункт Properties. Установите свойства элемента. Задайте идентификатор элемента – IDC_MYCTRL.

Рис. 20. Добавление классов впроект

5. Установите свойства элемента ActiveX. Задайте форму, текст, цвета.

Рис. 21. Приложение с элементом управления.

6. Постройте приложение и запустите его на выполнение (рис. 21 ).

Добавление взаимодействия элемента управления и контейнера. Посмотрите внимательно файлы, созданные мастером: MyСtrl.h b MyСtrl.cpp.

Page 28: MAKAREVICH mfc ActiveX

В файле MyCtrl.h приведены методы, которые Вы можете использовать для управления элементом ActiveX (эти методы выделены жирным шрифтом): class CMyCtrl : public CWnd { protected: DECLARE_DYNCREATE(CMyCtrl) public: CLSID const& GetClsid() { static CLSID const clsid = { 0x49e2f37f, 0xb48d, 0x11d3, { 0xa2, 0xb0, 0x0, 0xa0, 0xc9, 0x55, 0xfa, 0x9e } }; return clsid; } virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL) { return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID); } BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CFile* pPersist = NULL, BOOL bStorage = FALSE, BSTR bstrLicKey = NULL) { return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID, pPersist, bStorage, bstrLicKey); } // Attributes public: short GetShape(); void SetShape(short); short Get_Shape(); void Set_Shape(short); BOOL GetSelected(); void SetSelected(BOOL); OLE_COLOR GetForeColor(); void SetForeColor(OLE_COLOR); unsigned long GetSelectColor(); void SetSelectColor(unsigned long); CString GetCaption(); void SetCaption(LPCTSTR); COleFont GetFont(); void SetFont(LPDISPATCH); unsigned long GetTextColor(); void SetTextColor(unsigned long);

Page 29: MAKAREVICH mfc ActiveX

// Operations public: void AboutBox(); }; Добавьте в диалог кнопки, позволяющие менять свойства элемента. В примере добавлены 4 элемента. Элемент – кнопка “Сменить форму”с идентификатором IDC_SHAPE позволит менять форму, отображаемую в элементе. Элемент редактирования с идентификатором IDC_NEWTEXT позволит указать текст, выводимый в элементе ActiveX. Кнопка “Сменить текст” с идентификатором IDC_TEXT позволит при нажатии на нее изменить текст в элементе ActiveX. Элемент типа статического текста с идентификатором IDC_MESSAGE будет отображать текст, изменяющийся при щелчках мыши на элементе. Запустите ClassWizard и на закладке Member Variables задайте переменные для элементов с иденгтификаторами IDC_MESSAGE, IDC_NEWTEXT, IDC_MYCTRL. На закладке Message Map выберете класс СtestMyCtrlDlg. Задайте обработку событий, связанных с щелчками на кнопках IDC_TEXT и IDC_SHAPE. Если Вы выберете в списке Object Ids элемент IDC_MYCTRL, то в списке событий Вы увидите то единственное событие, которое мы посылаем в контейнер – Select. Добавьте обработчик для него – функцию с именем OnSelectMyCtrl. Измените текст в файле TestMyCtrl.cpp: void CTestMyCtrlDlg::OnSelectMyctrl(BOOL IsSelected) { if ( IsSelected ) m_Message = "Выбран"; else m_Message = "Не выбран"; UpdateData(FALSE); } void CTestMyCtrlDlg::OnShape() { short shape = m_MyCtrl.Get_Shape(); if ( shape < 2 ) m_MyCtrl.Set_Shape(++shape); else m_MyCtrl.Set_Shape(0); } void CTestMyCtrlDlg::OnText() { UpdateData(TRUE); m_MyCtrl.SetCaption(m_newText); } Обратите внимание на использование методов элемента ActiveX: вызовы методов выделены жирным шрифтом. Перестройте приложение и запустите его (рис. 22).

Page 30: MAKAREVICH mfc ActiveX

Рис. 22. Приложение с элементом ActiveX.

Добавление новых свойств, методов и событий в элемент ActiveX MyCtrl. Давайте усложним наш элемент ActiveX. Добавим таймер в элемент, который будет включен при загрузке элемента, сможет отключаться при управлении со стороны контейнера, интервал между включениями таймера будет изменяться при настройке элемента через страницу свойств. При срабатывании таймера пусть в элементе отображается счетчик. При этом в контейнер будут передаваться события – нотификационные сообщения. Добавление пользовательского свойства Sleep – задержки между срабатываниями таймера не должно вызвать у Вас затруднений. Пусть задание значения этого свойства выполняется через методы Set/Get, тип свойства – long. В классе CmyCtrlCtrl добавьте переменную типа long m_timeSleep, измените методы Get/Set, как показано ниже: long CMyCtrlCtrl::GetSleep() { return m_timeSleep; } void CMyCtrlCtrl::SetSleep(long nNewValue) { m_timeSleep = nNewValue; SetModifiedFlag(); } Добавьте в функцию CMyCtrlCtrl::DoPropExchange следующую строку, обеспечивающую сохранение свойства Sleep: PX_Long(pPX, _T("Sleep"), m_timeSleep, 1000); Добавьте в страницу свойств элемента элемент типа редактирования для задания численного значения m_timeSleep и свяжите его со свойством Sleep.

Page 31: MAKAREVICH mfc ActiveX

Для добавления таймера включите в класс CMyCtrlCtrl переменные UINT idTimer; // для идентификатора таймера BOOL timerOn; // для определения состояния таймера – включен.отключен. Установку таймера нужно добавить не в конструкторе, а в методе по обработке сообщения WM_CREATE. Для обработки этого сообщения используйте ClassWizard: #define ID_TIMER 1001 //идентификатор таймера int CMyCtrlCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (COleControl::OnCreate(lpCreateStruct) == -1) return -1; idTimer = SetTimer( ID_TIMER, m_timeSleep, NULL ); timerOn = FALSE; return 0; } Удаление таймера можно выполнить при обработке сообщения WM_CLOSE. Добавьте обработчик этого сообщения с помощью ClassWizard: void CMyCtrlCtrl::OnClose() { if ( timerOn ) { KillTimer( idTimer ); timerOn =FALSE; } CWnd::OnClose(); } Используя ClassWizard, добавьте обработчик события WM_TIMER в класс CMyCtrlCtrl. void CMyCtrlCtrl::OnTimer(UINT nIDEvent) { m_tick++; SetModifiedFlag(); InvalidateControl(); COleControl::OnTimer(nIDEvent); } Здесь m_tick – переменная, добавленная в класс CMyCtrlCtrl для подсчета тиков таймера: ULONG m_tick; Метод просто изменяет значение переменной m_tick и вызывает перерисовку элемента. Для вывода значения m_tick измените метод отрисовки элемента OnDraw - добавьте в конец его строки:

Page 32: MAKAREVICH mfc ActiveX

CString strTick; strTick.Format("%d",m_tick); pdc->ExtTextOut((rcBounds.left + rcBounds.right)/2,(rcBounds.top + tm.tmHeight), ETO_CLIPPED, rcBounds, strTick, strTick.GetLength(),NULL); Эти строки выведут значение m-tick в верхней строке элемента.

Добавление методов в элемент ActiveX. Методы добавлябтся в элемент ActiveX для того, чтобы контейнер мог вызвать их их для управления элементом. Методы делятся на две группы: базовые ( их только два – Refresh и DoClick) и пользовательские. Базовые методы реализованы в классе COleControl (об этих методах мы поговорим позже). Мы добавим метод, с помощью которого сможем из контейнера отключать таймер. Для добавления этого метода вызовем ClassWizard, выберем закладку Automation, нажмем кнопку Add Method, введем имя метода ( оно может различаться для внутреннего и внешнего употребления, но оставим имя одинаковым – DoChangeTimer. Тип возврата – BOOL,

параметров нет (рис. 23).

Рис. 23. Добавление пользовательского метода

Измените метод DoChangeTimer: BOOL CMyCtrlCtrl::DoChangeTimer() { if ( timerOn ) { KillTimer( idTimer ); timerOn =FALSE;

Page 33: MAKAREVICH mfc ActiveX

} else { idTimer = SetTimer( ID_TIMER, m_timeSleep, NULL ); timerOn =TRUE; } return timerOn; } Как Вы можете видеть, при вызове этого метода таймер отключается, если он был включен, и включается, если он был выключен.

Добавление события, посылаемого в контейнер. События посылаются контрольными элементами в контейнер для того, чтобы оповестить о каких-то изменениях в них. MFC поддерживает два вида событий: базовые (stock) и пользовательские (custom). Базовые события реализованы в базовом классе COleControl автоматически (об этих свойствах поговорим позже). Пользовательские позволяют реализовать сообщения в контейнер, специфические для элемента. Добавим пользовательское свойство, оповещающее контейнер о срабатывании таймера. Для этого запустим ClassWizard, выберем вкладку ActiveX Events, нажмем на кнопке Add Event, введем внешнее и внутреннее имена Tick и FireTick соответственно, укажем, что тип возврата – void, а тип входного параметра – long – значение счетчика тиков ( рис. 24).

Рис. 24. Добавление пользовательского

Page 34: MAKAREVICH mfc ActiveX

Тестирование элемента MyCtrl в ActiveX Control Test Container. Попробуйте загрузить новый элемент в ActiveX Control Test Container. Измените свойства элемента. Отметьте появление событий, посылаемых в контейнер: по таймеру посылается событие Tick c параметром ltick. При нажатии на элементе левой кнопки мыши появляется событие Select c параметром IsSelected. Эти события отображаются в нижней части окна ActiveX Control Test Container. Для проверки выполнения метода пользователя выберете в меню Control пункт Invoke Methods…, отметьте в списке Methods Name метод с именем DoChangeTimer, нажмите кнопку Invoke. Отметьте, что события Tick перестали появляться, и счетчик тиков перестал изменяться. Можете еще раз вызвать метод DoChangeTimer…(рис. 25)

Рис. 25. Тестирование элемента ActiveX.

Изменение приложения с использованием элемента ActiveX. Вернемся к нашему приложению TestMyCtrl. Добавим в диалог кнопку с идентификатором IDC_TIMER с названием “Переключить таймер”. Используя Class Wizard, добавим обработку события, связанного с этим элементом:

void CTestMyCtrlDlg::OnTimer() { m_MyCtrl.DoChangeTimer(); } Добавим обработку события Tick, получаемого от элемента ( в ClassWizard при выборе закладки Message Map для класса CtestMyCtrlDlg и элемента IDC_MYCTRL Вы должны увидеть оба пользовательских события – Select и

Page 35: MAKAREVICH mfc ActiveX

Tick. Добавьте в диалог элемент статичекого типа для вывода строки текста и установите для этого элемента переменную m_Tick типа long. Для обработки события Tick добавьте метод:

void CTestMyCtrlDlg::OnTickMyctrl(long ltick) { m_Tick.Format("%d", ltick); UpdateData(FALSE); }

Здесь просто выводится в диалоге тоже значение, что и в элементе MyCtrl (рис. 26 )

Для замены элемента рекомендуется удалить его из диалога, добавить в

проект новый элемент, зарегистрированный последним, заменить файлы, вставленные в проект, так как добавились новые методы и события, снова добавить элемент в диалог, указать тот же идентификатор – IDC_MYCTRL, задать свойства.

Рис. 26. Элемент MyCtrl в приложении.

Все файлы, полученные в результате разработки элемента и приложения, приведены в приложении 1. Эти файлы нам понадобятся позднее при более внимательном разборе классов, генерируемых ClassWizard. Общие принципы построения ActiveX с помощью MFC Итак, Вы создали свой первый ActiveX-элемент, встроили его в приложение и, надеюсь, поняли, что построение элемента ActiveX с помощью MFC не очень трудная задача, а использование элементов – совсем просто и удобно. Давайте рассмотрим построение элементов ActiveX с помощью MFC в общих чертах.

Page 36: MAKAREVICH mfc ActiveX

Построение остова приложения Процесс создания ActiveX с помощью MFC ActiveX Control Wizard состоит из двух шагов. На шаге 1 Вы должны: 1. Выбрать число элементов ActiveX, которые будут входить в Ваш проект. Вы

можете установить до 99 элементов. Для каждого элемента будет создан отдельный класс и отдельный класс будет создан для каждой страницы свойств. Если Вы хотите создать несколько элементов ActiveX, и они часто используются вместе, то рекомендуется включать все элементы в один файл, так как загрузка одного файла будет выполняться быстрее, чем нескольких.

2. Указать, нужна ли лицензия для Ваших контрольных элементов. Если Вы решите добавить поддержку лицензирования, мастер создаст файл LIC по умолчанию и добавит функции, поддерживающие проверку лицензионной информации. Без соответствующего файла лицензии (LIC) невозможно будет использовать элемент управления в режиме конструирования. Если Вы поставляете элемент с этим файлом, то пользователи сумеют выполнять разработку приложения с помощью данного элемента.

3. Выбрать, нужны ли Вам комментарии в исходных файлах, которые помогут Вам при добавлении кода.

4. Указать, хотите ли добавить файлы помощи для получения контекстно-зависимой подсказки.

На втором шаге Вы можете: 1. Если Вы строите в проекте более одного элемента, то отредактировать

имена классов и файлов. 2. Выбрать, какие черты будет иметь каждый элемент. Обратите внимание на

опцию Invisible at runtime (Невидим во время выполнения ), которая позволяет создавать элементы, которые не имеют видимого интерфейса во время выполнения. Опция Available in “Insert Object Dialog” (Доступно для “Вставки объекта в диалог“) позволяет создать элемент, который может вставляться в документы контейнера.

3. Выбрать класс окна для каждого элемента. Можно также указать базовый класс для нового элемента управления, например, класс BUTTON для создания элемента управления, наследующего поведение кнопки.

4. Нажав кнопку Advanced, добавить активность безоконного элемента, оптимизацию отрисовки и асинхронную загрузку свойств для каждого элемента.

Если Вы нажмете на кнопку Advanced, то появится окно, показанное на рис. 27. В таблице 1 приведено краткое описание каждого из расширенных свойств

Рис. 27. Расширенные черты элемента ActiveX.

Page 37: MAKAREVICH mfc ActiveX

элемента.

Таблица 1. Расширенные черты ActiveX

Расширенная черта ActiveX Описание Windowless activation – Активация без окна

Позволяет элементу активироваться без создания окна

Unclipped device context – Ограниченная клиентская зона

Используется для контрольных элементов, которые гарантированно не рисуют вне своей клиентской зоны. Не используется вместе с предыдущим свойством.

Flicker-free activation – Активация, свободная от мерцания

Используется, если контрольный элемент отображается одинаково в активном и неактивном состояниях. Не используется вместе со свойством Windowless activation.

Mouse pointer notifications when inactive – Сообщения от мыши, когда неактивен элемент

Разрешает элементу получать сообщения от мыши, когда элемент не активен.

Optimized drawing code – Отимизация отрисовки

Позволяет элементу отрисовывать себя в режиме оптимизации, если контейнер поддерживает это свойство.

Loads properties asynchronously – Асинхронная загрузка свойств

Асинхронные свойства – свойства, которые могуи загружаться в фоновом режиме, и они затрагивают передачу больших объемов информации.

5. Закончить процесс создания ActiveX.

Базовые компоненты ActiveX. Элементы ActiveX в MFC строятся на основе базового класса COleControl. Этот класс обеспечивает элемент ActiveX теми же базовыми свойствами, что и класс CWnd, и дополнительно возможностями передавать события (Fire events) в контейнер. При этом можно передавать и дополнительные параметры для событий. Кроме того, COleControl позволяет создать карту диспетчеризации (dispatch map), которая используется для того, чтобы показать набор функций и свойств элемента ActiveX. Свойства позволяют контейнеру или пользователю управлять контрольным элементом. Пользователь может изменить поведение и внешний вид элемента, а также получить информацию от элемента. Интерфейс определяется при построении элемента ActiveX с помощью ClassWizard.

Взаимодействие между контрольными элементами и контейнерами. Когда контрольный элемент вставляется в контейнер, он взаимодействует с ним с помощью двух механизмов: с использованием его свойств и методов и посылая события в контейнер. Рис. 28 показывает, как осуществляется это взаимодействие.

Page 38: MAKAREVICH mfc ActiveX

COle Control Вызов методов.

OLE - интерфейс

OLE – контрольный элемент

OLE - контейнер

Рис. 28. Взаимодействие элемента ActiveX иконтейнера.

Обратите внимание, что все взаимодействие с контейнером реализуется через COleControl.

Активное и неактивное состояния Active X. Контрольный элемент имеет два состояния: активное и неактивное. Традиционно эти состояния различаются по тому, имеет ли контрольный элемент окно. Активный элемент имеет окно, а неактивный - нет. Но даже если элемент не имеет окно, он может переходить в активное состояние, при этом он получает и ввод с клавиатуры, и от мыши, и скроллинг и другие события от своего контейнера. Вы можете также обеспечить взаимодействие мыши с неактивным элементом. Элемент может создать свое окно только тогда, когда станет активным.

Windows Оконная процедура

MFC Message

Map (CWnd)

COleControl (base class)

Windows сообщения

Вызов методов

Ваш ActiveX

Когда элемент с окном становится активным, он полностью взаимодействует с контейнером, пользователем и системой. Рис. 29 показывает, способ взаимодествия между ActiveX, контейнером и системой.

Рис. 29. Взаимодействие ActiveX, контейнераи системы.

Рассмотрим классы, создаваемые средством MFC ActiveX ControlWizard подробнее (см. Приложение 1).

Page 39: MAKAREVICH mfc ActiveX

Главный класс модуля ActiveX.

Объявление класса Файл MyCtrl.h является основным файлом заголовков для элемента управления MyCtrl, в нем объявляется класс CMyCtrlApp. Этот класс является потомком класса COleControlModule, а не класса CWinApp. Это справедливо обычно для всех элементов управления, построенных на основе MFC. Класс же COleControlModule библиотеки MFC в свою очередь просто является производным от класса CWinApp, в котором для удобства переопределены методы InitInstance и ExitInstance.

Методы класса Файл MyCtrl.cpp - это основной исходный файл для элемента управления MyCtrl. Функциями этого файла являются: регистрация элемента управления; обеспечение инициализации элемента управления; удаление регистрации элемента управления, когда он больше не нужен. В этом файле происходит создание глобального объекта класса CMyCtrlApp, порожденного от класса COleControlModule. В проекте может быть только один объект приложения, т.е. один объект класса, порожденного от COleControlModule. MFC ActiveX ControlWizard сгенерировал GUID (уникальный идентификатор) для библиотеки типов элемента управления с именем _tlid и принял, что старший номер версии библиотеки равен 1, а младший номер – 0. Эта информация будет записана в реестр во время выполнения макроса IMPLEMENT_OLETYPELIB, находящего в MyCtrlCtrl.cpp. Файл MyCtrl.cpp содержит реализацию методов класса CMyCtrlApp. Метод InitInstance отвечает за инициализацию DLL-файла: Он вызывается системой при первой загрузке элемента управления в память. Также этот метод вызывается при создании каждого экземпляра элемента MyCtrl. В этом методе можно выполнить собственные методы инициализации, однако необходимо всегда в первую очередь вызвать метод InitInstance базового класса COleControlModule. Метод ExitInstance вызывается непосредственно перед тем, как элемент управления выгружается из памяти, и очищает память, освобождает дескрипторы, удаляет GDI-объекты и т.д:

Регистрация элемента управления В файле MyCtrl.cpp, кроме методов класса CmyCtrlApp, определены две функции: DllRegisterServer (заносит данные в системный реестр) и DllUnregisterServer (удаляет данные из системного реестра): Элементы управления ActiveX являются саморегистрирующимися. Это означает, что они способны записывать информацию о себе в системный реестр Windows. Им не нужны ни файлы регистрации (имеющие расширение reg), ни какой-либо другой внешний механизм. Функция DllRegisterServer реализована во всех элементах ActiveX. Это глобальная функция, вызов которой осуществляют внешние функции или приложения.

Page 40: MAKAREVICH mfc ActiveX

При вызове функция DllRegisterServer выполняет следующие действия. Сначала она регистрирует библиотеку типов элемента управления посредством вызова функции AfxOleRegisterTypeLib. (Этот этап включает создание и обновление параметров элемента управления в системном реестре в ключе HKEY_CLASSES_ROOT\TypeLib. Исходный файл библиотеки типов автоматически генерируется MFC ActiveX ControlWizard. В нем описываются свойства, методы, события элемента управления и специфическая для данного класса информация). Затем функция DllRegisterServer регистрирует все фабрики классов данного приложения в системном реестре посредством метода COleObjectFactoryEx::UpdateRegistryAll. ( Фабрика классов представляет собой COM-объект, реализующий интерфейс IClassFactory и отвечающий за производство COM-серверов заданного типа (CLSID). Библиотеки OLE не могут создавать серверы без участия фабрики классов. Понятие фабрики классов рассматривается при исследовании файла MyCtrlCtl.cpp.) При компиляции и компоновке элемента управления с использованием Microsoft Developer Studio последнее, что делает программа, - это вызывает функцию DllRegisterServer, что приводит к автоматической регистрации элемента управления на данной машине. Если необходимо инсталлировать элемент управления на другой машине, нужно каким-то образом вызвать его функцию DllRegisterServer. Обычно это делает регистрационная программа, которую необходимо предоставить. Зарегистрировать ActiveX-объект можно и при помощи утилиты regsvr32.exe, которая обычно находится в каталоге C:\WINDOWS\SYSTEM. Функция DllUnregisterServer является дополнением к функции DllRegisterServer. Элементы ActiveX самостоятельно удаляют свою регистрацию. Это значит, что библиотека типов, фабрика классов и информация об элементе может быть автоматически удалена из системного реестра. Функция DllUnregisterServer не вызывается автоматически из Developer Studio. Ее должны вызывать исключительно внешние функции или приложения, которые хотят полностью удалить элемент из системы. Например, эту функцию могла бы вызвать программа деинсталляции. Заметим, что регистрацию и удаление регистрационной информации возможно с помощью программы ActiveX Control Test Container, вхддящей в состав DevStudio.

Макрос AFX_MANAGE_STATE(p) Макрос AFX_MANAGE_STATE встречается в функциях DllRegisterServer и DllUnregisterServer. Этот макрос необходим некоторым методам управления OLE для выполнения переключения состояния модуля. Для начала уточним понятие “состояние модуля”. Во время выполнения MFC-программы библиотека хранит информацию о состоянии приложения. Эта информация включает дескрипторы окон, таблицы связей дескрипторов и MFC-объектов и др. Если выполняется только базовый модуль (DLL-файлы не подгружаются), то может существовать лишь один набор данных о состоянии. Однако при подключении DLL-файлов, использующих библиотеку MFC (как в случае элементов ActiveX), могут возникать проблемы. Каждый элемент управления сохраняет свою информацию о состоянии. Когда поток выполнения “входит” в элемент управления, последний должен сообщить MFC о том, что наступила его очередь выполняться и следует изменить указатель на набор

Page 41: MAKAREVICH mfc ActiveX

данных о состоянии так, чтобы он указывал на набор данных элемента управления. Элемент управления делает это с помощью макроса AFX_MANAGE_STATE. Такая переадресация должна происходить во всех точках входа в DLL. Поскольку все элементы ActiveX экспортируют функции DllRegisterServer и DllUnregisterServer, то не удивительно, что первым оператором в теле обеих функций является макрос AFX_MANAGE_STATE. Если не выполнить изменение состояния, результат работы программы может быть непредсказуемым.

Класс элемента ActiveX.

Объявление класса Класс OLE-элемента управления объявлен в файле MyCtrlCtl.h и реализуется в файле MyCtrlCtl.cpp. Базовый класс элемента управления наследуется от класса COleControl - стандартного базового класса библиотеки MFC, который используется элементами управления ActiveX. Этот класс, будучи потомком класса CWnd, наследует все функции объекта окна, а также располагает дополнительными, специфическими для OLE функциями, такими как генерация событий и поддержка методов и свойств OLE-элемента управления. В классе COleControl реализованы все интерфейсы, необходимые для работы элемента управления. Он скрывает детали реализации элемента и имеет понятный, объектно-ориентированный интерфейс, который используется элементами управления при выполнении собственных функций.

Конструктор и деструктор класса В конструкторе класса CMyCtrlCtrl происходит вызов функции InitializeIIDs. Эта функция должна вызываться только в конструкторе элемента управления. Она сообщает базовому классу идентификаторы интерфейсов (IID) элемента управления. В данном случае класс COleControl получает идентификатор базового интерфейса автоматизации и идентификатор интерфейса диспетчеризации. Конструктор класса в своем первоначальном виде ничего не делает, но в него можно по мере необходимости добавить код, очищающий все данные экземпляра элемента управления.

Методы класса Класс CMyCtrlCtrl переопределяет некоторые методы базового класса: OnDraw, DoPropExchange, OnResetState. Метод OnDraw вызывается операционной системой, когда элемент управления должен быть перерисован. Вызов метода DoPropExchange происходит, когда элемент управления записывается или считывается из постоянного хранилища (с диска). Эта функция отвечает за сохранение (запись на диск) и восстановление (чтение с диска) информации о свойствах элемента и его версии. Вызов метода OnResetState осуществляет система, когда элемент управления должен сбросить свои свойства (установить их в первоначальные значения). Если ранее сохраненный элемент управления не может быть правильно воссоздан в памяти (то есть не могут быть правильно восстановлены значения

Page 42: MAKAREVICH mfc ActiveX

его свойств), то система дает указание вновь созданному элементу управления (в который помещаются данные, считанные с диска) присвоить своим свойствам значения, имеющие хоть какой-то смысл, т.е. значения по умолчанию.

Поддержка элементов управления Объявление класса CMyCtrlCtrl включает в себя несколько макросов, обеспечивающих поддержку элементов управления OLE. Макрос DECLARE_OLECREATE_EX(CMyCtrlCtrl) объявляет фабрику классов и метод GetClassID для класса элемента управления. Метод GetClassID (объявляемый в этом макросе) вызывается системой, когда ей необходимо получить CLSID элемента. CLSID используется библиотеками OLE для регистрации, создания элемента управления и т.д. Фабрика классов представляет собой OLE-объект, который используется для производства экземпляров других объектов. Фабрики классов элемента управления располагаются внутри OCX-файла, вместе с самим элементом. Библиотека MFC сама обеспечивает реализацию фабрики классов (разработчику не нужно о ней беспокоиться). В основном фабрика классов реализована в базовом классе COleObjectFactoryEx . Фактически конструктор фабрики классов для элемента управления просто передает свои параметры базовому классу, в котором скрыты детали реализации. Рассмотрим подробнее назначение класса COleObjectFactoryEx. Класс COleObjectFactoryEx является синонимом класса COleObjectFactory. Он реализует фабрику классов OLE и содержит методы для:

• управления регистрации объектов; • обновления системного реестра, а также регистрации элемента

управления во время выполнения программы (это необходимо для информирования библиотек OLE о том, что объект находится в режиме выполнения и готов к приему сообщений);

• принудительного лицензирования путем задания ограничений на использование элемента зарегистрированными разработчиками (на этапе проектирования) и зарегистрированными приложениями (на этапе выполнения);

• регистрации фабрики классов элемента управления в системном реестре. Инициализация фабрики классов элемента управления и формирование GUID (CLSID) производятся в файле реализации. Эти действия выполняет макрос IMPLEMENT_OLECREATE_EX. В этом макросе реализуется фабрика классов элемента управления и метод GetClassID. Фабрика классов создает экземпляры элемента управления. Метод GetClassID возвращает CLSID элемента всем, кто его запрашивает, например библиотекам OLE. В объявлении класса присутствует макрос DECLARE_OLETYPELIB(CMyCtrlCtrl), который объявляет метод GetTypeLib для класса элемента управления.. Этот макрос должен присутствовать в файле заголовков элемента управления. Метод GetTypeLib элемента управления реализуется макросом IMPLEMENT_OLETYPELIB в файле реализации класса. Библиотека типов представляет собой файл, содержащий информацию о типе элемента управления, его интерфейсах и элементах автоматизации (к ним относятся

Page 43: MAKAREVICH mfc ActiveX

свойства, методы и события). Сгенерированный метод GetTypeLib вызывается системой, когда ей нужна информация о библиотеке типов элемента. Эти данные сравниваются с данными из системного реестра, и если обнаруживается совпадение, то информация передается системе. Элемент управления ActiveX использует страницы свойств, чтобы дать пользователю возможность просматривать и изменять свойства элемента управления. Страницы свойств реализуются в виде диалоговых окон с закладками. В макросе DECLARE_PROPPAGEIDS(CMyCtrlCtrl) объявляются идентификаторы и методы, используемые этим механизмом. Макрос DECLARE_OLECTLTYPE(CMyCtrlCtrl) объявляет методы, которые обрабатывают информацию о типе элемента. Информация о типе элемента управления записана в переменной типа const с именем _dwMyCtrlOleMisc А макрос IMPLEMENT_OLECTLTYPE в реализации класса определяет два метода элемента управления GetUserTypeNameID и GetMiscStatus. Метод GetUserTypeNameID возвращает идентификатор ресурса строки (константа IDS_Name), содержащей имя типа элемента управления. Метод GetMiscStatus вызывается внешними объектами, например контейнером, для получения информации об элементе управления. Значения, записанные в переменной _dwNameOleMisc, генерируются при создании проекта и являются выбором установок проекта.

Идентификаторы интерфейсов (IID) автоматизации Строки файла реализации класса, объявляющие IID_DMyCtrl и IID_DMyCtrlEvents определяют идентификаторы интерфейсов элемента управления. Первый из них идентифицирует реализацию IDispatch - базовый интерфейс диспетчеризации класса. Этот интерфейс используется клиентами автоматизации для редактирования и просмотра элемента управления и выполнения его методов. Второй идентификатор определяет интерфейс диспетчеризации событий класса. Этот интерфейс используется элементом управления для генерации событий. Оба идентификатора хранятся в системном реестре в ключе HKEY_CLASSES_ROOT\Interface.

Таблица сообщений класса Класс CMyCtrlCtrl c помощью макроса DECLARE_MESSAGE_MAP объявляет таблицу сообщений (схему сообщений) класса, при помощи чего сообщает системе, что класс будет обрабатывать сообщения. Сама таблица сообщений объявляется в файле реализации класса в макросе BEGIN_MESSAGE_MAP. За реализацию технологии OLE-автоматизации, то есть за предоставление внешнему миру методов и свойств элемента, отвечает OLE-интерфейс IDispatch. Библиотека MFC обеспечивает поддержку этой технологии с помощью конструкции под названием “схема диспетчеризации” (dispatch map). Эта схема представляет собой эквивалент знакомой уже схемы сообщений (message map). Подобно тому, как схемы сообщений скрывают детали взаимодействия с каналом сообщений Windows, схемы диспетчеризации скрывают детали OLE-автоматизации. При обработке каких-то сообщений Windows в элементе ActiveX в раздел BEGIN_MESSAGE_MAP(CMyCtrlCtrl, COleControl)

Page 44: MAKAREVICH mfc ActiveX

… END_MESSAGE_MAP() добавляются соответствующие строки.

Схема диспетчеризации класса В объявлении класса присутствует макрос DECLARE_DISPATCH_MAP, который объявляет схему диспетчеризации. Посредством нее элемент управления связывает свои свойства с соответствующими методами записи, чтения и оповещения (последние вызываются при изменении значения свойства). Дополнительно устанавливается связь между идентификаторами диспетчеризации (DISPID) и фактическими методами. Сначала схема диспетчеризации в файле реализации ( BEGIN_DISPATCH_MAP(CMyCtrlCtrl, COleControl … END_DISPATCH_MAP() ) имеет одну запись, сделанную при помощи макроса DISP_FUNCTION_ID, который связывает между собой следующие элементы: имя класса; внешнее имя метода; идентификатор (DISPID) метода; метод C++, соответствующий вызову метода автоматизации; список параметров метода (тип VARIANT); тип значения, возвращаемого методом (тип VARIANT). Эта информация необходима системе для того, чтобы направлять вызовы методов автоматизации по правильному пути. Когда контроллер автоматизации вызывает метод при помощи его внешнего имени, такой вызов преобразуется в вызов внутренней функции элемента управления. В основе этого механизма лежит DISPID. Каждому объявленному свойству или методу должен соответствовать свой макрос в схеме диспетчеризации класса. При создании свойств и методов элементов в раздел BEGIN_DISPATCH_MAP(CMyCtrlCtrl, COleControl) … END_DISPATCH_MAP() добавляются соответствующие строки.

Схема диспетчеризации событий класса В объявлении класса макрос DECLARE_EVENT_MAP добавляет схему событий (event map). Подобно тому, как доступ к свойствам и методам элемента управления осуществляется с помощью схемы диспетчеризации, доступ к событиям элемента осуществляется посредством схемы событий. В ней имена и идентификаторы событий связываются с функциями, ответственными за генерацию событий. На этапе проектирования у элемента нет событий по умолчанию, поэтому эта схема событий в файле реализации пуста. Затем она дополняется, когда Вы добавляете события к элементу ActiveX: BEGIN_EVENT_MAP(CMyCtrlCtrl, COleControl)

Page 45: MAKAREVICH mfc ActiveX

//{{AFX_EVENT_MAP(CMyCtrlCtrl) EVENT_CUSTOM("Select", FireSelect, VTS_BOOL) EVENT_CUSTOM("Tick", FireTick, VTS_I4) //}}AFX_EVENT_MAP END_EVENT_MAP()

Идентификаторы DISPID Различные идентификаторы свойств, методов и событий (DISPID) располагаются между макросами AFX_DISP_ID в файле объявления класса элемента MyCtrl. Эти идентификаторы используются в ODL-файле элемента управления. По мере добавления в элемент свойств, методов и событий этот раздел будет заполняться средством ClassWizard.

Класс страницы свойств элемента управления ActiveX. Страницы свойств - это OLE-объекты, которые дают пользователю возможность просматривать и изменять свойства элемента управления OLE. Страницы свойств содержат один или несколько стандартных диалоговых элементов управления, связанных с различными свойствами. Страницы свойств отображаются в виде модальных или немодальных диалоговых окон с закладками, причем каждой странице соответствует своя закладка. По умолчанию средство MFC ActiveX ControlWizard создает одну страницу свойств, за работу которой в рассматриваемом случае OLE-элемента управления отвечает класс CMyCtrlPropPpg.

Объявление класса Класс страницы свойств CMyCtrlPropPage объявляется в файле MyCtrlPpg.h. Этот класс сгенерирован при создании проекта. Он реализует одну пустую страницу свойств. Этот класс является прямым потомком класса COlePropertyPage, базового класса всех страниц свойств. Класс CMyCtrlPropPage является OLE-объектом. Поэтому ему необходима фабрика классов, генерацию которой обеспечивает макрос DECLARE_OLECREATE_EX. Фабрика классов и генерация CLSID для класса страницы свойств реализована в файле MyCtrlPpg.cpp при помощи макроса IMPLEMENT_OLECREATE_EX.

Стандартные элементы управления страницы свойств Страницы свойств - это просто дочерние диалоговые окна, обычно содержащие ряд стандартных элементов управления. Как и в случае обычных диалоговых окон, можно связать с каждым элементом управления диалога соответствующую ему переменную - элемент класса диалога. Такие элементы объявляются средством ClassWizard в классе страницы свойств в следующем блоке AFX_DATA. Обмен данными между диалоговым окном страницы свойств и его элементами управления посредством связанных с элементами управления переменных класса осуществляется при помощи метода DoDataExchange(), который реализован в файле MyCtrlPpg.cpp. Этот метод использует функции обмена и

Page 46: MAKAREVICH mfc ActiveX

проверки данных (DDX/DDV-макросы). Мастер ClassWizard в дополнение к любым вызовам функций DDX_ и DDV_ вставляет в функцию DoDataExchange класса страницы свойств набор вызовов функций DDP_.

Редактирование записей системного реестра для класса страницы свойств Добавление и удаление записей из системного реестра для класса CMyCtrlPropPage производится в методе UpdateRegistry фабрики классов. Этот метод фабрики классов регистрирует либо снимает регистрацию класса CMyCtrlPropPage. Это делает страницу свойств доступной для всех OLE-серверов и контейнеров. Добавление событий, методов и свойств для элемента ActiveX.

События. События посылаются контрольными элементами в контейнер для того, чтобы оповестить о каких-то изменениях в нем. Эти события могут быть связаны с нажатием на клавиатуру, мышь или изменениями состояния элемента. Когда это случается, элемент уведомляет (Fire) контейнер. MFC поддерживает два вида событий: базовые (stock) и пользовательские (custom). Базовые события реализованы в базовом классе COleControl автоматически. Пользовательские позволяют реализовать сообщения в контейнер, специфические для данного контрольного элемента. Для правильной передачи сообщений в контейнер Ваш элемент должен реализовать обработку каждого события как функцию-член, которая вызывается, когда событие происходит. Делается это с помощью карты событий ( event map ). Тогда Class Wizard может легко манипулировать событиями. Карта событий объявляется в .H файле класса элемента: DECLARE_EVENT_MAP() После объявления карты событий она должна быть определена в .CPP файле: BEGIN_EVENT_MAP(CMyCtrlCtrl, COleControl) //{{AFX_EVENT_MAP(CMyCtrlCtrl)

… //}}AFX_EVENT_MAP END_EVENT_MAP() При использовании Control Wizard эти строки добавляются автоматически. Для добавления событий используется ClassWizard. При этом весь необходимый код добавляется и в карту и в файл .ODL.

Добавление базовых событий в контрольный элемент. Базовые события отличаются от пользовательских тем, что они автоматически посылаются классом COleControl. COleControl содержит методы, которые посылают события из элемента. Этих событий 9. Они перечислены в таблице 2.

Таблица 2. Базовые события, поддерживаемые в COleControl

Событие Функция, посылающая событие в контейнер Примечания

Page 47: MAKAREVICH mfc ActiveX

Click void FireClick( )

Посылается в контейнер, когда контрольный элемент владеет мышью и на ней выполняется щелчок любой кнопкой и кнопка отпускается. Базовые события MouseDown и MouseUp (см. ниже) случаются перед этим событием. Вход в карте событий -EVENT_STOCK_CLICK( )

DblClick void FireDblClick( )

Посылается, когда BUTTONDBLCLKсообщение принимается. Вход в карте событий -EVENT_STOCK_DBLCLICK( )

Error

void FireError( SCODE scode, LPCSTR lpszDescription, UINT nHelpID = 0 )

Посылается в контейнер, когда ошибка произошла вне методов вызова и доступа к свойствам. Вход в карте событий -EVENT_STOCK_ERROREVENT( )

KeyDown void FireKeyDown( short nChar, short nShiftState )

Посылается в контейнер, когда WM_SYSKEYDOWN или WM_KEYDOWN сообщения принимает контрольный элемент. Вход в карте событий -EVENT_STOCK_KEYDOWN( )

KeyPress void FireKeyPress( short* pnChar )

Посылается в контейнер, когда WM_CHAR сообщений получено. Вход в карте событий -EVENT_STOCK_KEYPRESS( )

KeyUp void FireKeyUp( short nChar, short nShiftState )

Посылается, когда WM_SYSKEYUPили WM_KEYUP сообщения получены. Вход в карте событий -EVENT_STOCK_KEYUP( )

MouseDown void FireMouseDown( short nButton, short nShiftState, float x, float y )

Полылается, когда BUTTONDOWN(левая, средняя, или правая) событие получается. Вход в карте событий -EVENT_STOCK_MOUSEDOWN( )

MouseMove void FireMouseMove( short nButton, short nShiftState, float x, float y )

Посылается, когда WM_MOUSEMOVE событие получено элементом. Вход в карте событий -EVENT_STOCK_MOUSEMOVE( )

MouseUp void FireMouseUp( short nButton, short nShiftState, float x, float y )

Посылается в контейнер, когда BUTTONUP событие, связанное с любой кнопкой мыши, получено. Вход в карте событий -EVENT_STOCK_MOUSEUP( )

ReadyStateChange void Посылается, когда контрольный

Page 48: MAKAREVICH mfc ActiveX

FireReadyStateChange( ) элемент переходит в новое состояние при получении данных. Вход в карте событий -EVENT_STOCK_READYSTATECHANGE( )

Добавление базовых событий к контрольному элементу проще, чем пользовательских, так как посылка события в контейнер уже реализована в базовом классе COleControl. Для того, чтобы добавить к контрольному элементу, созданному с помощью СontrolWizard, событие, например, KeyPress, надо выполнить следующие действия с помощью Class Wizard: 1. Загрузить проект. 2. В меню View выбрать Class Wizard. 3. Щелкнуть на вкладке ActiveX Events. 4. Выбрать имя Вашего контрольного элемента в списке Class Name. 5. Щелкнуть на кнопке Add Event. 6. В списке External Name выбрать KeyPress или другое из встроенных

событий. 7. Нажать кнопку OK. 8. Нажать кнопку OK для завершения работы с Class Wizard.

Изменения, внесенные Class Wizard в код. Так как базовые события управляются базовым классом COleControl, ClassWizard не вносит изменения в файл .H. Он добавляет событие в карту событий (event map) и добавляет вход в файл .ODL. При этом в файл .cpp добавляется одна строка: EVENT_STOCK_KEYPRESS() Добавление этой строки будет приводить в посылке события KeyPress в контейнер, когда контрольный элемент получит событие WM_CHAR. Это же событие можно послать в контейнер программно в любой другой момент, если вызвать функцию типа fire, в нашем случае – FireKeyPress. ClassWizard добавляет следующую строку в .ODL файл: [id(DISPID_KEYPRESS)] void KeyPress( short * KeyAscii); Эта строка связывает событие KeyPress c его стандартным идентификатором и позволяет контейнеру ожидать этого события.

Добавление пользовательских событий в контрольный элемент. Пользовательские события автоматически не реализованы в классе COleControl. В карту событий они добавляются с использованием макроса EVENT_CUSTOM.

Добавление пользовательского события с помощью ClassWizard. Следующий пример добавляет событие пользователя с именем ClickIn. Для другого события просто смените имя и параметры события. 1. Загрузите Ваш проект. 2. В меню View выберете ClassWizard. 3. Щелкните на вкладке ActiveX Events. 4. Выберете имя контрольного элемента в списке Class Name. 5. Нажмите на кнопке Add Event.

Page 49: MAKAREVICH mfc ActiveX

6. В поле External name впечатайте имя события – ClickIn. 7. В поле Internal name впечатайте имя функции для пересылки события в

контейнер. По умолчанию имя функции формируется добавлением Fire: FireClickIn.

8. Добавьте параметры в список параметров Parameter List: xCoord и yCoord типа OLE_XPOS_PIXELS и OLE_YPOS_PIXELS соответственно. Обратите внимание, что тип параметров можно выбрать из открываемого списка.

9. Щелкните на кнопке OK, чтобы закрыть диалог Add Event.. 10. Щелкните на кнопке OK, чтобы закрыть ClassWizard.

Изменения, внесенные в код ClassWizard. При добавлении пользовательских событий ClassWizard вставляет код в .H, .CPP, .ODL файлы. Показанный ниже код добавлен для события ClickIn. В .H файл Вашего контрольного элемента добавляются строки: void FireCliclIn(OLE_XPOS_PIXELS xCoord, OLE_YPOS_PIXELS yCoord) {

FireEvent ( eventIdClickIn, EVENT_PARAM(VTS_XPOS_PIXELS VTS_YPOS_PIXELS), xCoord,yCoord);

} Этот код объявляет функцию FireClickIn, которая вызывает COleControl::FireEvent с событием ClickIn и параметрами, которые Вы определили в ClassWizard. В файл элемента .CPP добавляется код: EVENT_CUSTOM(“ClickIn”, FireClickIn, VTS_XPOS_PIXELS, VTS_YPOS_PIXELS) Этот код связывает событие СlickIn с функцией FireClickIn и параметрами, определенными в ClassWizard. В файл .ODL добавляется следующий код: [id(1)] void ClickIn(OLE_XPOS_PIXELS xCoord, OLE_YPOS_PIXELS yCoord); Эта строка назначает событию ClickIn идентификатор. Идентификатор определяется в зависимости от порядкового номера события в списке событий ClassWizard.

Вызов функции FireClickIn. Вы должны вызвать функцию FireClickIn, когда Вы хотите передать сообщение в контейнер. Например, Вы хотите, чтобы событие передавалось в контейнер, когда пользователь щелкает на левой кнопке мыши в элементе. Тогда Вы должны добавить обработчик события WM_LBUTTONDOWN в элементе: 1. Загрузите Ваш проект. 2. В меню View щелкните на пункте ClassWizard. 3. Выберете закладку Message Maps. 4. В списке Object Ids выберете имя Вашено контрольного элемента. 5. В списке Messages выберете сообщение WM_LBUTTONDOWN. 6. Щелкните на кнопке Add Function. 7. Отредактируйте код вставленной функции, например, так: void CMyCtrlCtrl::OnLButtonDown(UINT nFlags, CPoint point)

Page 50: MAKAREVICH mfc ActiveX

{ COleControl::OnLButtonDown(nFlags, point); FireClickIn(point.x, point.y); }

Пользовательские события с базовыми именами. Вы можете реализовать пользовательские события с именами базовых, но нельзя реализовать с одним именем и базовое и пользовательское события. Например, Вы можете создать свое событие с именем Click, но тогда базовое событие Click не будет пересылаться в контейнер. Но Вы в любой момент можете послать свое событие, вызвав соответствующий метод – FireClick. Чтобы добавить пользовательское событие с именем базового, выполните следующие действия: 1. Загрузите Ваш проект. 2. В меню View выберете ClassWizard. 3. Щелкните на вкладке ActiveX Events. 4. Щелкните Add Event. 5. В списке External name выберете имя базового события, например, Click. 6. Выберете Custom в группе Implementation. 7. Нажмите на кнопку OK. 8. Выйдите из ClassWizard, нажав на OK. 9. Вызывайте FireClick, где надо в Вашем коде.

Методы ActiveX. Элемент ActiveX взаимодействует с контейнером, посылая в него сообщения о событиях. Контейнер также должен взаимодействовать с элементом. Это достигается использованием методов и свойств. Методы подобны функциям-членам в С++. Имеется два типа методов, которые можно реализовать в Вашем контрольном элементе: базовые и пользовательские. Также как и базовые события, базовые методы реализованы в классе COleControl. MFC реализует механизм для поддержки и базовых и пользовательских методов. СOleControl - наследних класса CWnd, поэтому он использует методы, общие для всех элементов ActiveX. Вторая часть механизма, обеспечиваемая MFC, - это карта диспетчеризации (dispatch map). Эта карта подобна карте сообщений (message map), однако, вместо связи функций с оконными сообщениями карта диспетчеризации осуществляет связь виртуальных методов с идентификаторами IDispatch. Карта диспетчеризации должна быть объявлена в .H файле элемента: DECLARE_DISPATCH_MAP() Главная задача этой карты - связать методы, используемые контейнерами, c методами контрольного элемента, реализующими определенные действия. Карта определяется в файле .CPP: BEGIN_DISPATCH_MAP(CMyCtrlCtrl, COleControl) //{{AFX_DISPATCH_MAP(CMyCtrlCtrl) … //}}AFX_DISPATCH_MAP END_DISPATCH_MAP()

Page 51: MAKAREVICH mfc ActiveX

При создании элемента с помощью ControlWizard нужные строки добавляются автоматически.

Добавление базовых методов к контрольному элементу. Базовые методы уже реализованы в классе COleControl. Например, COleControl содержит функцию, которая поддерживает Refresh метод для ActiveX. Вход в карте диспетчеризации для этого метода DISP_STOCKFUNC_REFRESH. COleControl поддерживает два базовых метода – DoClick и Refresh. Метод Refresh вызывается для обновления отображения элемента, а метод DoClick – вызывается для пересылки события Click в контейнер. Вход в карте диспетчеризации для метода DoClick - DISP_STOCKFUNC_DOCLICK. Для добавления базового метода к элементу Вы должны: 1. Загрузить Ваш проект. 2. В меню View выбрать ClassWizard. 3. Выбрать вкладку Automation. 4. Выберете имя класса из списка Class name. 5. Щелкнуть на кнопке Add Method. 6. В списке External name выберете имя Refresh или DoClick. 7. Щелкните Ok. 8. Закройте ClassWizard, нажав на кнопку OK.

Изменения, внесенные в код при вставке базовых методов. Поскольку базовые методы поддерживаются в классе COleControl, то изменения вносятся только в .CPP и .ODL файлы. В файл .CPP вставляется строка в карту диспетчеризации: DISP_STOCKFUNC_REFRESH() В файл .ODL добавляется строка, назначающая методу Refresh идентификатор: [id(DISPID_REFRESH)] void Refresh();

Добавление пользовательских методов к контрольному элементу. Используя пользовательские методы, можно из контейнера обращаться к элементу ActiveX. Давайте попробуем добавить некоторый метод MyMethod к элементу. Для этого Вы должны: 1. Загрузить Ваш проект. 2. В меню View выбрать ClassWizard. 3. Выбрать вкладку Automation. 4. Выберете имя класса из списка Class name. 5. Щелкнуть на кнопке Add Method. 6. Впечатать в списке External name имя метода MyMethod. 7. В элемент Internal name впечатайте имя внутренней функции или оставьте

значение, предлагаемое ClassWizard по умолчанию. 8. В списке Return Type выберете тип возвращаемого значения, в нашем случае

– short. 9. Используя Parameter List добавьте параметры, например, xCoord типа

OLE_XPOS_PIXELS и yCoord типа OLE_YPOS_PIXELS. 10. Щелкните Ok. 11. Закройте ClassWizard, нажав на кнопку OK.

Page 52: MAKAREVICH mfc ActiveX

Изменения в коде при добавлении пользовательских методов. В .H файл добавляются строки в карту диспетчеризации: afx_msg short MyMethod(OLE_XPOS_PIXELS xCoord, OLE_YPOS_PIXELS yCoord); Пользователь может вызвать этот метод из контейнера. Следующая строка добавляется в .ODL файл: [id(5)] short MyMethod(OLE_XPOS_PIXELS xCoord, OLE_YPOS_PIXELS yCoord); Строка назначает методу определенный идентификатор. В файл .CPP добавляется вход в карту диспетчеризации: DISP_FUNCTION(CMyCtrlCtrl, "MyMethod", MyMethod, VT_I2, VTS_XPOS_PIXELS VTS_YPOS_PIXELS) Кроме того, добавляется текст для самой функции MyMethod в класс элемента: short CMyCtrlCtrl::MyMethod(OLE_XPOS_PIXELS xCoord, OLE_YPOS_PIXELS yCoord) { // TODO: Add your dispatch handler code here return 0; }

Возврат кодов ошибок из методов. Для того, чтобы сообщить контейнеру, что произошла ошибка в методе, Вы должны использовать метод COleControl::ThrowError. Этот метод имеет параметр SCODE ( status code ). Вы можете использовать свой код для SCODE или применять один из определенных. Этот метод можно применять только для методов или для функций Get/Set. В остальных случаях Вы должны вызвать метод COleControl::FireError. Этот метод также использует в качестве параметра код типа SCODE.

Таблица 3. Коды ошибок для ActiveX

Ошибка Описание CTL_E_ILLEGALFUNCTIONCALL Неправильный вызов функции CTL_E_OVERFLOW Переполнение CTL_E_OUTOFMEMORY Вне памяти CTL_E_DIVISIONBYZERO Деление на ноль CTL_E_OUTOFSTRINGSPACE Вне диапазона строки CTL_E_OUTOFSTACKSPACE Вне диапазона стека CTL_E_BADFILENAMEORNUMBER Плохое имя файла или число CTL_E_FILENOTFOUND Файл не найден CTL_E_BADFILEMODE Неверный режим файла CTL_E_FILEALREADYOPEN Файл уже открыт CTL_E_DEVICEIOERROR Ошибка устройства ввода/вывода

Page 53: MAKAREVICH mfc ActiveX

CTL_E_FILEALREADYEXISTS Файл уже существует CTL_E_BADRECORDLENGTH Плохая длина записи CTL_E_DISKFULL Диск переполнен CTL_E_BADRECORDNUMBER Плохой номер записи CTL_E_BADFILENAME Плохое имя файла CTL_E_TOOMANYFILES Слишком много файлов CTL_E_DEVICEUNAVAILABLE Устройство недоступно CTL_E_PERMISSIONDENIED Доступ запрещен CTL_E_DISKNOTREADY Диск не готов CTL_E_PATHFILEACCESSERROR Ошибка доступа к файлу CTL_E_PATHNOTFOUND Путь не найден CTL_E_INVALIDPATTERNSTRING Неверный образец CTL_E_INVALIDUSEOFNULL Неверное использование NULL CTL_E_INVALIDFILEFORMAT Неверный формат файла CTL_E_INVALIDPROPERTYVALUE Неверное значение свойства CTL_E_INVALIDPROPERTYARRAYINDEX Неверный индекс в массиве свойств

CTL_E_SETNOTSUPPORTEDATRUNTIME Установка свойства не поддерживается во время выполнения программы

CTL_E_SETNOTSUPPORTED Установка свойства не поддерживается (свойство только читается)

CTL_E_NEEDPROPERTYARRAYINDEX Необходим индекс в массиве свойств CTL_E_SETNOTPERMITTED Установка свойства не разрешено

CTL_E_GETNOTSUPPORTEDATRUNTIME Получение свойства не поддерживается во время выполнения программы

CTL_E_GETNOTSUPPORTED Получение свойства не поддерживается (свойство только устанавливается)

CTL_E_PROPERTYNOTFOUND Свойство не найдено CTL_E_INVALIDCLIPBOARDFORMAT Неверный формат для буфера обмена CTL_E_INVALIDPICTURE Неверный формат рисунка CTL_E_PRINTERERROR Ошибка принтера CTL_E_CANTSAVEFILETOTEMP Не могу сохранить файл в TEMP CTL_E_SEARCHTEXTNOTFOUND Не найден указанный текст CTL_E_REPLACEMENTSTOOLONG Текст для замены слишком длинный

Для определения своего кода Вы можете использовать макрос CUSTOM_CTL_SCODE . Параметр для этого макроса должен лежать в диапазоне от 1000 до 32767 включительно. Например: #define MYCTL_E_SPECIALERROR CUSTOM_CTL_SCODE(1000)

Задание свойств для ActiveX. Итак, контрольный элемент взаимодействует с контейнером, передавая внего события, а контейнер воздействует на элемент, используя методы или свойства.

Page 54: MAKAREVICH mfc ActiveX

Отличаются методы и свойства тем, что методы – это функции класса элемента, а свойства – это его данные. Свойства также делятся на базовые, для которых реализованы методы в классе COleControl, и пользовательские. Базовые свойства, поддерживаемые в COleControl, показаны в таблице 4.

Таблица 4. Базовые свойства, поддерживаемые в COleControl

Свойство Вход в карте диспетчеризации Как получить значение свойства или изменить его

Appearance DISP_STOCKPROP_APPEARANCE( ) Значение доступно через m_sAppearance.

BackColor DISP_STOCKPROP_BACKCOLOR( ) Значение доступно через вызов GetBackColor.

BorderStyle DISP_STOCKPROP_BORDERSTYLE( )

Значение доступно через m_sBorderStyle.

Caption DISP_STOCKPROP_CAPTION( ) Значение доступно через вызов InternalGetText.

Enabled DISP_STOCKPROP_ENABLED( ) Значение доступно через m_bEnabled.

Font DISP_STOCKPROP_FONT( ) Смю ниже использование шрифтов в ActiveX.

ForeColor DISP_STOCKPROP_FORECOLOR( ) Значение доступно через вызов GetForeColor.

hWnd DISP_STOCKPROP_HWND( ) Значение доступно через m_hWnd.

Text DISP_STOCKPROP_TEXT( )

Значение доступно через вызов InternalGetText. Это свойство эквивалентно Caption, кроме имени свойства.

Добавление базовых свойств к контрольному элементу. Для добавления любого базового свойства Вы должны выполнить следующие действия: 1. Загрузить Ваш проект. 2. В меню View выбрать ClassWizard. 3. Выбрать вкладку Automation. 4. Выберете имя класса из списка Class name. 5. Щелкнуть на кнопке Add Property. 6. В списке External Name выбрать нужное свойство, например, Caption или

Font. При этом в группе Implementation автоматически выбирается Stock. 7. Щелкнуть на кнопке OK для закрытия диалога Add Property. 8. Щелкнуть на кнопке Ok для закрытия ClassWizard.

Изменения в коде, внесенные ClassWizard. Поскольку базовые свойства уже реализованы в COleControl, код добавляется только в .СPP файл и .ODL файл.

Page 55: MAKAREVICH mfc ActiveX

В карту диспетчеризации вставляется строка вида DISP_STOCKPROP_CAPTION() DISP_STOCKPROP_FONT(), в зависимости от добавленного свойства. Следующая строка добавляется в файл .ODL: [id(DISPID_CAPTION), bindable, requestedit] BSTR Caption; [id(DISPID_FONT), bindable] IFontDisp* Font; в зависимости от добавленного свойства. Строки назначают идентификаторы свойствам. Теперь выбранные свойства доступны для пользователей. Для этого просто можно использовать методы и данные класса COleControl. С большинством базовых свойств связаны функции, которые вызываются при их изменении. Например, при изменении свойства BackColor вызывается метод OnBackColor. По умолчанию реализация метода заключается в вызове InvalidateControl. Вы можете, конечно, добавить дополнительные действия.

Свойства определения цветов. Вы можете использовать базовые свойства ForeColor и BackColor или создать собственные для отрисовки контрольного элемента. Для того, чтобы использовать свойство, связанное с цветом, Вам следует вызвать функцию COleControl::TranslateColor. Свойство, связанное с цветом, имеет тип OLE_COLOR. Этот тип должен быть преобразован к типу COLORREF. Параметры функции COleControl::TranslateColor – значение свойства типа OLE_COLOR и, по желанию, идентификатор палитры. Возвращаемое значение – цвет в форме COLORREF, который можно использовать во всех функциях GDI, например, SetTextColor и CreateSolidBrush. Получить значения свойств ForeColor и BackColor можно с помощью функций GetForeColor и GetBackColor соответственно. Пример ниже показывает применение этих функций: CBrush bkBrush(TranslateColor(GetBackColor())); COLORREF clrFore = TranslateColor(GetForeColor()); pdc->FillRect( rcBounds, &bkbrush ); pdc->SetTextColor( clrFore ); pdc->DrawText( InternalGetText(), -1, rcBounds, DT_SINGLELINE | DT_CENTER | DT_VCENTER ); Здесь создается кисть с цветом, заданным как базовое свойство BackColor. Цвет текста определяется свойством ForeColor.

Использование шрифтов в элементах ActiveX. Если Ваш элемент использует текст, то Вы можете изменять шрифт, применяя свойство шрифта. При этом можно использовать базовое свойство или реализуемое пользователем.

Использование базового свойства шрифтов в элементах ActiveX. Это свойство реализовано в классе COleControl. Дополнительно реализована страница свойств шрифта, позволяя пользователю указывать различные парамтреы шрифтов.

Page 56: MAKAREVICH mfc ActiveX

Доступ к шрифтам осуществляется с помощью методов GetFont, SetFont и InternalGetFont класса COleControl. Методы GetFont, SetFont используются как обычные методы Get и Set. Если требуется получить параметры шрифта внутри элемента, рекомендуется использовать метод InternalGetFont. Добавление базового свойства шрифта выполняется, как было описано выше. Обратите внимание на код, добавляемый к файлу .H: DISP_STOCKPROP_FONT(), А ткже на код, добавленный к файлу .ODL: [id(DISPID_FONT), bindable] IFontDisp* Font;

Использование пользовательского свойства шрифта. Чтобы реализовать пользовательское свойство, добавьте его с помощью ClassWizard, а затем вручную измените код: 1. Загрузите Ваш проект. 2. В меню View выберете ClassWizard. 3. Выберете вкладку Automation. 4. Выберете имя класса из списка Class name. 5. Щелкните на кнопке Add Property. 6. В списке External Name впечатайте нужное свойство, например,

HeadingFont. 7. При этом в группе Implementation выберете Get/Set Methods. 8. В списке Return Type выберете LPFONTDISP. 9. Щелкните на кнопке OK для закрытия диалога Add Property. 10. Щелкните на кнопке Ok для закрытия ClassWizard. ClassWizard создаст код в карте диспетчеризации: DISP_PROPERTY_EX(CSampleCtrl, "HeadingFont", GetHeadingFont, SetHeadingFont, VT_DISPATCH) Макрос DISP_PROPERTY_EX связывает свойство HeadingFont с соответствующими методами Get and Set - GetHeadingFont and SetHeadingFont. Тип этого свойства - VT_DISPATCH. ClassWizard также добавляет объявление методов GetHeadingFont и SetHeadingFont в файлы .H, а их реализацию – в файл .CPP: LPDISPATCH CMyCtrlCtrl::GetHeadingFont() { // TODO: Add your property handler here return NULL; } void CMyCtrlCtrl::SetHeadingFont(LPDISPATCH newValue) { // TODO: Add your property handler here SetModifiedFlag(); } Наконец, ClassWizard изменяет .ODL файл: [id(1)] IDispatch* HeadingFont;

Page 57: MAKAREVICH mfc ActiveX

Как изменить код при использовании пользовательского свойства шрифта. Вы должны изменить код вручную, чтобы добавленное Вами свойство использовалось правильно. В файл (.H) добавьте переменную: protected: CFontHolder m_fontHeading; В файле (.CPP):

• Инициализируйте m_fontHeading в конструкторе: CMyCtrlCtrl:: CMyCtrlCtrl ( ) : m_fontHeading( &m_xFontNotification ) { // [...тело конструктора...] }

• Объявите статическую структуру FONTDESC, содержащую параметры шрифта поумолчанию: static const FONTDESC _fontdescHeading = { sizeof(FONTDESC), OLESTR("MS Sans Serif"), FONTSIZE( 12 ), FW_BOLD, ANSI_CHARSET, FALSE, FALSE, FALSE };

• В методе DoPropExchange добавьте вызов PX_Font: void CMyCtrlCtrl::DoPropExchange(CPropExchange* pPX) { COleControl::DoPropExchange(pPX); // [...другие PX_ function ...] PX_Font(pPX, _T("HeadingFont"), m_fontHeading, &_fontdescHeading); }

• Закончите реализацию метода GetHeadingFont: LPFONTDISP CMyCtrlCtrl::GetHeadingFont( ) { return m_fontHeading.GetFontDispatch( ); }

• Закончите реализацию метода SetHeadingFont: void CMyCtrlCtrl::SetHeadingFont( LPFONTDISP newValue ) { m_fontHeading.InitializeFont( &_fontdescHeading, newValue); OnFontChanged(); //Объявить об изменениях SetModifiedFlag( ); }

• Измените метод OnDraw : CFont* pOldHeadingFont; pOldHeadingFont = SelectFontObject(pdc, m_fontHeading); // рисование новым шрифтом pdc->SelectObject(pOldHeadingFont);

После создания свойства шрифта следует реализовать страницу для изменения этого свойства при использовании контрольного элемента. Для добавления такой страницы измените макрос BEGIN_PROPPAGEIDS , вставив строку: PROPPAGEID(CLSID_CFontPropPage)

Page 58: MAKAREVICH mfc ActiveX

Вы должны также изменить число страниц в макросе BEGIN_PROPPAGEIDS: BEGIN_PROPPAGEIDS(CMyCtrlCtrl, 2)

Обработка нотификационных сообщений при работе со шрифтом. В большинстве случаев контрольный элемент должен знать об изменении характеристик шрифта. Каждый раз при изменении параметров шрифта вызывается функция-член класса COleControl - OnFontChanged. При использовании базового свойства можно применять реализацию этого метода по умолчанию. Если же Вы использовали пользовательское свойство шрифта, Вы должны изменить эту функцию. Например, Для нашего примера надо передать в качестве параметра &m_xFontNotification при инициализации m_fontHeading.

Добавление пользовательских свойств в элемент ActiveX. Пользовательские свойства не реализованы в классе COleControl. Эти свойства обычно используются для выделения определенного состояния ActiveX. Пользовательские свойства осуществляются с помощью четырех способов: 1. Переменной-члена ( member variable );

Здесь состояние свойства представляется как переменная – член класса ActiveX. Этот способ применяется, когда не важно знать, когда свойство меняет значение. В карту диспетчеризации добавляется макрос DISP_PROPERTY.

2. Переменной и оповещения ( member variable with notification ); Реализация этого способа включает не только переменную-член класса, но и функцию, которая будет вызыватся автоматически при изменении свойства. В карту диспетчеризации добавьяется макрос DISP_PROPERTY_NOTIFY.

3. Методов Get/Set; Реализация этого способа включает два метода, добавляемых в класс элемента. Использование этого метода целесообразно, когда пользователь хочет получать и устанавливать значения свойств во время выполнения программы. При этом выполняется проверка устанавливаемого значения свойства, а также возможна реализация свойств, обладающих возможностью только чтения или записи. В карту диспетчеризации добавляется макрос DISP_PROPERTY_EX.

4. С помощью параметров. Этот способ выполняется с помощью ClassWizard. Параметризованное свойство (property array) может быть использовано для доступа к набору значений через одно свойство. В карту диспетчеризации добавляется макрос DISP_PROPERTY_PARAM.

Добавление пользовательских свойств c помощью ClassWizard. Для добавления пользовательского свойства с применением ClassWizard Вы должны выполнить следующие действия: 1. Загрузить Ваш проект. 2. В меню View выбрать ClassWizard. 3. Выбрать вкладку Automation. 4. Выберете имя класса из списка Class name. 5. Щелкнуть на кнопке Add Property.

Page 59: MAKAREVICH mfc ActiveX

6. В списке External Name напечатать нужное свойство, например, MyProperty. 7. В группе Implementation указать Get/Set Methods. 8. В списке Type выбрать тип свойства, например, short. 9. Впечатать имена Get/Set функций или оставить те, что предлагает

ClassWizard. 10. Щелкнуть на кнопке OK для того, чтобы закрыть диалог Add Property. 11. Щелкнуть на кнопке OK для того, чтобы закрыть диалог ClassWizard.

Изменения, внесенные в код Class Wizard. В .H файл класса элемента добавляются строки следующего вида: afx_msg short GetMyProperty(); afx_msg void SetMyProperty (short nNewValue); В файл .ODL добавляется строка: [id(1)] short MyProperty; Здесь свойство получает уникальный идентификатор. В файл .CPP добавляются следующие строки в карту диспетчеризации: DISP_PROPERTY_EX(CMyCtrlCtrl, " MyProperty ", GetMyProperty, SetMyProperty, VT_I2) Также в файле .CPP добавляется реализация методов Get/Set: short CMyCtrlCtrl::GetMyProperty () { return 0; } void CMyCtrlCtrl::SetMyProperty (short nNewValue) { SetModifiedFlag(); } Заметим, что в метод Set добавляется вызов функции SetModifiedFlag(), так как изменение свойства обычно предполагает перерисовку элемента.

Свойства, доступные только для чтения или записи. Для задания доступа к свойству Вы должны выполнить следующие действия: 1. Загрузить Ваш проект. 2. В меню View выбрать ClassWizard. 3. Выбрать вкладку Automation. 4. Выберете имя класса из списка Class name. 5. Щелкнуть на кнопке Add Property. 6. В списке External Name напечатать нужное свойство, например, MyProperty. 7. В группе Implementation указать Get/Set Methods. 8. В списке Type выбрать тип свойства, например, short. 9. Впечатать имена Get/Set функций или оставить те, что предлагает

ClassWizard. Если вы хотите, чтобы свойство только читалось, то удалите имя Set функции, а если Вы желаете, чтобы свойство только устанавливалось извне, то удалите имя Get функции.

10. Щелкнуть на кнопке OK для того, чтобы закрыть диалог Add Property. 11. Щелкнуть на кнопке OK для того, чтобы закрыть диалог ClassWizard.

Page 60: MAKAREVICH mfc ActiveX

При этом в карту диспетчеризации будет вставлена функция SetNotSupported или GetNotSupported: DISP_PROPERTY_EX(CMyCtrlCtrl, "MyProperty", Get MyProperty, SetNotSupported, VT_I2) Если Вы хотите сделать уже существующее свойство только читаемым, например, то вручную внесите изменения в карту диспетчеризации, и также вручную удалите ненужные функции Get или Set. Если же Вы хотите, чтобы свойства были доступны или недоступны в определенных ситуациях, то создайте свойства обычным способом, а при реализации функций Get/Set вызывайте, когда необходимо, функции SetNotSupported или GetNotSupported: void CSampleCtrl::SetMyProperty( short propVal )

{ // Проверка условия для установки свойства if ( m_bReadOnlyMode ) SetNotSupported( );// нельзя устанавливать

else m_ipropVal = propVal; // установка свойства

}

Возврат кодов ошибок при установке свойств. Для индикации ошибок при попытке установить или прочитать значение свойства, использется метод COleControl::ThrowError, которая принимает параметр типа SCODE. Об этом способе сообщалось выше.

Добавление свойства с переменной членом и сообщением об изменении его. Пусть MyProp – свойство, при изменении которого не нужна специальная обработка контрольного элемента. Тогда для его реализации можно использовать способ, связанный только с переменной-членом класса, и с оповещением об изменении свойства. Для добавления свойства MyProp необходимо: 1. Загрузить Ваш проект. 2. В меню View выбрать ClassWizard. 3. Выбрать вкладку Automation. 4. Выберете имя класса из списка Class name. 5. Щелкнуть на кнопке Add Property. 6. В списке External Name напечатать нужное свойство, например, MyProp. 7. В группе Implementation указать Member variable ods. 8. В списке Type выбрать тип свойства, например, short. 9. Проверить, что Notification function содержит OnMyPropChanged, а

Variable name – m_myProp. 10. Щелкнуть на кнопке OK для того, чтобы закрыть диалог Add Property. Заметьте, что в разделе Implementation появился текст: short m_myProp; void OnMyPropChanged(); 11. Щелкнуть на кнопке OK для того, чтобы закрыть диалог ClassWizard. ClassWizard изменяет код и в файле .H, и в файле .ODL, и в файле .CPP. Така наше свойство – переменная- член класса, то в файле .H появилась соответствующие строки:

Page 61: MAKAREVICH mfc ActiveX

short m_myProp; afx_msg void OnMyPropChanged(); В файле .CPP в карте диспетчеризации добавится макрос: DISP_PROPERTY_NOTIFY(CMyCtrlCtrl, "MyProp", m_myProp, OnMyPropChanged, VT_I2) Макрос DISP_PROPERTY_NOTIFY связывает имя свойства MyProp с:

• Переменной класса M_myProp; • Функцией OnMyPropChanged, которая вызывается при изменении

свойства; • Типом VT_I2, который соответствует нашему свойству.

ClassWizard также добавляет определение функции OnMyPropChanged в файл .CPP: void CMyCtrlCtrl::OnMyPropChanged() { // TODO: Add notification handler code SetModifiedFlag(); } В файл .ODL добавляется строка, определяющая уникальный идентификатор свойства: [id(1)] short MyProp; Если Вы не хотите использовать нотификационную функцию, то удалите ее имя в мастере.

Реализация параметрических свойств Параметричекое свойство ( иногда называемое массив свойств) – это способ для работы с набором однородных свойств как с одним свойством. Например, такое свойство можно использовать параметрическое свойство для определения массива. Для реализации параметрического свойства можно использовать вкладку Automation ClassWizard. При этом надо реализовать свойство добавлением метода Get/Set. Количество параметрических свойств для одного элемента ActiveX не должно превышать 15. Ниже описываются действия, которые надо выполнить для добавления к элементу параметрического свойства Array, которое позволяет получить доступ к 2-мерному массиву целых чисел. 1. Загрузить Ваш проект. 2. В меню View выбрать ClassWizard. 3. Выбрать вкладку Automation. 4. Выберете имя класса из списка Class name. 5. Щелкнуть на кнопке Add Property. 6. В списке External Name напечатать нужное свойство, например, Array. 7. В группе Implementation указать Get/Set Methods. 8. В списке Type выбрать тип свойства, например, short.

Page 62: MAKAREVICH mfc ActiveX

9. В элементах Get Function и SetFunction установить имена функций. 10. Используя Parameter List, добавьте параметр row типа short. 11. Используя Parameter List, добавьте второй параметр column типа short. 12. Щелкнуть на кнопке OK для того, чтобы закрыть диалог Add Property. 13. Щелкнуть на кнопке OK для того, чтобы закрыть ClassWizard.

Изменения, сделанные в коде ClassWizard. В .H файл будут внесены строки: afx_msg short GetArray(short row, short column); afx_msg void SetArray(short row, short column, short nNewValue); Код позволяет указать номер колонки и строки для элемента массива. Также следующие строки добавляются в файл .CPP: DISP_PROPERTY_PARAM(CSampleCtrl, "Array", GetArray, SetArray, VT_I2, VTS_I2 VTS_I2) Для разумного применения этого свойства следует объявить двумерный массив типа short в классе элемента, чтобы сохранять или передавать значения свойств. Функции Get и Set должны быть, естественно, модифицированы, чтобы работать со значениями, соответствующими указанным индексам.

Использование картинок в ActiveX. Картинки (Picture) включают метафайлы, битмапы (bitmap), иконки, которые позволяют пользователю нарисовать картинку в ActiveX элементе. Свойство Picture реализовано с использованием объекта picture и методов Get/Set. Доступ к свойству может быть реализован и через страницу свойств Picture. Для реализации работы с картинками можно использовать следующее:

• Класс CPictureHolder. Этот класс обеспечивает легкий доступ к объекту picture, а также применение страницы свойств для свойства Picture.

• Поддержка структуры LPPICTUREDISP, используемой методами Get/Set. Используя ClassWizard, Вы сможете быстро добавить свойства для применения картинок.

• Страницу свойств для манипулирования свойствами Picture. Когда Вы выполните все действия, описанные ниже, Вы сможете в контрольном элементе выводить картинки, выбранные пользователем. Вы сможете просматривать картинки для выбора.

Дейсвия, которые Вы должны выполнить, подобны тем, что Вы выполняли для добавления пользовательских свойств, но тип для свойства должен быть Picture. Кроме того, необходимо сделать добавления к коду ActiveX.

Изменение кода. Для добавления страницы свойств вставьте следующую строку после макроса BEGIN_PROPPAGEIDS в файле (.CPP): PROPPAGEID(CLSID_CPicturePropPage) Вы должны также изменить число страниц в макросе BEGIN_PROPPAGEIDS: BEGIN_PROPPAGEIDS(CmyCtrlCtrl, 2) Для того, чтобы добавить CPictureHolder объект в класс элемента, Вы долюны вставить следующие строки в файл (.H): CPictureHolder m_pic;

Page 63: MAKAREVICH mfc ActiveX

Конечно имя переменной типа CPictureHolder может отличаться от m_pic. Теперь добавьте свойство, используя ClassWizard.

Добавление пользовательского свойства для отображения картинок. Для этого Вы должны выполнить следующие действия: 1. Загрузить Ваш проект. 2. В меню View выбрать ClassWizard. 3. Выбрать вкладку Automation. 4. Выберете имя класса из списка Class name. 5. Щелкнуть на кнопке Add Property. 6. В списке External Name напечатать нужное свойство, например,

ControlPicture. 7. В группе Implementation указать Get/Set Methods. 8. В списке Type выбрать тип свойства LPPICTUREDISP. 9. В элементах Get Function и SetFunction установить имена функций. 10. Щелкнуть на кнопке OK для того, чтобы закрыть диалог Add Property. 11. Щелкнуть на кнопке OK для того, чтобы закрыть ClassWizard. ClassWizard добавит код в файл (.H): afx_msg LPPICTUREDISP GetControlPicture(); afx_msg void SetControlPicture(LPPICTUREDISP newValue); Также будут внесены изменения в код (.CPP) файла: DISP_PROPERTY_EX(CMyCtrlCtrl, "ControlPicture", GetControlPicture, SetControlPicture, VT_PICTURE) – в карту диспетчеризации и LPPICTUREDISP СMyCtrlCtrl::GetControlPicture() { // TODO: Add your property handler here return NULL; } void CMyCtrlCtrl::SetControlPicture(LPPICTUREDISP newValue) { // TODO: Add your property handler here SetModifiedFlag(); } Имена класса и методов могут, естественно, отличаться от указанных.

Изменения, которые Вы должны внести в проект. В методе OnResetState, принадлежащем Вашему элементу, добавьте вызов после строки COleControl::OnResetState: m_pic.CreateEmpty(); Этот вызов задает пустую картинку в элемент контроля. Для отрисовки картинки вызовите CPictureHolder::Render в методе OnDraw. Посмотрите пример кода ниже: void CMyCtrlCtrl::OnDraw( CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)

Page 64: MAKAREVICH mfc ActiveX

{ // ****** Добавьте пожобный код ********** // m_pic.Render(pdc, rcBounds, rcBounds); } В метод GetControlPicture добавьте строку: return m_pic.GetPictureDispatch(); В метод SetControlPicture добавьте строки: m_pic.SetPictureDispatch(newValue); InvalidateControl(); Добавьте строку в метод DoPropExchange: PX_Picture(pPX, "ControlPicture",m_pic); Перестройте проект и посмотрите, что получится. В странице свойств кнопка Обзор позволит выбрать для отображения любую картинку. Кнопка Очистить уберет картинку.

Доступ к свойствам контейнера. Контрольный элемент может получать информацию о свойствах контейнера. Эти свойства могут содержать важную для отображения информацию, например, цвет фона контейнера, шрифт, используемый в контейнере. Элемент ActiveX может использовать эти свойства для своего отображения. Следует заметить, что некоторые контейнеры могут не поддерживать указанные свойства, поэтому элемент ActiveX должен иметь какое-то значение по умолчанию. Для доступа к свойствам контейнера можно использовать метод класса COleControl::GetAmbientProperty: BOOL GetAmbientProperty( DISPID dwDispid, VARTYPE vtProp, void* pvProp ); Первый параметр этого метода – идентификатор для свойства контейнера. Стандартные свойства контейнера приводятся в файле OLECTL.H: #define DISPID_AMBIENT_BACKCOLOR (-701) #define DISPID_AMBIENT_DISPLAYNAME (-702) #define DISPID_AMBIENT_FONT (-703) #define DISPID_AMBIENT_FORECOLOR (-704) #define DISPID_AMBIENT_LOCALEID (-705) #define DISPID_AMBIENT_MESSAGEREFLECT (-706) #define DISPID_AMBIENT_SCALEUNITS (-707) #define DISPID_AMBIENT_TEXTALIGN (-708) #define DISPID_AMBIENT_USERMODE (-709) #define DISPID_AMBIENT_UIDEAD (-710) #define DISPID_AMBIENT_SHOWGRABHANDLES (-711) #define DISPID_AMBIENT_SHOWHATCHING (-712) #define DISPID_AMBIENT_DISPLAYASDEFAULT (-713) #define DISPID_AMBIENT_SUPPORTSMNEMONICS (-714) #define DISPID_AMBIENT_AUTOCLIP (-715) #define DISPID_AMBIENT_APPEARANCE (-716) #define DISPID_AMBIENT_CODEPAGE (-725) #define DISPID_AMBIENT_PALETTE (-726) #define DISPID_AMBIENT_CHARSET (-727) #define DISPID_AMBIENT_TRANSFERPRIORITY (-728) #define DISPID_AMBIENT_RIGHTTOLEFT (-732)

Page 65: MAKAREVICH mfc ActiveX

#define DISPID_AMBIENT_TOPTOBOTTOM (-733) Второй параметр функции GetAmbientProperty определяет ожидаемый тип свойства, а третий – адрес, по которому будет записано значение. При успешном выполнении и поддержке свойства контейнером функция возвращает TRUE, в противном случае –FALSE. Ниже приводится фрагмент кода для получения свойства контейнера UserMode: BOOL bUserMode; if( !GetAmbientProperty( DISPID_AMBIENT_USERMODE, VT_BOOL, &bUserMode ) ) bUserMode = TRUE; Класс COleControl содержит методы для доступа к часто используемым свойствам контейнера (таблица 5):

Таблица 5. Некоторые функции для получения свойств контейнера.

Функция Назначение OLE_COLOR AmbientBackColor() Возвращает значение фонового цвета окружения. CString AmbientDisplayName() Возвращает имя элемента в контейнере.

OLE_COLOR AmbientForeColor() Возвращает значение цвета переднего плана окружения.

LPFONTDISP AmbientFont() Возвращает значение шрифта окружения.

short AmbientTextAlign()

Возвращает тип тектового выравнивания в контейнере: 0 – обычное выравнивание; 1 – по левому краю; 2 – по центру; 3 – по правому краю.

BOOL AmbientUIDead()

Возвращает, должен ли элемент реагировать на действия пользователя: не 0 , если элемент должен реагировать на действия пользователя

BOOL AmbientUserMode() Возвращает режим контейнера: 0-режим конструирования, не 0 – рабочий режим.

BOOL GetAmbientProperty(DISPID dwDispid, VARTYPEvtProp, void* pvProp );

Возвращает значение указанного как параметр свойства окружения.

Если свойство контейнера изменяется, то вызывается функция COleControl::OnAmbientPropertyChanged. Переопределяя эту функцию, Вы можете определить свои действия. Параметр этой функции определяет свойство, которое изменилось.

Составление страниц свойств для элементов ActiveX. Страницы свойств позволяют пользователю элемента ActiveX посмотреть и изменить свойства элемента. Эти свойства доступны через вызов диалога, содержащего несколько закладок для удобного задания свойств.

Page 66: MAKAREVICH mfc ActiveX

Реализация страницы свойств по умолчанию Если Вы используете ControlWizard для создания элемента ActiveX, то страница свойств по умолчанию создается мастером. Вначале страница пуста, но в нее можно добавить любые элементы. Все дополнительные страницы должны быть созданы вручную с помощью ClassWizard. При этом все страницы строятся на основе базового класса ColePropertyPage. Для построения страницы по умолчанию: 1. Используйте редактор диалогов для добавления любых элементов в диалог,

созданный мастером. Для этого откройте закладку ResourceView в Project Workspace

2. Дважды щелкните мышью на элементе Dialog. 3. Откройте диалог с идентификатором IDD_PROPPAGE_имяэлемента. 4. Выберете элемент из Control Palette и перенесите его в область диалога.

Укажите идентификатор для него, например, IDC_ELEMENT. 5. Вызовите ClassWizard и на вкладке MemberVariables для указанного диалога

добавьте переменную для элемента IDC_ELEMENT нужного типа. При этом обязательно свяжите переменную со свойством в Optional Property Name.

В метод DoDataExchange страницы свойств добавится нужный вызов функции DDP_ в зависимости от типа переменной: DDP_Text(pDX, IDC_ELEMENT, m_caption, _T("Caption")); Существует несколько функций DDP_, соответствующих различным типам элементов в панели свойств (таблица 6).

Таблица 6. Функции DDP_.

Имя функции Описание DDP_CBIndex Передача целых значений поля со списком. DDP_CBString Передача строковых значений поля со списком. DDP_CBStringExact Передача строковых значений поля со списком. DDP_Check Флажок. DDP_LBIndex Передача целых значений окна списка. DDP_LBString Передача строковых значений окна списка. DDP_LBStringExact Передача строковых значений окна списка. DDP_Radio Передача целых значений переключателя. DDP_Text Текст из элементов управления.

Добавление другой страницы свойств. ActiveX может иметь свойств больше, чем помещается на обну страницу. В этом случае вы можете добавить несколько дополнительных страниц. Ниже рассматривается добавление страниц свойств, если есть уже хотя бы одна страница. Замечание: Cтрого рекомендуется, чтобы размеры страниц не менялись. Они составляют 250х62 единицы для базовой страницы и страницы цветов и 250х110 единиц для страницы шрифтов. Страница по умолчанию, созданная Сontrol Wizard, имеет размер 250х62 единицы. Если Вы измените размер, то каждый раз при открытии свойств будет выдаваться сообщение о несоответствии размеров.

Page 67: MAKAREVICH mfc ActiveX

Для того, чтобы создать дополнительную страницу Вы должны выполнить следующие действия: 1. Выбрать закладку ResourceView в Project Workspace. 2. В меню Insert щелкнуть на строке Resource. 3. Выбрать ресурс Dialog для создания нового диалога и дважды щелкнуть

мышкой. 4. Удалить кнопки OK и Cancel. 5. Щелкнуть правой кнопкой мыши и откройте окно Dialog Properties.

Впечатайте идентификатор диалога, например, IDD_PROPPAGE_NEWPAGE. 6. Выбрать вкладку Styles, из выпадающего списка Styles выбрать Child. из

выпадающего списка Border выбрать None. Убедиться, что опция Titlebar не включена, а на закладке More Styles опция Visible не включена.

7. Дважды щелкнуть кнопкой мыши на окне диалога для запуска ClassWizard. 8. Добавить новый класс в диалоге Adding a Class. 9. Ввести имя нового класса в диалоге New Class в элементе Class Name. 10. Для изменения имена файлов можно щелкнуть на кнопке Change. 11. В качестве базового класса Вы должны выбрать ColePropertyPage в списке

Base Class. 12. В элементе Dialog ID выбрать IDD_PROPPAGE_NEWPAGE. 13. Щелкнуть на OK, чтобы создать класс. Можно закрыть ClassWizard. Чтобы пользователи контрольного элемента получили доступ к новой странице, сделайте следующие изменения в файле реализации контрольного элемента: BEGIN_PROPPAGEIDS(CMyCtrlCtrl, 2) PROPPAGEID(CMyCtrlPropPage::guid) PROPPAGEID(CAddtlPropPage::guid) ... END_PROPPAGEIDS(CMyCtrlCtrl) Здесь CAddtlPropPage – имя нового класса. В макросе BEGIN_PROPPAGEIDS измените второй параметр (счетчик страниц) , увеличив его на 1. В этот же файл реализации элемента включите h-файл нового класса страницы. На следующем шаге добавьте два новых строковых ресурса, которые обеспечат имя и заголовок для новой страницы свойств. 1. Откройте вкладку ResourceView. 2. Дважды щелкните на папке String Table. 3. Щелкните на пустой строке в правой части окна и впечатайте заголовок

строки, например, Новая страница. При этом откроется окно свойств, в котором в элементе Caption появится введенная строка. В элементе ID впечатайте обозначение идентификатора, например, IDS_ADDPAGE. Нажмите кнопку OK.

4. Повторите пункт 3 для вставки строки с заголовком для страницы, например, Дополнительная страница, с идентификатором IDS_ADDPPG_CAPTION.

5. В файле .CPP Вашего нового класса (например, CAddtlPropPage) измените CAddtlPropPage::CAddtlPropPageFactory::UpdateRegistry так, чтобы в функцию AfxOleRegisterPropertyPageClass передавался в качестве параметра идентификатор диалога-страницы свойств:

BOOL CAddtlPropPage::CAddtlPropPageFactory::UpdateRegistry(BOOL bRegister) { if (bRegister)

Page 68: MAKAREVICH mfc ActiveX

return AfxOleRegisterPropertyPageClass(AfxGetInstanceHandle(), m_clsid, IDS_ADDPAGE); else return AfxOleUnregisterClass(m_clsid, NULL); }

6. Измените конструктор нового класса CAddtlPropPage так, чтобы IDS_ADDPPG_CAPTION передавался в COlePropertyPage конструктор:

CAddtlPropPage::CAddtlPropPage() : // ****** Add your code below this line ********** // COlePropertyPage(IDD, IDS_ADDPPG_CAPTION) // ****** Add your code above this line ********** // { //{{AFX_DATA_INIT(CAddtlPropPage) // NOTE: ClassWizard will add member initialization here // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_DATA_INIT }

Теперь Вы можете перестроить проект и протестировать элемент в Test Container, чтобы увидеть новую страницу свойств.

Использование базовых страниц свойств. MFC обеспечивает три базовых страницы для использования с ActiveX: CLSID_CColorPropPage, CLSID_CFontPropPage, и CLSID_CpicturePropPage. Эти страницы позволяют пользователю изменить базовый цвет, шрифт и свойства картинки, соответственно. Чтобы встроить эти страницы в элемент ActiveX, добавьте их ID в код, инициализирующий массив страниц свойств (Добавленные строки показаны жирным шрифтом):

BEGIN_PROPPAGEIDS( CMyCtrlCtrl, 4 ) PROPPAGEID( CMyCtrlPropPage::guid PROPPAGEID( CLSID_CFontPropPage )

)

PROPPAGEID( CLSID_CColorPropPage ) PROPPAGEID( CLSID_CPicturePropPage ) END_PROPPAGEIDS(CSampleCtrl) Отметьте, что счетчик страниц в макросе BEGIN_PROPPAGEIDS – 4 и соответствует числу страниц.

Перестроив проект, Вы можете увидеть дополнительные страницы для задания свойств элемента.

Test Container Приложение Test Container, поставляемое с Visual C++, предназначено для тестирования и отладки ActiveX. Test Container позволяет пользователю проверить, как элемент позволяет менять свойства, как выполняет методы и пересылает события в контейнер. Для тестирования Вашего элемента ActiveX : 1. В меню Tools DevStudio выберете ActiveX Control Test Container. 2. В меню Edit в оболочке Test Container выберете Insert New Control.

Page 69: MAKAREVICH mfc ActiveX

3. В списке Insert Control выберете желаемый элемент ActiveX и щелкните на кнопке OK. Элементе появится в контейнере.

Замечание: Если элемент отсутствует в списке, то он не зарегистрирован в системе. Для регистрации элемента можно использовать команду Register Controls из меню File оболочки Test Container. Для отладки элемента ActiveX с помощью отладчика: 1. Постройте отладочную версию элемента. 2. Из меню Build выберете команду Settings. 3. В появившемся диалоге Project Settings выберете вкладку Debug. 4. В элемент редактирования Executable for Debug Session впечатайте

TstCon32.exe – имя программы ActiveX Control Test Container. 5. Щелкните OK. После этого Test Container будет запускаться автоматически при запуске проекта в режиме отладки. Вы можете задавать точки останова, смотреть значения переменных и т. п. Для тестирования свойств:

1. В меню Control, щелкните Invoke Methods. 2. В списке Method Name выберете PropPut method для свойства, которое

Вы хотите протестировать. 3. Измените Parameter Value или Parameter Type и щелкните Set Value

кнопку. 4. Щелкните Invoke, чтобы установить новое значение.

Для тестирования событий: 1. В меню Options щелкните Logging. 2. Укажите, куда направлять события. Если Вы направляете события в файл

(Log to file), то сможете посмотреть затем их появление в файле. Можно направить события в окно отладки (Log to debugger window) или в окно, находящееся в нижней части главного окна ActiveX Control Test Container (Log to output window). Вы можете запретить просмотр событий (No logging).

Для тестирования методов: 1. В меню Control, щелкните Invoke Methods. 2. В списке Method Name выберете метод, который Вы хотите

протестировать. 3. Измените Parameter Value или Parameter Type и кнопку Invoke, чтобы

выполнить метод. Для проверки работы со страницами свойств щелкните на Properties в меню Edit. Задания для самостоятельной работы. Разработайте элемент ActiveX, оттестируйте его в Test Container, разработайте приложение, используя созданный ActiveX. Покажите возможность задания свойств ActiveX, проиилюстрируйте изменение свойств при использовании элементов во время работы приложения, покажите возможность получать события от ActiveX и обрабатывать их в приложении, покажите использование методов ActiveX. Покажите реализацию обмена данными межлу ActiveX и приложением. Продемонстрируйте применение разработанного ActiveX в Internet-технологиях.

Page 70: MAKAREVICH mfc ActiveX

1. Создать элемент ActiveX - календарь критических дней по циклам ( эмоциональному, интеллектуальному, физическому ).

2. Создать элемент ActiveX - цифровые часы, позволяющие задавать время.. 3. Создать элемент ActiveX - стрелочные часы, позволяющие задавать время.. 4. Создать элемент ActiveX - spin, позволяющий работать с числами в

диапазоне от 0 до 0xffffffff. 5. Создать элемент ActiveX - бегущая строка. 6. Создать элемент ActiveX – календарь на текущий месяц. 7. Создать элемент ActiveX – позволяющий просматривать текст, выполняя

его прокрутку. 8. Создать элемент ActiveX – индикатор длительного процесса, похожий на

Progress bar. 9. Создать элемент ActiveX – список, позволяющий вставлять любое

количество элементов, осуществлять выбор элементов. Предусмотреть собственный внешний вид списка.

10. Создать элемент ActiveX –показывающий погоду на сутки. 11. Создать любой оригинальный элемент ActiveX.

Page 71: MAKAREVICH mfc ActiveX

Литература 1. Нортон П., МакГрегор Р. Руководство Питера Нортона.

Программирование в Windows 95/NT4 с помощью MFC. В 2-х книгах. Книга 2. - М.:”СК Пресс”,1998 – 560 с.

2. Тосс В. Visual C++5. Энциклопедия пользователя. - К.:”ДиаСофт”, 1998 – 688 с.

3. Грегори К. Использование Visual C++ 5. Специальное издание. - К.:Диалектика, 1997 – 816 с.

4. Джон Пьюполо. OLE:создание элементов управления. - К.:Издательская группа BHV, 1997 – 432 c.

5. Дэвид Чеппел. Технология ActiveX и OLE. - М.:Русская редакция, 1997 – 320 с.

6. MSDN Library Visual Studio Release. Microsoft, 1999. Электронная версия библиотеки для разработчиков.

Приложение 1.

Элемент ActiveX MyCtrl

Файл MyCtrl.h #if !defined(AFX_MYCTRL_H__49E2F385_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED_) #define AFX_MYCTRL_H__49E2F385_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 // MyCtrl.h : main header file for MYCTRL.DLL #if !defined( __AFXCTL_H__ ) #error include 'afxctl.h' before including this file #endif #include "resource.h" // main symbols ///////////////////////////////////////////////////////////////////////////// // CMyCtrlApp : See MyCtrl.cpp for implementation. class CMyCtrlApp : public COleControlModule { public: BOOL InitInstance(); int ExitInstance();

Page 72: MAKAREVICH mfc ActiveX

}; extern const GUID CDECL _tlid; extern const WORD _wVerMajor; extern const WORD _wVerMinor; //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_MYCTRL_H__49E2F385_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED)

Файл MyCtrl.cpp // MyCtrl.cpp : Implementation of CMyCtrlApp and DLL registration. #include "stdafx.h" #include "MyCtrl.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif CMyCtrlApp NEAR theApp; const GUID CDECL BASED_CODE _tlid = { 0x49e2f37b, 0xb48d, 0x11d3, { 0xa2, 0xb0, 0, 0xa0, 0xc9, 0x55, 0xfa, 0x9e } }; const WORD _wVerMajor = 1; const WORD _wVerMinor = 0; //////////////////////////////////////////////////////////////////////////// // CMyCtrlApp::InitInstance - DLL initialization BOOL CMyCtrlApp::InitInstance() { BOOL bInit = COleControlModule::InitInstance(); if (bInit) { // TODO: Add your own module initialization code here. }

Page 73: MAKAREVICH mfc ActiveX

return bInit; } //////////////////////////////////////////////////////////////////////////// // CMyCtrlApp::ExitInstance - DLL termination int CMyCtrlApp::ExitInstance() { // TODO: Add your own module termination code here. return COleControlModule::ExitInstance(); } ///////////////////////////////////////////////////////////////////////////// // DllRegisterServer - Adds entries to the system registry STDAPI DllRegisterServer(void) { AFX_MANAGE_STATE(_afxModuleAddrThis); if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid)) return ResultFromScode(SELFREG_E_TYPELIB); if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE)) return ResultFromScode(SELFREG_E_CLASS); return NOERROR; } ///////////////////////////////////////////////////////////////////////////// // DllUnregisterServer - Removes entries from the system registry STDAPI DllUnregisterServer(void) { AFX_MANAGE_STATE(_afxModuleAddrThis); if (!AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor)) return ResultFromScode(SELFREG_E_TYPELIB); if (!COleObjectFactoryEx::UpdateRegistryAll(FALSE)) return ResultFromScode(SELFREG_E_CLASS); return NOERROR; }

Page 74: MAKAREVICH mfc ActiveX

Файл MyCtrlCtrl.h #if !defined(AFX_MYCTRLCTL_H__49E2F38D_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED_) #define AFX_MYCTRLCTL_H__49E2F38D_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 // MyCtrlCtl.h : Declaration of the CMyCtrlCtrl ActiveX Control class. ///////////////////////////////////////////////////////////////////////////// // CMyCtrlCtrl : See MyCtrlCtl.cpp for implementation. class CMyCtrlCtrl : public COleControl { DECLARE_DYNCREATE(CMyCtrlCtrl) // Constructor public: CMyCtrlCtrl(); // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CMyCtrlCtrl) public: virtual void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid); virtual void DoPropExchange(CPropExchange* pPX); virtual void OnResetState(); //}}AFX_VIRTUAL BOOL m_bSelected; short m_nShape; OLE_COLOR m_selectColor; OLE_COLOR m_textColor; long m_timeSleep; UINT idTimer; BOOL timerOn; ULONG m_tick; protected: ~CMyCtrlCtrl(); DECLARE_OLECREATE_EX(CMyCtrlCtrl) // Class factory and guid DECLARE_OLETYPELIB(CMyCtrlCtrl) // GetTypeInfo DECLARE_PROPPAGEIDS(CMyCtrlCtrl) // Property page IDs

Page 75: MAKAREVICH mfc ActiveX

DECLARE_OLECTLTYPE(CMyCtrlCtrl) // Type name and misc status // Message maps //{{AFX_MSG(CMyCtrlCtrl) afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnTimer(UINT nIDEvent); afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); afx_msg void OnClose(); //}}AFX_MSG DECLARE_MESSAGE_MAP() // Dispatch maps //{{AFX_DISPATCH(CMyCtrlCtrl) afx_msg short GetShape(); afx_msg void SetShape(short nNewValue); afx_msg BOOL GetSelected(); afx_msg OLE_COLOR GetSelectColor(); afx_msg void SetSelectColor(OLE_COLOR nNewValue); afx_msg OLE_COLOR GetTextColor(); afx_msg void SetTextColor(OLE_COLOR nNewValue); afx_msg long GetSleep(); afx_msg void SetSleep(long nNewValue); afx_msg BOOL DoChangeTimer(); //}}AFX_DISPATCH DECLARE_DISPATCH_MAP() afx_msg void AboutBox(); // Event maps //{{AFX_EVENT(CMyCtrlCtrl) void FireSelect(BOOL IsSelected) {FireEvent(eventidSelect,EVENT_PARAM(VTS_BOOL), IsSelected);} void FireTick(long ltick) {FireEvent(eventidTick,EVENT_PARAM(VTS_I4), ltick);} //}}AFX_EVENT DECLARE_EVENT_MAP() // Dispatch and event IDs public: enum { //{{AFX_DISP_ID(CMyCtrlCtrl) dispidShape = 1L, dispidSelected = 2L, dispidSelectColor = 3L, dispidTextColor = 4L, dispidSleep = 5L, dispidDoChangeTimer = 6L,

Page 76: MAKAREVICH mfc ActiveX

eventidSelect = 1L, eventidTick = 2L, //}}AFX_DISP_ID }; }; //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_MYCTRLCTL_H__49E2F38D_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED)

Файл MyCtrlCtl.cpp // MyCtrlCtl.cpp : Implementation of the CMyCtrlCtrl ActiveX Control class. #include "stdafx.h" #include "MyCtrl.h" #include "MyCtrlCtl.h" #include "MyCtrlPpg.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif IMPLEMENT_DYNCREATE(CMyCtrlCtrl, COleControl) ///////////////////////////////////////////////////////////////////////////// // Message map BEGIN_MESSAGE_MAP(CMyCtrlCtrl, COleControl) //{{AFX_MSG_MAP(CMyCtrlCtrl) ON_WM_LBUTTONDOWN() ON_WM_TIMER() ON_WM_CREATE() ON_WM_CLOSE() //}}AFX_MSG_MAP ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties) END_MESSAGE_MAP() /////////////////////////////////////////////////////////////////////////////

Page 77: MAKAREVICH mfc ActiveX

// Dispatch map BEGIN_DISPATCH_MAP(CMyCtrlCtrl, COleControl) //{{AFX_DISPATCH_MAP(CMyCtrlCtrl) DISP_PROPERTY_EX(CMyCtrlCtrl, "Shape", GetShape, SetShape, VT_I2) DISP_PROPERTY_EX(CMyCtrlCtrl, "Selected", GetSelected, SetNotSupported, VT_BOOL) DISP_PROPERTY_EX(CMyCtrlCtrl, "SelectColor", GetSelectColor, SetSelectColor, VT_COLOR) DISP_PROPERTY_EX(CMyCtrlCtrl, "TextColor", GetTextColor, SetTextColor, VT_COLOR) DISP_PROPERTY_EX(CMyCtrlCtrl, "Sleep", GetSleep, SetSleep, VT_I4) DISP_FUNCTION(CMyCtrlCtrl, "DoChangeTimer", DoChangeTimer, VT_BOOL, VTS_NONE) DISP_DEFVALUE(CMyCtrlCtrl, "Shape") DISP_STOCKPROP_FORECOLOR() DISP_STOCKPROP_CAPTION() DISP_STOCKPROP_FONT() //}}AFX_DISPATCH_MAP DISP_FUNCTION_ID(CMyCtrlCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE) END_DISPATCH_MAP() ///////////////////////////////////////////////////////////////////////////// // Event map BEGIN_EVENT_MAP(CMyCtrlCtrl, COleControl) //{{AFX_EVENT_MAP(CMyCtrlCtrl) EVENT_CUSTOM("Select", FireSelect, VTS_BOOL) EVENT_CUSTOM("Tick", FireTick, VTS_I4) //}}AFX_EVENT_MAP END_EVENT_MAP() ///////////////////////////////////////////////////////////////////////////// // Property pages // TODO: Add more property pages as needed. Remember to increase the count! BEGIN_PROPPAGEIDS(CMyCtrlCtrl, 3) PROPPAGEID(CMyCtrlPropPage::guid) PROPPAGEID(CLSID_CColorPropPage) PROPPAGEID(CLSID_CFontPropPage) END_PROPPAGEIDS(CMyCtrlCtrl) ///////////////////////////////////////////////////////////////////////////// // Initialize class factory and guid

Page 78: MAKAREVICH mfc ActiveX

IMPLEMENT_OLECREATE_EX(CMyCtrlCtrl, "MYCTRL.MyCtrlCtrl.1", 0x49e2f37f, 0xb48d, 0x11d3, 0xa2, 0xb0, 0, 0xa0, 0xc9, 0x55, 0xfa, 0x9e) ///////////////////////////////////////////////////////////////////////////// // Type library ID and version IMPLEMENT_OLETYPELIB(CMyCtrlCtrl, _tlid, _wVerMajor, _wVerMinor) ///////////////////////////////////////////////////////////////////////////// // Interface IDs const IID BASED_CODE IID_DMyCtrl = { 0x49e2f37d, 0xb48d, 0x11d3, { 0xa2, 0xb0, 0, 0xa0, 0xc9, 0x55, 0xfa, 0x9e } }; const IID BASED_CODE IID_DMyCtrlEvents = { 0x49e2f37e, 0xb48d, 0x11d3, { 0xa2, 0xb0, 0, 0xa0, 0xc9, 0x55, 0xfa, 0x9e } }; ///////////////////////////////////////////////////////////////////////////// // Control type information static const DWORD BASED_CODE _dwMyCtrlOleMisc = OLEMISC_ACTIVATEWHENVISIBLE | OLEMISC_SETCLIENTSITEFIRST | OLEMISC_INSIDEOUT | OLEMISC_CANTLINKINSIDE | OLEMISC_RECOMPOSEONRESIZE; IMPLEMENT_OLECTLTYPE(CMyCtrlCtrl, IDS_MYCTRL, _dwMyCtrlOleMisc) ///////////////////////////////////////////////////////////////////////////// // CMyCtrlCtrl::CMyCtrlCtrlFactory::UpdateRegistry - // Adds or removes system registry entries for CMyCtrlCtrl BOOL CMyCtrlCtrl::CMyCtrlCtrlFactory::UpdateRegistry(BOOL bRegister) { // TODO: Verify that your control follows apartment-model threading rules. // Refer to MFC TechNote 64 for more information. // If your control does not conform to the apartment-model rules, then // you must modify the code below, changing the 6th parameter from // afxRegApartmentThreading to 0. if (bRegister) return AfxOleRegisterControlClass( AfxGetInstanceHandle(),

Page 79: MAKAREVICH mfc ActiveX

m_clsid, m_lpszProgID, IDS_MYCTRL, IDB_MYCTRL, afxRegApartmentThreading, _dwMyCtrlOleMisc, _tlid, _wVerMajor, _wVerMinor); else return AfxOleUnregisterClass(m_clsid, m_lpszProgID); } ///////////////////////////////////////////////////////////////////////////// // CMyCtrlCtrl::CMyCtrlCtrl - Constructor CMyCtrlCtrl::CMyCtrlCtrl() { InitializeIIDs(&IID_DMyCtrl, &IID_DMyCtrlEvents); m_bSelected = FALSE; m_nShape = 0; SetForeColor(RGB(0,255,0)); m_timeSleep = 1000; m_tick = 0; } ///////////////////////////////////////////////////////////////////////////// // CMyCtrlCtrl::~CMyCtrlCtrl - Destructor CMyCtrlCtrl::~CMyCtrlCtrl() { } ///////////////////////////////////////////////////////////////////////////// // CMyCtrlCtrl::OnDraw - Drawing function void CMyCtrlCtrl::OnDraw( CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid) { CPen pen; CBrush foreBrush, backBrush; CPoint points[3]; pdc->SaveDC(); pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); ULONG foreColor = TranslateColor(GetForeColor());

Page 80: MAKAREVICH mfc ActiveX

backBrush.CreateSolidBrush(TranslateColor(AmbientBackColor())); foreBrush.CreateSolidBrush(GetSelected() ? foreColor : TranslateColor(m_selectColor)); pdc->FillRect(rcBounds, &backBrush); pdc->SelectObject(&pen); pdc->SelectObject(&foreBrush); switch (m_nShape) { case 0: pdc->Ellipse(rcBounds); break; case 1: pdc->Rectangle(rcBounds); break; case 2: points[0].x = rcBounds.left; points[0].y = rcBounds.bottom - 1; points[1].x = (rcBounds.left + rcBounds.right - 1) / 2; points[1].y = rcBounds.top; points[2].x = rcBounds.right - 1; points[2].y = rcBounds.bottom - 1; pdc->Polygon(points, 3); break; } CFont *pOldFont; TEXTMETRIC tm; pdc->SetTextColor(TranslateColor(m_textColor)); pdc->SetBkMode(TRANSPARENT); CString strCaption = GetText(); pOldFont = SelectStockFont(pdc); pdc->GetTextMetrics(&tm); pdc->SetTextAlign(TA_CENTER|TA_TOP); pdc->ExtTextOut((rcBounds.left + rcBounds.right)/2,(rcBounds.top + rcBounds.bottom - tm.tmHeight)/2, ETO_CLIPPED, rcBounds, strCaption, strCaption.GetLength(),NULL); CString strTick; strTick.Format("%d",m_tick); pdc->ExtTextOut((rcBounds.left + rcBounds.right)/2,(rcBounds.top + tm.tmHeight), ETO_CLIPPED, rcBounds, strTick, strTick.GetLength(),NULL); pdc->RestoreDC(-1); }

Page 81: MAKAREVICH mfc ActiveX

///////////////////////////////////////////////////////////////////////////// // CMyCtrlCtrl::DoPropExchange - Persistence support void CMyCtrlCtrl::DoPropExchange(CPropExchange* pPX) { ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor)); COleControl::DoPropExchange(pPX); PX_Short(pPX, _T("Shape"), m_nShape, 0); PX_Long(pPX, _T("SelectColor"), (long&) m_selectColor, RGB(255,0,0)); PX_Long(pPX, _T("TextColor"), (long&) m_textColor, RGB(0,0,0)); PX_Long(pPX, _T("Sleep"), m_timeSleep, 1000); } ///////////////////////////////////////////////////////////////////////////// // CMyCtrlCtrl::OnResetState - Reset control to default state void CMyCtrlCtrl::OnResetState() { COleControl::OnResetState(); // Resets defaults found in DoPropExchange // TODO: Reset any other control state here. } ///////////////////////////////////////////////////////////////////////////// // CMyCtrlCtrl::AboutBox - Display an "About" box to the user void CMyCtrlCtrl::AboutBox() { CDialog dlgAbout(IDD_ABOUTBOX_MYCTRL); dlgAbout.DoModal(); } ///////////////////////////////////////////////////////////////////////////// // CMyCtrlCtrl message handlers short CMyCtrlCtrl::GetShape() { return m_nShape; } void CMyCtrlCtrl::SetShape(short nNewValue)

Page 82: MAKAREVICH mfc ActiveX

{ m_nShape = nNewValue; SetModifiedFlag(); InvalidateControl(); } BOOL CMyCtrlCtrl::GetSelected() { return m_bSelected; } void CMyCtrlCtrl::OnLButtonDown(UINT nFlags, CPoint point) { COleControl::OnLButtonDown(nFlags, point); m_bSelected = !m_bSelected; InvalidateControl(); FireSelect(m_bSelected); } OLE_COLOR CMyCtrlCtrl::GetSelectColor() { return m_selectColor; } void CMyCtrlCtrl::SetSelectColor(OLE_COLOR nNewValue) { m_selectColor = nNewValue; SetModifiedFlag(); InvalidateControl(); } OLE_COLOR CMyCtrlCtrl::GetTextColor() { return m_textColor; } void CMyCtrlCtrl::SetTextColor(OLE_COLOR nNewValue) { m_textColor = nNewValue; SetModifiedFlag(); InvalidateControl(); } long CMyCtrlCtrl::GetSleep()

Page 83: MAKAREVICH mfc ActiveX

{ return m_timeSleep; } void CMyCtrlCtrl::SetSleep(long nNewValue) { m_timeSleep = nNewValue; SetModifiedFlag(); } void CMyCtrlCtrl::OnTimer(UINT nIDEvent) { m_tick++; FireTick(m_tick); SetModifiedFlag(); InvalidateControl(); COleControl::OnTimer(nIDEvent); } #define ID_TIMER 1001 int CMyCtrlCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (COleControl::OnCreate(lpCreateStruct) == -1) return -1; idTimer = SetTimer( ID_TIMER, m_timeSleep, NULL ); timerOn = FALSE; return 0; } void CMyCtrlCtrl::OnClose() { if ( timerOn ) { KillTimer( idTimer ); timerOn =FALSE; } CWnd::OnClose(); } BOOL CMyCtrlCtrl::DoChangeTimer() { if ( timerOn ) { KillTimer( idTimer ); timerOn =FALSE; }

Page 84: MAKAREVICH mfc ActiveX

else { idTimer = SetTimer( ID_TIMER, m_timeSleep, NULL ); timerOn =TRUE; } return timerOn; }

Файл MyCtrlPpg.h #if !defined(AFX_MYCTRLPPG_H__49E2F38F_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED_) #define AFX_MYCTRLPPG_H__49E2F38F_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 // MyCtrlPpg.h : Declaration of the CMyCtrlPropPage property page class. //////////////////////////////////////////////////////////////////////////// // CMyCtrlPropPage : See MyCtrlPpg.cpp.cpp for implementation. class CMyCtrlPropPage : public COlePropertyPage { DECLARE_DYNCREATE(CMyCtrlPropPage) DECLARE_OLECREATE_EX(CMyCtrlPropPage) // Constructor public: CMyCtrlPropPage(); // Dialog Data //{{AFX_DATA(CMyCtrlPropPage) enum { IDD = IDD_PROPPAGE_MYCTRL }; int m_nShape; CString m_Caption; CString m_timeSleep; //}}AFX_DATA // Implementation protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support // Message maps

Page 85: MAKAREVICH mfc ActiveX

protected: //{{AFX_MSG(CMyCtrlPropPage) // NOTE - ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG DECLARE_MESSAGE_MAP() }; //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_MYCTRLPPG_H__49E2F38F_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED)

Файл MyCtrlPpg.cpp // MyCtrlPpg.cpp : Implementation of the CMyCtrlPropPage property page class. #include "stdafx.h" #include "MyCtrl.h" #include "MyCtrlPpg.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif IMPLEMENT_DYNCREATE(CMyCtrlPropPage, COlePropertyPage) ///////////////////////////////////////////////////////////////////////////// // Message map BEGIN_MESSAGE_MAP(CMyCtrlPropPage, COlePropertyPage) //{{AFX_MSG_MAP(CMyCtrlPropPage) // NOTE - ClassWizard will add and remove message map entries // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // Initialize class factory and guid

Page 86: MAKAREVICH mfc ActiveX

IMPLEMENT_OLECREATE_EX(CMyCtrlPropPage, "MYCTRL.MyCtrlPropPage.1", 0x49e2f380, 0xb48d, 0x11d3, 0xa2, 0xb0, 0, 0xa0, 0xc9, 0x55, 0xfa, 0x9e) ///////////////////////////////////////////////////////////////////////////// // CMyCtrlPropPage::CMyCtrlPropPageFactory::UpdateRegistry - // Adds or removes system registry entries for CMyCtrlPropPage BOOL CMyCtrlPropPage::CMyCtrlPropPageFactory::UpdateRegistry(BOOL bRegister) { if (bRegister) return AfxOleRegisterPropertyPageClass(AfxGetInstanceHandle(), m_clsid, IDS_MYCTRL_PPG); else return AfxOleUnregisterClass(m_clsid, NULL); } ///////////////////////////////////////////////////////////////////////////// // CMyCtrlPropPage::CMyCtrlPropPage - Constructor CMyCtrlPropPage::CMyCtrlPropPage() : COlePropertyPage(IDD, IDS_MYCTRL_PPG_CAPTION) { //{{AFX_DATA_INIT(CMyCtrlPropPage) m_nShape = -1; m_Caption = _T(""); m_timeSleep = _T(""); //}}AFX_DATA_INIT } ///////////////////////////////////////////////////////////////////////////// // CMyCtrlPropPage::DoDataExchange - Moves data between page and properties void CMyCtrlPropPage::DoDataExchange(CDataExchange* pDX) { //{{AFX_DATA_MAP(CMyCtrlPropPage) DDP_CBIndex(pDX, IDC_CBSHAPE, m_nShape, _T("Shape") ); DDX_CBIndex(pDX, IDC_CBSHAPE, m_nShape); DDP_Text(pDX, IDC_CAPTION, m_Caption, _T("Caption") ); DDX_Text(pDX, IDC_CAPTION, m_Caption); DDP_Text(pDX, IDC_SLEEP, m_timeSleep, _T("Sleep") ); DDX_Text(pDX, IDC_SLEEP, m_timeSleep); //}}AFX_DATA_MAP DDP_PostProcessing(pDX); }

Page 87: MAKAREVICH mfc ActiveX

///////////////////////////////////////////////////////////////////////////// // CMyCtrlPropPage message handlers

Файл MyCtrl.odl // MyCtrl.odl : type library source for ActiveX Control project. // This file will be processed by the Make Type Library (mktyplib) tool to // produce the type library (MyCtrl.tlb) that will become a resource in // MyCtrl.ocx. #include <olectl.h> #include <idispids.h> [ uuid(49E2F37B-B48D-11D3-A2B0-00A0C955FA9E), version(1.0), helpfile("MyCtrl.hlp"), helpstring("MyCtrl ActiveX Control module"), control ] library MYCTRLLib { importlib(STDOLE_TLB); importlib(STDTYPE_TLB); // Primary dispatch interface for CMyCtrlCtrl [ uuid(49E2F37D-B48D-11D3-A2B0-00A0C955FA9E), helpstring("Dispatch interface for MyCtrl Control"), hidden ] dispinterface _DMyCtrl { properties: // NOTE - ClassWizard will maintain property information here. // Use extreme caution when editing this section. //{{AFX_ODL_PROP(CMyCtrlCtrl) [id(1)] short Shape; [id(0)] short _Shape; [id(2)] boolean Selected; [id(DISPID_FORECOLOR), bindable, requestedit] OLE_COLOR ForeColor; [id(3)] OLE_COLOR SelectColor; [id(DISPID_CAPTION), bindable, requestedit] BSTR Caption; [id(DISPID_FONT), bindable] IFontDisp* Font; [id(4)] OLE_COLOR TextColor; [id(5)] long Sleep; //}}AFX_ODL_PROP methods:

Page 88: MAKAREVICH mfc ActiveX

// NOTE - ClassWizard will maintain method information here. // Use extreme caution when editing this section. //{{AFX_ODL_METHOD(CMyCtrlCtrl) [id(6)] boolean DoChangeTimer(); //}}AFX_ODL_METHOD [id(DISPID_ABOUTBOX)] void AboutBox(); }; // Event dispatch interface for CMyCtrlCtrl [ uuid(49E2F37E-B48D-11D3-A2B0-00A0C955FA9E), helpstring("Event interface for MyCtrl Control") ] dispinterface _DMyCtrlEvents { properties: // Event interface has no properties methods: // NOTE - ClassWizard will maintain event information here. // Use extreme caution when editing this section. //{{AFX_ODL_EVENT(CMyCtrlCtrl) [id(1)] void Select(boolean IsSelected); [id(2)] void Tick(long ltick); //}}AFX_ODL_EVENT }; // Class information for CMyCtrlCtrl [ uuid(49E2F37F-B48D-11D3-A2B0-00A0C955FA9E), helpstring("MyCtrl Control"), control ] coclass MyCtrl { [default] dispinterface _DMyCtrl; [default, source] dispinterface _DMyCtrlEvents; }; //{{AFX_APPEND_ODL}} //}}AFX_APPEND_ODL}} };

Приложение TestMyCtrl

Файл TestMyCtrl.h // TestMyCtrl.h : main header file for the TESTMYCTRL application //

Page 89: MAKAREVICH mfc ActiveX

#if !defined(AFX_TESTMYCTRL_H__80E61EA5_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_) #define AFX_TESTMYCTRL_H__80E61EA5_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #ifndef __AFXWIN_H__ #error include 'stdafx.h' before including this file for PCH #endif #include "resource.h" // main symbols ///////////////////////////////////////////////////////////////////////////// // CTestMyCtrlApp: // See TestMyCtrl.cpp for the implementation of this class // class CTestMyCtrlApp : public CWinApp { public: CTestMyCtrlApp(); // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CTestMyCtrlApp) public: virtual BOOL InitInstance(); //}}AFX_VIRTUAL // Implementation //{{AFX_MSG(CTestMyCtrlApp) // NOTE - the ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG DECLARE_MESSAGE_MAP() }; ///////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}}

Page 90: MAKAREVICH mfc ActiveX

// Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_TESTMYCTRL_H__80E61EA5_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_)

Файл TestMyCtrl.cpp // TestMyCtrl.cpp : Defines the class behaviors for the application. // #include "stdafx.h" #include "TestMyCtrl.h" #include "TestMyCtrlDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CTestMyCtrlApp BEGIN_MESSAGE_MAP(CTestMyCtrlApp, CWinApp) //{{AFX_MSG_MAP(CTestMyCtrlApp) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG ON_COMMAND(ID_HELP, CWinApp::OnHelp) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CTestMyCtrlApp construction CTestMyCtrlApp::CTestMyCtrlApp() { // TODO: add construction code here, // Place all significant initialization in InitInstance } ///////////////////////////////////////////////////////////////////////////// // The one and only CTestMyCtrlApp object CTestMyCtrlApp theApp; ///////////////////////////////////////////////////////////////////////////// // CTestMyCtrlApp initialization

Page 91: MAKAREVICH mfc ActiveX

BOOL CTestMyCtrlApp::InitInstance() { AfxEnableControlContainer(); // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need. #ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL #else Enable3dControlsStatic(); // Call this when linking to MFC statically #endif CTestMyCtrlDlg dlg; m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); if (nResponse == IDOK) { // TODO: Place code here to handle when the dialog is // dismissed with OK } else if (nResponse == IDCANCEL) { // TODO: Place code here to handle when the dialog is // dismissed with Cancel } // Since the dialog has been closed, return FALSE so that we exit the // application, rather than start the application's message pump. return FALSE; }

Файл TestMyCtrlDlg.h // TestMyCtrlDlg.h : header file // //{{AFX_INCLUDES() #include "myctrl.h" //}}AFX_INCLUDES #if !defined(AFX_TESTMYCTRLDLG_H__80E61EA7_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_)

Page 92: MAKAREVICH mfc ActiveX

#define AFX_TESTMYCTRLDLG_H__80E61EA7_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 ///////////////////////////////////////////////////////////////////////////// // CTestMyCtrlDlg dialog class CTestMyCtrlDlg : public CDialog { // Construction public: CTestMyCtrlDlg(CWnd* pParent = NULL); // standard constructor // Dialog Data //{{AFX_DATA(CTestMyCtrlDlg) enum { IDD = IDD_TESTMYCTRL_DIALOG }; CString m_Message; CMyCtrl m_MyCtrl; CString m_newText; CString m_Tick; //}}AFX_DATA // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CTestMyCtrlDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected: HICON m_hIcon; // Generated message map functions //{{AFX_MSG(CTestMyCtrlDlg) virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); afx_msg void OnSelectMyctrl(BOOL IsSelected); afx_msg void OnTickMyctrl(long ltick); afx_msg void OnText(); afx_msg void OnTimer(); afx_msg void OnShape(); DECLARE_EVENTSINK_MAP()

Page 93: MAKAREVICH mfc ActiveX

//}}AFX_MSG DECLARE_MESSAGE_MAP() }; //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_TESTMYCTRLDLG_H__80E61EA7_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_)

Файл TestMyCtrlDlg.cpp // TestMyCtrlDlg.cpp : implementation file // #include "stdafx.h" #include "TestMyCtrl.h" #include "TestMyCtrlDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CAboutDlg dialog used for App About class CAboutDlg : public CDialog { public: CAboutDlg(); // Dialog Data //{{AFX_DATA(CAboutDlg) enum { IDD = IDD_ABOUTBOX }; //}}AFX_DATA // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CAboutDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected:

Page 94: MAKAREVICH mfc ActiveX

//{{AFX_MSG(CAboutDlg) //}}AFX_MSG DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { //{{AFX_DATA_INIT(CAboutDlg) //}}AFX_DATA_INIT } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAboutDlg) //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //{{AFX_MSG_MAP(CAboutDlg) // No message handlers //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CTestMyCtrlDlg dialog CTestMyCtrlDlg::CTestMyCtrlDlg(CWnd* pParent /*=NULL*/) : CDialog(CTestMyCtrlDlg::IDD, pParent) { //{{AFX_DATA_INIT(CTestMyCtrlDlg) m_Message = _T(""); m_newText = _T(""); m_Tick = _T(""); //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CTestMyCtrlDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CTestMyCtrlDlg) DDX_Text(pDX, IDC_MESSAGE, m_Message); DDX_Control(pDX, IDC_MYCTRL, m_MyCtrl); DDX_Text(pDX, IDC_NEWTEXT, m_newText); DDX_Text(pDX, IDC_TICK, m_Tick); //}}AFX_DATA_MAP }

Page 95: MAKAREVICH mfc ActiveX

BEGIN_MESSAGE_MAP(CTestMyCtrlDlg, CDialog) //{{AFX_MSG_MAP(CTestMyCtrlDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_TEXT, OnText) ON_BN_CLICKED(IDC_TIMER, OnTimer) ON_BN_CLICKED(IDC_SHAPE, OnShape) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CTestMyCtrlDlg message handlers BOOL CTestMyCtrlDlg::OnInitDialog() { CDialog::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here return TRUE; // return TRUE unless you set the focus to a control } void CTestMyCtrlDlg::OnSysCommand(UINT nID, LPARAM lParam)

Page 96: MAKAREVICH mfc ActiveX

{ if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialog::OnSysCommand(nID, lParam); } } // If you add a minimize button to your dialog, you will need the code below // to draw the icon. For MFC applications using the document/view model, // this is automatically done for you by the framework. void CTestMyCtrlDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { CDialog::OnPaint(); } } // The system calls this to obtain the cursor to display while the user drags // the minimized window. HCURSOR CTestMyCtrlDlg::OnQueryDragIcon() { return (HCURSOR) m_hIcon; }

Page 97: MAKAREVICH mfc ActiveX

BEGIN_EVENTSINK_MAP(CTestMyCtrlDlg, CDialog) //{{AFX_EVENTSINK_MAP(CTestMyCtrlDlg) ON_EVENT(CTestMyCtrlDlg, IDC_MYCTRL, 1 /* Select */, OnSelectMyctrl, VTS_BOOL) ON_EVENT(CTestMyCtrlDlg, IDC_MYCTRL, 2 /* Tick */, OnTickMyctrl, VTS_I4) //}}AFX_EVENTSINK_MAP END_EVENTSINK_MAP() void CTestMyCtrlDlg::OnSelectMyctrl(BOOL IsSelected) { if ( IsSelected ) m_Message = "Âûáðàí"; else m_Message = "Íå âûáðàí"; UpdateData(FALSE); } void CTestMyCtrlDlg::OnShape() { short shape = m_MyCtrl.Get_Shape(); if ( shape < 2 ) m_MyCtrl.Set_Shape(++shape); else m_MyCtrl.Set_Shape(0); } void CTestMyCtrlDlg::OnText() { UpdateData(TRUE); m_MyCtrl.SetCaption(m_newText); } void CTestMyCtrlDlg::OnTimer() { m_MyCtrl.DoChangeTimer(); } void CTestMyCtrlDlg::OnTickMyctrl(long ltick) { m_Tick.Format("%d", ltick); CWnd * pwnd = GetDlgItem(IDC_TICK); pwnd->SetWindowText(m_Tick); }

Page 98: MAKAREVICH mfc ActiveX

Файл MyCtrl.h #if !defined(AFX_MYCTRL_H__80E61EB0_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_) #define AFX_MYCTRL_H__80E61EB0_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 // Machine generated IDispatch wrapper class(es) created by Microsoft Visual C++ // NOTE: Do not modify the contents of this file. If this class is regenerated by // Microsoft Visual C++, your modifications will be overwritten. // Dispatch interfaces referenced by this interface class COleFont; ///////////////////////////////////////////////////////////////////////////// // CMyCtrl wrapper class class CMyCtrl : public CWnd { protected: DECLARE_DYNCREATE(CMyCtrl) public: CLSID const& GetClsid() { static CLSID const clsid = { 0x49e2f37f, 0xb48d, 0x11d3, { 0xa2, 0xb0, 0x0, 0xa0, 0xc9, 0x55, 0xfa, 0x9e } }; return clsid; } virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL) { return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID); } BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CFile* pPersist = NULL, BOOL bStorage = FALSE, BSTR bstrLicKey = NULL)

Page 99: MAKAREVICH mfc ActiveX

{ return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID, pPersist, bStorage, bstrLicKey); } // Attributes public: short GetShape(); void SetShape(short); short Get_Shape(); void Set_Shape(short); BOOL GetSelected(); void SetSelected(BOOL); OLE_COLOR GetForeColor(); void SetForeColor(OLE_COLOR); unsigned long GetSelectColor(); void SetSelectColor(unsigned long); CString GetCaption(); void SetCaption(LPCTSTR); COleFont GetFont(); void SetFont(LPDISPATCH); unsigned long GetTextColor(); void SetTextColor(unsigned long); long GetSleep(); void SetSleep(long); // Operations public: void DoClick(); BOOL DoChangeTimer(); void AboutBox(); }; //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_MYCTRL_H__80E61EB0_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_)

Файл MyCtrl.cpp // Machine generated IDispatch wrapper class(es) created by Microsoft Visual C++ // NOTE: Do not modify the contents of this file. If this class is regenerated by // Microsoft Visual C++, your modifications will be overwritten. #include "stdafx.h"

Page 100: MAKAREVICH mfc ActiveX

#include "myctrl.h" // Dispatch interfaces referenced by this interface #include "font.h" ///////////////////////////////////////////////////////////////////////////// // CMyCtrl IMPLEMENT_DYNCREATE(CMyCtrl, CWnd) ///////////////////////////////////////////////////////////////////////////// // CMyCtrl properties short CMyCtrl::GetShape() { short result; GetProperty(0x1, VT_I2, (void*)&result); return result; } void CMyCtrl::SetShape(short propVal) { SetProperty(0x1, VT_I2, propVal); } short CMyCtrl::Get_Shape() { short result; GetProperty(0x0, VT_I2, (void*)&result); return result; } void CMyCtrl::Set_Shape(short propVal) { SetProperty(0x0, VT_I2, propVal); } BOOL CMyCtrl::GetSelected() { BOOL result; GetProperty(0x2, VT_BOOL, (void*)&result); return result; } void CMyCtrl::SetSelected(BOOL propVal) { SetProperty(0x2, VT_BOOL, propVal); }

Page 101: MAKAREVICH mfc ActiveX

OLE_COLOR CMyCtrl::GetForeColor() { OLE_COLOR result; GetProperty(DISPID_FORECOLOR, VT_I4, (void*)&result); return result; } void CMyCtrl::SetForeColor(OLE_COLOR propVal) { SetProperty(DISPID_FORECOLOR, VT_I4, propVal); } unsigned long CMyCtrl::GetSelectColor() { unsigned long result; GetProperty(0x3, VT_I4, (void*)&result); return result; } void CMyCtrl::SetSelectColor(unsigned long propVal) { SetProperty(0x3, VT_I4, propVal); } CString CMyCtrl::GetCaption() { CString result; GetProperty(DISPID_CAPTION, VT_BSTR, (void*)&result); return result; } void CMyCtrl::SetCaption(LPCTSTR propVal) { SetProperty(DISPID_CAPTION, VT_BSTR, propVal); } COleFont CMyCtrl::GetFont() { LPDISPATCH pDispatch; GetProperty(DISPID_FONT, VT_DISPATCH, (void*)&pDispatch); return COleFont(pDispatch); } void CMyCtrl::SetFont(LPDISPATCH propVal) { SetProperty(DISPID_FONT, VT_DISPATCH, propVal); } unsigned long CMyCtrl::GetTextColor()

Page 102: MAKAREVICH mfc ActiveX

{ unsigned long result; GetProperty(0x4, VT_I4, (void*)&result); return result; } void CMyCtrl::SetTextColor(unsigned long propVal) { SetProperty(0x4, VT_I4, propVal); } long CMyCtrl::GetSleep() { long result; GetProperty(0x5, VT_I4, (void*)&result); return result; } void CMyCtrl::SetSleep(long propVal) { SetProperty(0x5, VT_I4, propVal); } ///////////////////////////////////////////////////////////////////////////// // CMyCtrl operations void CMyCtrl::DoClick() { InvokeHelper(DISPID_DOCLICK, DISPATCH_METHOD, VT_EMPTY, NULL, NULL); } BOOL CMyCtrl::DoChangeTimer() { BOOL result; InvokeHelper(0x6, DISPATCH_METHOD, VT_BOOL, (void*)&result, NULL); return result; } void CMyCtrl::AboutBox() { InvokeHelper(0xfffffdd8, DISPATCH_METHOD, VT_EMPTY, NULL, NULL); }

Page 103: MAKAREVICH mfc ActiveX

Оглавление Макаревич Л. Г.............................................................................................................1 ActiveX с нуля ..............................................................................................................1 Практическое руководство .........................................................................................1 Введение .......................................................................................................................2 Контрольные элементы ActiveX.............................................................................3

Построение элементов ActiveX ..................................................................................4 Использование MFC ................................................................................................4 Постановка задачи ...................................................................................................5 Построение остова для элемента MyCtrl ...............................................................5 Добавление кода в файлы, созданные мастером. .................................................7 Изменение растрового изображения элемента управления................................8 Изменение диалога “About…” для элемента управления. ...................................9 Добавление свойств пользователя к элементу управления. ..............................10 Устойчивость свойства..........................................................................................12 Добавление событий..............................................................................................13 Рисование элемента управления. .........................................................................15 Добавление интерфейса окна свойств. ................................................................16 Связывание страницы свойств со свойствами элемента управления. ..............17 Создание файла типа MyCtrl.ocx для элемента MyCtrl......................................18 Тестирование элемента управления.....................................................................18 Распространение элемента управления. ..............................................................19 Добавление новых свойств в элемент управления MyCtrl. ...............................20 Добавление базового свойства. ........................................................................20 Установка значения базового свойства по умолчанию..................................20 Добавление пользовательского свойства цвета. .............................................20 Изменение метода Draw. ...................................................................................21 Добавление страницы базовых свойств...........................................................22 Добавление текста в элемент управления. ......................................................22 Добавление страницы свойств для шрифта. ...................................................23 Добавление элемента для задания текста. .......................................................23

Изменение метода OnDraw. ..................................................................................24 Добавление элемента управления в приложение. ..................................................26 Добавление взаимодействия элемента управления и контейнера. ...................27

Добавление новых свойств, методов и событий в элемент ActiveX MyCtrl........30 Добавление методов в элемент ActiveX. .........................................................32 Добавление события, посылаемого в контейнер. ...........................................33 Тестирование элемента MyCtrl в ActiveX Control Test Container. .............34 Изменение приложения с использованием элемента ActiveX. .....................34

Общие принципы построения ActiveX с помощью MFC ......................................35 Построение остова приложения ...........................................................................36 Базовые компоненты ActiveX...............................................................................37 Взаимодействие между контрольными элементами и контейнерами. .............37 Активное и неактивное состояния Active X........................................................38 Главный класс модуля ActiveX. ...........................................................................39 Объявление класса .............................................................................................39 Методы класса....................................................................................................39 Регистрация элемента управления ...................................................................39

Page 104: MAKAREVICH mfc ActiveX

Макрос AFX_MANAGE_STATE(p).................................................................40 Класс элемента ActiveX.........................................................................................41 Объявление класса .............................................................................................41 Конструктор и деструктор класса ....................................................................41 Методы класса....................................................................................................41 Поддержка элементов управления ...................................................................42 Идентификаторы интерфейсов (IID) автоматизации .....................................43 Таблица сообщений класса ...............................................................................43 Схема диспетчеризации класса ........................................................................44 Схема диспетчеризации событий класса .........................................................44 Идентификаторы DISPID..................................................................................45

Класс страницы свойств элемента управления ActiveX. ...................................45 Объявление класса .............................................................................................45 Стандартные элементы управления страницы свойств .................................45 Редактирование записей системного реестра для класса страницы свойств..............................................................................................................................46

Добавление событий, методов и свойств для элемента ActiveX...........................46 События. .................................................................................................................46 Добавление базовых событий в контрольный элемент..................................46 Изменения, внесенные Class Wizard в код. .....................................................48 Добавление пользовательских событий в контрольный элемент. ................48 Добавление пользовательского события с помощью ClassWizard. ..............48 Изменения, внесенные в код ClassWizard. ......................................................49 Вызов функции FireClickIn. ..............................................................................49 Пользовательские события с базовыми именами. ..........................................50

Методы ActiveX. ....................................................................................................50 Добавление базовых методов к контрольному элементу. .............................51 Изменения, внесенные в код при вставке базовых методов..........................51 Добавление пользовательских методов к контрольному элементу. .............51 Изменения в коде при добавлении пользовательских методов. ...................52 Возврат кодов ошибок из методов. ..................................................................52

Задание свойств для ActiveX. ...............................................................................53 Добавление базовых свойств к контрольному элементу. ..............................54 Изменения в коде, внесенные ClassWizard......................................................54 Свойства определения цветов...........................................................................55 Использование шрифтов в элементах ActiveX. ..............................................55 Использование базового свойства шрифтов в элементах ActiveX. ..............55 Использование пользовательского свойства шрифта. ...................................56 Как изменить код при использовании пользовательского свойства шрифта...............................................................................................................................57 Обработка нотификационных сообщений при работе со шрифтом. ............58 Добавление пользовательских свойств в элемент ActiveX............................58 Добавление пользовательских свойств c помощью ClassWizard. .................58 Изменения, внесенные в код Class Wizard. .....................................................59 Свойства, доступные только для чтения или записи. ....................................59 Возврат кодов ошибок при установке свойств. ..............................................60 Добавление свойства с переменной членом и сообщением об изменении его. .......................................................................................................................60 Реализация параметрических свойств .............................................................61

Page 105: MAKAREVICH mfc ActiveX

Изменения, сделанные в коде ClassWizard......................................................62 Использование картинок в ActiveX. ................................................................62 Изменение кода. .................................................................................................62 Добавление пользовательского свойства для отображения картинок..........63 Изменения, которые Вы должны внести в проект..........................................63 Доступ к свойствам контейнера. ......................................................................64

Составление страниц свойств для элементов ActiveX.......................................65 Реализация страницы свойств по умолчанию.................................................66 Добавление другой страницы свойств. ...........................................................66 Использование базовых страниц свойств........................................................68

Test Container..............................................................................................................68 Задания для самостоятельной работы......................................................................69 Литература ..................................................................................................................71 Приложение 1. ............................................................................................................71 Элемент ActiveX MyCtrl........................................................................................71 Файл MyCtrl.h.....................................................................................................71 Файл MyCtrl.cpp.................................................................................................72 Файл MyCtrlCtrl.h ..............................................................................................74 Файл MyCtrlCtl.cpp ............................................................................................76 Файл MyCtrlPpg.h .............................................................................................84 Файл MyCtrlPpg.cpp...........................................................................................85 Файл MyCtrl.odl..................................................................................................87

Приложение TestMyCtrl ........................................................................................88 Файл TestMyCtrl.h..............................................................................................88 Файл TestMyCtrl.cpp ..........................................................................................90 Файл TestMyCtrlDlg.h........................................................................................91 Файл TestMyCtrlDlg.cpp ....................................................................................93 Файл MyCtrl.h.....................................................................................................98 Файл MyCtrl.cpp.................................................................................................99

Оглавление ...............................................................................................................103