Изменения, которые Вы должны внести в проект.
В методе OnResetState, принадлежащем Вашему элементу, добавьте вызов после строки COleControl::OnResetState:
m_pic.CreateEmpty();
Этот вызов задает пустую картинку в элемент контроля.
Для отрисовки картинки вызовите CPictureHolder::Render
в методе OnDraw. Посмотрите пример кода ниже:
void CMyCtrlCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
// ****** Добавьте пожобный код ********** //
m_pic.Render(pdc, rcBounds, rcBounds);
}
В метод GetControlPicture
добавьте строку:
return m_pic.GetPictureDispatch();
В метод SetControlPicture
добавьте строки:
m_pic.SetPictureDispatch(newValue);
InvalidateControl();
Добавьте строку в метод DoPropExchange:
PX_Picture(pPX, "ControlPicture",m_pic);
Перестройте проект и посмотрите, что получится. В странице свойств кнопка Обзор позволит выбрать для отображения любую картинку. Кнопка Очистить уберет картинку.
Изменения, сделанные в коде 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 должны быть, естественно, модифицированы, чтобы работать со значениями, соответствующими указанным индексам.
Изменения в коде при добавлении пользовательских методов.
В .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;
}
Изменения в коде, внесенные ClassWizard.
Поскольку базовые свойства уже реализованы в COleControl, код добавляется только в .СPP файл и .ODL файл.
В карту диспетчеризации вставляется строка вида
DISP_STOCKPROP_CAPTION()
DISP_STOCKPROP_FONT(),
в зависимости от добавленного свойства.
Следующая строка добавляется в файл .ODL:
[id(DISPID_CAPTION), bindable, requestedit] BSTR Caption;
[id(DISPID_FONT), bindable] IFontDisp* Font;
в зависимости от добавленного свойства.
Строки назначают идентификаторы свойствам. Теперь выбранные свойства доступны для пользователей. Для этого просто можно использовать методы и данные класса COleControl.
С большинством базовых свойств связаны функции, которые вызываются при их изменении. Например, при изменении свойства BackColor вызывается метод OnBackColor. По умолчанию реализация метода заключается в вызове InvalidateControl. Вы можете, конечно, добавить дополнительные действия.
Изменения, внесенные 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 его стандартным идентификатором и позволяет контейнеру ожидать этого события.
Изменения, внесенные в код 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(), так как изменение свойства обычно предполагает перерисовку элемента.
Изменения, внесенные в код 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.
Изменения, внесенные в код при вставке базовых методов.
Поскольку базовые методы поддерживаются в классе COleControl, то изменения вносятся только в .CPP и .ODL файлы.
В файл .CPP вставляется строка в карту диспетчеризации:
DISP_STOCKFUNC_REFRESH()
В файл .ODL добавляется строка, назначающая методу Refresh идентификатор:
[id(DISPID_REFRESH)] void Refresh();
Как изменить код при использовании пользовательского свойства шрифта.
Вы должны изменить код вручную, чтобы добавленное Вами свойство использовалось правильно.
В файл (.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)
Вы должны также изменить число страниц в макросе BEGIN_PROPPAGEIDS:
BEGIN_PROPPAGEIDS(CMyCtrlCtrl, 2)
Класс страницы свойств элемента управления ActiveX.
Страницы свойств - это OLE-объекты, которые дают пользователю возможность просматривать и изменять свойства элемента управления OLE. Страницы свойств содержат один или несколько стандартных диалоговых элементов управления, связанных с различными свойствами. Страницы свойств отображаются в виде модальных или немодальных диалоговых окон с закладками, причем каждой странице соответствует своя закладка.
По умолчанию средство MFC ActiveX ControlWizard создает одну страницу свойств, за работу которой в рассматриваемом случае OLE-элемента управления отвечает класс CMyCtrlPropPpg.
Конструктор и деструктор класса
В конструкторе класса CMyCtrlCtrl происходит вызов функции InitializeIIDs. Эта функция должна вызываться только в конструкторе элемента управления. Она сообщает базовому классу идентификаторы интерфейсов (IID) элемента управления. В данном случае класс COleControl
получает идентификатор базового интерфейса автоматизации и идентификатор интерфейса диспетчеризации.
Конструктор класса в своем первоначальном виде ничего не делает, но в него можно по мере необходимости добавить код, очищающий все данные экземпляра элемента управления.
Контрольные элементы ActiveX
ActiveX строится на основе COM. ActiveX используются как строительные блоки при создании пользовательского интерфейса. Сам элемент всегда реализуется внутри сервеpa. Сервер является динамически подключаемой библиотекой (DLL), подгружаемой во время работы приложения, и находится в файле с расширением .OCX.
Итак, ActiveX – это OLE-сервер, который может быть использован в любом OLE - контейнере. Заметим, что использование ActiveX возможно только внутри процесса OLE – контейнера, поскольку элементы ActiveX представляют собой обычные библиотеки DLL и выполняются в рамках клиентского процесса, который их использует. Правда, сейчас почти все приложения реализуют свойства OLE – контейнера.
При этом контрольные элементы должны обеспечивать следующую функциональность:
Свойства и методы
Контрольный элемент обеспечивает часть своих возможностей через набор интерфейсов, обеспечивающих свойства и методы. Контейнер может передавать свои свойства в контрольный элемент.
События
Контрольный элемент также может использовать интерфейсы для того, чтобы информировать клиента о событиях, связанных с элементом. Клиент может реагировать на эти события.
Визуальное представление
Контрольный элемент может поддерживать позиционирование и размещение в контейнере. Контейнер позиционирует контрольный элемент и определяет его размер.
Управление клавиатурой
Контрольный элемент может реагировать на клавиатурные комбинации, так что он может обрабатывать ввод от пользователя. Контейнер должен управлять клавиатурным вводом для всех встроенных контрольных элементов.
Сохранение состояния
Контрольный элемент может сохранять свое состояние. Клиент управляет сохранением состояния встроенных элементов.
Регистрация и лицензирование.
Контрольный элемент обычно поддерживает саморегистрацию на машине, где он запускается, и прописывает необходимую информацию в реестр. Контрольный элемент может быть пролицензирован для того, чтобы запретить неразрешенное использование.
Некоторые из указанных черт касаются и элементов 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. Электронная версия библиотеки для разработчиков.
Макрос AFX_MANAGE_STATE(p)
Макрос AFX_MANAGE_STATE
встречается в функциях DllRegisterServer
и DllUnregisterServer. Этот макрос необходим некоторым методам управления OLE для выполнения переключения состояния модуля.
Для начала уточним понятие “состояние модуля”. Во время выполнения MFC-программы библиотека хранит информацию о состоянии приложения. Эта информация включает дескрипторы окон, таблицы связей дескрипторов и MFC-объектов и др. Если выполняется только базовый модуль (DLL-файлы не подгружаются), то может существовать лишь один набор данных о состоянии.
Однако при подключении DLL-файлов, использующих библиотеку MFC (как в случае элементов ActiveX), могут возникать проблемы. Каждый элемент управления сохраняет свою информацию о состоянии. Когда поток выполнения “входит” в элемент управления, последний должен сообщить MFC о том, что наступила его очередь выполняться и следует изменить указатель на набор данных о состоянии так, чтобы он указывал на набор данных элемента управления. Элемент управления делает это с помощью макроса AFX_MANAGE_STATE. Такая переадресация должна происходить во всех точках входа в DLL.
Поскольку все элементы ActiveX экспортируют функции DllRegisterServer
и DllUnregisterServer, то не удивительно, что первым оператором в теле обеих функций является макрос AFX_MANAGE_STATE. Если не выполнить изменение состояния, результат работы программы может быть непредсказуемым.
Методы 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()
При создании элемента с помощью ControlWizard нужные строки добавляются автоматически.
Методы класса
Файл 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-объекты и т.д:
Класс CMyCtrlCtrl переопределяет некоторые методы базового класса: OnDraw, DoPropExchange, OnResetState. Метод OnDraw вызывается операционной системой, когда элемент управления должен быть перерисован.
Вызов метода DoPropExchange
происходит, когда элемент управления записывается или считывается из постоянного хранилища (с диска). Эта функция отвечает за сохранение (запись на диск) и восстановление (чтение с диска) информации о свойствах элемента и его версии.
Вызов метода OnResetState
осуществляет система, когда элемент управления должен сбросить свои свойства (установить их в первоначальные значения). Если ранее сохраненный элемент управления не может быть правильно воссоздан в памяти (то есть не могут быть правильно восстановлены значения его свойств), то система дает указание вновь созданному элементу управления (в который помещаются данные, считанные с диска) присвоить своим свойствам значения, имеющие хоть какой-то смысл, т.е. значения по умолчанию.
Объявление класса
Файл MyCtrl.h
является основным файлом заголовков для элемента управления MyCtrl, в нем объявляется класс CMyCtrlApp. Этот класс является потомком класса COleControlModule, а не класса CWinApp. Это справедливо обычно для всех элементов управления, построенных на основе MFC.
Класс же COleControlModule
библиотеки MFC в свою очередь просто является производным от класса CWinApp, в котором для удобства переопределены методы InitInstance
и ExitInstance.
Класс OLE-элемента управления объявлен в файле MyCtrlCtl.h и реализуется в файле MyCtrlCtl.cpp. Базовый класс элемента управления наследуется от класса COleControl - стандартного базового класса библиотеки MFC, который используется элементами управления ActiveX. Этот класс, будучи потомком класса CWnd, наследует все функции объекта окна, а также располагает дополнительными, специфическими для OLE функциями, такими как генерация событий и поддержка методов и свойств OLE-элемента управления.
В классе COleControl
реализованы все интерфейсы, необходимые для работы элемента управления. Он скрывает детали реализации элемента и имеет понятный, объектно-ориентированный интерфейс, который используется элементами управления при выполнении собственных функций.
Класс страницы свойств CMyCtrlPropPage объявляется в файле MyCtrlPpg.h. Этот класс сгенерирован при создании проекта. Он реализует одну пустую страницу свойств. Этот класс является прямым потомком класса COlePropertyPage, базового класса всех страниц свойств.
Класс CMyCtrlPropPage является OLE-объектом. Поэтому ему необходима фабрика классов, генерацию которой обеспечивает макрос DECLARE_OLECREATE_EX. Фабрика классов и генерация CLSID для класса страницы свойств реализована в файле MyCtrlPpg.cpp при помощи макроса IMPLEMENT_OLECREATE_EX.
Обработка нотификационных сообщений при работе со шрифтом.
В большинстве случаев контрольный элемент должен знать об изменении характеристик шрифта. Каждый раз при изменении параметров шрифта вызывается функция-член класса COleControl - OnFontChanged. При использовании базового свойства можно применять реализацию этого метода по умолчанию. Если же Вы использовали пользовательское свойство шрифта, Вы должны изменить эту функцию. Например, Для нашего примера надо передать в качестве параметра &m_xFontNotification при инициализации m_fontHeading.
Общие принципы построения ActiveX с помощью MFC
Итак, Вы создали свой первый ActiveX-элемент, встроили его в приложение и, надеюсь, поняли, что построение элемента ActiveX с помощью MFC не очень трудная задача, а использование элементов – совсем просто и удобно.
Давайте рассмотрим построение элементов ActiveX с помощью MFC в общих чертах.
Поддержка элементов управления
Объявление класса 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
в файле реализации класса. Библиотека типов представляет собой файл, содержащий информацию о типе элемента управления, его интерфейсах и элементах автоматизации (к ним относятся свойства, методы и события). Сгенерированный метод GetTypeLib вызывается системой, когда ей нужна информация о библиотеке типов элемента. Эти данные сравниваются с данными из системного реестра, и если обнаруживается совпадение, то информация передается системе.
Элемент управления ActiveX использует страницы свойств, чтобы дать пользователю возможность просматривать и изменять свойства элемента управления. Страницы свойств реализуются в виде диалоговых окон с закладками. В макросе
DECLARE_PROPPAGEIDS(CMyCtrlCtrl) объявляются идентификаторы и методы, используемые этим механизмом.
Макрос DECLARE_OLECTLTYPE(CMyCtrlCtrl) объявляет методы, которые обрабатывают информацию о типе элемента.
Информация о типе элемента управления записана в переменной типа const с именем _dwMyCtrlOleMisc А макрос IMPLEMENT_OLECTLTYPE в реализации класса определяет два метода элемента управления GetUserTypeNameID и GetMiscStatus. Метод GetUserTypeNameID возвращает идентификатор ресурса строки (константа IDS_Name), содержащей имя типа элемента управления. Метод GetMiscStatus
вызывается внешними объектами, например контейнером, для получения информации об элементе управления. Значения, записанные в переменной _dwNameOleMisc, генерируются при создании проекта и являются выбором установок проекта.
Пользовательские события с базовыми именами.
Вы можете реализовать пользовательские события с именами базовых, но нельзя реализовать с одним именем и базовое и пользовательское события. Например, Вы можете создать свое событие с именем 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. На рис 1. показан элемент управления в работе. Назовем наш элемент MyCtrl. Выполнив построение нашего контрольного элемента, протестируем его, построим приложение с использованием его, а затем более глубоко посмотрим на сгенерируемые тексты и попробуем разобраться в них.
Построение элементов ActiveX
Создание элементов ActiveX до недавнего времени считали очень сложной задачей, посильной только для специалистов высокого класса. Но использование Visual C++ позволяет легко создать элементы ActiveX c применением библиотеки MFC или библиотеки шаблонов ATL (Active Template Library ).
Построение остова для элемента MyCtrl
Для создания элемента ActiveX воспользуемся Мастером построения приложений – ControlWizard. С его помощью создается проект с базовым набором классов и ресурсов.
Такой базовый элемент отображается в виде эллипса. Никакие свойства, события, методы для взаимодействия с родительским окном не включаются в базовый проект.
Для того, чтобы создать такой базовый элемент, выполните следующие шаги:
1.
В меню File выберите пункт New.
2. В появившемся диалоговом окне New выберите закладку Projects (рис 2.).
3. Отметьте, что Вы хотите построить MFC ActiveX ControlWizard.
4. Введите имя проекта, для нашего элемента - MyCtrl.
5. Укажите путь для расположения файлов проекта.
6. После щелчка на кнопке OK появится первое окно мастера MFC ActiveX ControlWizard (рис.3), в котором можно указать число элементов в проекте ( один проект может содержать несколько элементов управления ), требуется ли поддержка лицензирования и нужны ли комментарии и поддержка файла справки.
7.
Во втором окне мастера ( рис. 4) можно изменять имена классов, но обычно это не делается.
Для нашего примера использованы установки, предлагаемые по умолчанию. После нажатия на кнопку 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. |
Построение остова приложения
Процесс создания 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. Расширенные черты ActiveX
Расширенная черта ActiveX |
Описание |
Windowless activation – Активация без окна |
Позволяет элементу активироваться без создания окна |
Unclipped device context – Ограниченная клиентская зона |
Используется для контрольных элементов, которые гарантированно не рисуют вне своей клиентской зоны. Не используется вместе с предыдущим свойством. |
Flicker-free activation – Активация, свободная от мерцания |
Используется, если контрольный элемент отображается одинаково в активном и неактивном состояниях. Не используется вместе со свойством Windowless activation. |
Mouse pointer notifications when inactive – Сообщения от мыши, когда неактивен элемент |
Разрешает элементу получать сообщения от мыши, когда элемент не активен. |
Optimized drawing code – Отимизация отрисовки |
Позволяет элементу отрисовывать себя в режиме оптимизации, если контейнер поддерживает это свойство. |
Loads properties asynchronously – Асинхронная загрузка свойств |
Асинхронные свойства – свойства, которые могуи загружаться в фоновом режиме, и они затрагивают передачу больших объемов информации. |
Практическое руководство
Для студентов 3-го курса факультета АВТФ
направления 542200 Информатика и вычислительная техника
Распространение элемента управления.
Для распространения элемента управления надо ответить на два вопроса. Первый связан с тем, какие файлы должны поставляться пользователю. Второй – как установить контрольный элемент, чтобы его можно было использовать.
Пользователю контрольного элемента достаточно иметь только файл с расширением .ocx. На самом деле, этот файл является библиотекой dll, содержащей код элемента.
Для установки элемента ActiveX его надо зарегистрировать. Регистрацию можно выполнить, используя программу regsrvr32.exe, поставляемую вместе с DevStudio и свободно распространяемую:
regsrvr32.exe /s <имя элемента управления>.ocx
Можно также в программе, использующей элемент управления, загрузить файл OCX
как обычную DLL, а потом вызвать функцию DLLRegisterServer, описанную в файле приложения MyCtrl.cpp.
Если Вы используете лицензирование, то в число поставляемых разработчикам файлов должен быть включен файл с расширением .lic. Конечным пользователям этот файл не нужно поставлять.
Реализация параметрических свойств
Параметричекое свойство ( иногда называемое массив свойств) – это способ для работы с набором однородных свойств как с одним свойством. Например, такое свойство можно использовать параметрическое свойство для определения массива.
Для реализации параметрического свойства можно использовать вкладку 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.
9. В элементах Get Function и SetFunction установить имена функций.
10. Используя Parameter List, добавьте параметр row типа short.
11. Используя Parameter List, добавьте второй параметр column типа short.
12. Щелкнуть на кнопке OK для того, чтобы закрыть диалог Add Property.
13. Щелкнуть на кнопке OK для того, чтобы закрыть ClassWizard.
Реализация страницы свойств по умолчанию
Если Вы используете 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 | Текст из элементов управления. |
Редактирование записей системного реестра для класса страницы свойств
Добавление и удаление записей из системного реестра для класса CMyCtrlPropPage производится в методе UpdateRegistry
фабрики классов. Этот метод фабрики классов регистрирует либо снимает регистрацию класса CMyCtrlPropPage. Это делает страницу свойств доступной для всех OLE-серверов и контейнеров.
Регистрация элемента управления
В файле MyCtrl.cpp, кроме методов класса CmyCtrlApp, определены две функции: DllRegisterServer
(заносит данные в системный реестр) и DllUnregisterServer
(удаляет данные из системного реестра):
Элементы управления ActiveX являются саморегистрирующимися. Это означает, что они способны записывать информацию о себе в системный реестр Windows. Им не нужны ни файлы регистрации (имеющие расширение reg), ни какой-либо другой внешний механизм.
Функция DllRegisterServer
реализована во всех элементах 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.
Рисование элемента управления.
Отрисовка элемента управления осуществляется в методе OnDraw. Параметры этого метода содержат указатель на контекст устройства, размеры прямоугольника, определяющего границы элемента и прямоугольник , в пределах которого необходимо выполнить перерисовку.
В общем случае внешний вид элемента может зависеть от некоторых свойств элемента, в нашем случае – от свойств Shape и Select, связанных с переменными m_bSelected и m_nShape:
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.
Схема диспетчеризации класса
В объявлении класса присутствует макрос 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)
//{{AFX_EVENT_MAP(CMyCtrlCtrl)
EVENT_CUSTOM("Select", FireSelect, VTS_BOOL)
EVENT_CUSTOM("Tick", FireTick, VTS_I4)
//}}AFX_EVENT_MAP
END_EVENT_MAP()
в контейнер для того, чтобы
События посылаются контрольными элементами в контейнер для того, чтобы оповестить о каких-то изменениях в нем. Эти события могут быть связаны с нажатием на клавиатуру, мышь или изменениями состояния элемента. Когда это случается, элемент уведомляет (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.
Составление страниц свойств для элементов ActiveX.
Страницы свойств позволяют пользователю элемента ActiveX посмотреть и изменить свойства элемента. Эти свойства доступны через вызов диалога, содержащего несколько закладок для удобного задания свойств.
Создание файла типа MyCtrl.ocx для элемента MyCtrl.
Построение элемента управления ActiveX выполняется также, как и всех программных модулей в DevStudio. При компиляции и последующей сборке программного модуля автоматически происходит регистрация элемента управления в системе.
Стандартные элементы управления страницы свойств
Страницы свойств - это просто дочерние диалоговые окна, обычно содержащие ряд стандартных элементов управления. Как и в случае обычных диалоговых окон, можно связать с каждым элементом управления диалога соответствующую ему переменную - элемент класса диалога. Такие элементы объявляются средством ClassWizard в классе страницы свойств в следующем блоке AFX_DATA.
Обмен данными между диалоговым окном страницы свойств и его элементами управления посредством связанных с элементами управления переменных класса осуществляется при помощи метода DoDataExchange(), который реализован в файле MyCtrlPpg.cpp. Этот метод использует функции обмена и проверки данных (DDX/DDV-макросы). Мастер ClassWizard в дополнение к любым вызовам функций DDX_ и DDV_ вставляет в функцию DoDataExchange класса страницы свойств набор вызовов функций DDP_.
Связывание страницы свойств со свойствами элемента управления.
Переменная для новой страницы свойств может быть добавлена с помощью ClassWizard, необычным является способ связывания этой переменной ( переменной класса CMyCtrlPropPage) cо свойством элемента управления MyCtrl.
1. В диалоге ClassWizard щелкните на вкладке Member Variables.
2. Выберите в поле Class name класс CMyCtrlPropPage.
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.
Свойства, доступные только для чтения или записи.
Для задания доступа к свойству Вы должны выполнить следующие действия:
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.
При этом в карту диспетчеризации будет вставлена функция 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; // установка свойства
}
Свойства определения цветов.
Вы можете использовать базовые свойства 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.
Таблица сообщений класса
Класс 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)
…
END_MESSAGE_MAP()
добавляются соответствующие строки.
Test Container
Приложение Test Container, поставляемое с Visual C++, предназначено для тестирования и отладки ActiveX. Test Container позволяет пользователю проверить, как элемент позволяет менять свойства, как выполняет методы и пересылает события в контейнер.
Для тестирования Вашего элемента ActiveX :
1. В меню Tools DevStudio выберете ActiveX Control Test Container.
2. В меню Edit в оболочке Test Container выберете Insert New Control.
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.
Тестирование элемента 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)
Тестирование элемента управления.
Чтобы протестировать элемент управления, можно использовать приложение ActiveX Control Test Container, входящее в состав DevStudio и поставляемое вместе с Visual C++. Для запуска этой программы можно использовать соответствующий пункт в меню Tools.
Можно запустить эту же программу из главного меню системы (Пуск – Программы – Microsoft Visual C++ - Microsoft Visual C++ Tools - ActiveX Control Test Container.
Более удобным способом отладки элементов управления является установка этого приложения, находящегося в файле tstcon32.exe, в качестве исполняемой программы отладки через команду Settings меню Project в DevStudio. В этом случае можно задавать точки останова и управлять выполнением программы элемента управления с помощью отладчика.
После запуска программы - ActiveX Control Test Container вставьте свой контрольный элемент, используя команду Edit – Insert New Control (рис. 16).
Затем Вы можете изменить указанное свойство в странице свойств – форму изображаемой фигуры. Для вызова страницы свойств в меню Edit Вы можете выбрать пункт Properties ( рис. 17 ). Проверить, как элемент реагирует на событие мыши Вы можете, щелкнув левой кнопкой мыши на элементе. Вы можете убедиться, что элемент меняет цвет отрисованной формы.
Установка значения базового свойства по умолчанию.
Класс COleControl реализует следующий механизм для получения значений по умолчанию для базовых свойств. В методе COleControl::OnResetState вызывается функция COleControl::DoPropExchange, которая запрашивает контейнер о некоторых его свойствах (ForeColor, BackColor и т. п.) и устанавливает значение этого свойства по умолчанию.
Для получения значения текущего базового свойства можно воспользоваться методами класса COleControl, в нашем случае GetForeColor().
Устойчивость свойства.
Для установки свойства элемента во время разработки к функции DoPropExchange необходимо добавить обращение к функции PX_.
Функция 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);
Возврат кодов ошибок из методов.
Для того, чтобы сообщить контейнеру, что произошла ошибка в методе, Вы должны использовать метод 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 | Ошибка устройства ввода/вывода | ||
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)
Возврат кодов ошибок при установке свойств.
Для индикации ошибок при попытке установить или прочитать значение свойства, использется метод
COleControl::ThrowError, которая принимает параметр типа SCODE. Об этом способе сообщалось выше.
Эта работа предназначена для тех
Эта работа предназначена для тех студентов, кто умеет писать приложения еа 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 – компоненты являются независимыми от языка, то они могут взаимодействовать с любыми программами, реализованными на других языках. Кроме того, они могут выполняться в любом адресном пространстве: как в том, где запущен клиент, так и в другом процессе на той же машине или даже на другой машине.
COM – это основа для построения составных документов (OLE), ActiveX и т.д. (для углубленного теоретического изучения советуем обратиться к литературе, приводимой в конце работы).
Для того, чтобы понять COM, надо иметь в виду, что это не объектно-ориентированный язык, а двоичный стандарт, определяющий, как COM-объекты взаимодействуют с другими объектами. Язык для реализации COM – объектов должен поддерживать указатели и вызывать функции через указатели.
COM определяет природу COM-объекта. COM- объект – это набор данных, доступ к которым осуществляется исключительно через набор методов, образующих интерфейсы.
COM определяет базовые интерфейсы, которые обеспечивают методы, общие для всех COM – технологий. Кроме того, COM обеспечивает набор API-функций, которые требуются для всех компонентов, а также позволяют компонентам взаимодействовать через сеть и обеспечивает защиту программных систем.
COM – компонент поддерживает двоичный стандарт, поэтому он может быть реализован с помощью любого языка программирования. Объектно-ориентированные языки идеально подходят для создания COM-элементов.
Вызов функции 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)
{
COleControl::OnLButtonDown(nFlags, point);
FireClickIn(point.x, point.y);
}
Взаимодействие между контрольными элементами и контейнерами.
Когда контрольный элемент вставляется в контейнер, он взаимодействует с ним с помощью двух механизмов: с использованием его свойств и методов и посылая события в контейнер. Рис. 28 показывает, как осуществляется это взаимодействие.
Обратите внимание, что все взаимодействие с контейнером реализуется через COleControl.
Задание свойств для 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, кроме имени свойства. |
Задания для самостоятельной работы.
Разработайте элемент ActiveX, оттестируйте его в Test Container, разработайте приложение, используя созданный ActiveX.
Покажите возможность задания свойств ActiveX, проиилюстрируйте изменение свойств при использовании элементов во время работы приложения, покажите возможность получать события от ActiveX и обрабатывать их в приложении, покажите использование методов ActiveX. Покажите реализацию обмена данными межлу ActiveX и приложением.
Продемонстрируйте применение разработанного ActiveX в Internet-технологиях.
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.