Глава 22. Основы создания элементов управления ActiveX

Вы уже знакомы с разнообразными элементами управления среды Windows, такими как переключатели, флажки, списки и т.д. Но многие разработчики предпочитают наряду с ними использовать и собственные элементы управления, известные как элементы управления ActiveX. Впервые они появились в языке VisualBasic и изначально носили название VBX-элементов (таковым было расширение соответствующих файлов). По сути, эти элементы управления являлись небольшими библиотеками динамической компоновки (DLL), только имевшими расширение VBX. Возможности современных 32-разрядных элементов управления значительно расширены, а для их файлов теперь применяется расширение OCX.

Многие программисты использовали VisualBasic для разработки собственных элементов управления, а затем включали их в приложения, написанные на C/C++. Стало очевидным, что языки C/C++ неплохо было бы снабдить своим средством разработки элементов управления. Однако в то время, когда компания Microsoft подошла к решению данной проблемы, началась смена поколений операционной системы — переход от 16-разрядной Windows3.1 к 32-разрядным Windows95 и NT. Но оказалось, что аппаратно-зависимые 16-разрядные элементы управления VBX не могли служить так же хорошо на новых 32-разрядных платформах. Специалисты Microsoft решили, что целесообразнее будет заняться не расширением спецификации VBX, а разработкой для 32-разрядных платформ принципиально новой архитектуры элементов управления. Для их файлов было выбрано расширение OCX, а сами элементы получили название элементов управления ActiveX.

Хорошей новостью для программистов, работающих с C++, стало включение в среду MicrosoftVisualC++ специального мастера, предназначенного для построения элементов управления ActiveX. Мастер создает код на основе библиотеки MFC. Элемент управления в процессе разработки можно протестировать с помощью утилиты ActiveXControlTestContainer. Готовый элемент можно внедрить в любое приложение, поддерживающее технологию OLE, скажем в MicrosoftWord или Excel.

Основные концепции

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

Вся ответственность за функционирование элемента управления ложится на его разработчика. При этом необходимо учитывать два аспекта. С одной стороны, на стадии разработки следует написать, отладить и скомпилировать весь код, управляющий выводом элемента управления и реализующий все, его свойства и методы. В результате будет создана небольшая динамическая библиотека с расширением OCX. С другой стороны, приложение, использующее элемент управления ActiveX, должно будет взаимодействовать с ним, вызывая его методы, запрашивая его данные и т.д. Разработчик должен определить интерфейс этого взаимодействия таким образом, чтобы элемент управления был полностью независимым от использующего его приложения. О таких объектах говорят, что они обеспечивают повторное вхождение. Вспомните, что элемент управления ActiveX представляет собой отдельную библиотеку динамической компоновки, не связанную ни с каким приложением. Повторное вхождение достигается за счет создания экземпляров библиотеки в каждом приложении, содержащем внедренный элемент управления. Взаимодействие между приложением и элементом осуществляется только посредством сообщений.

Критерии разработки элементов управления

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

Класс COleControl

Элементы управления ActiveX порождаются от MFC-класса COleControl. В приведенном ниже листинге содержится часть файла AFXCTL.H с сокращенным описанием данного класса. Мы не станем подробно объяснять назначение каждой части файла, а лишь наглядно проиллюстрируем наиболее важные моменты.

// Это класс библиотеки MFC.

// Copyright (С)1992-1998Microsoft Corporation

// All rights reserved.

//

// Данный исходный код служит дополнением к справочному

// руководству и сопутствующей электронной документации по MFC.

// Обращайтесь к ним для получения более полной информации.

///////////////////////////////////
// AFXCTL.H поддержка элементов управления OLE

///////////////////////////////////
// Базовые события

#define EVENT_STOCK_CLICK() \

{afxEventStock, DISPID_CLICK, _T("Click"),VTS_NONE },

#define EVENT_STOCK_DBLCLICK() \

{ afxEventStock, DISPID_DBLCLICK, _T("DblClick"), VTS_NONE },

#define EVENT_STOCK_KEYDOWN() \

{ afxEventStock, DISPID_KEYDOWN, _T("KeyDown"), VTS_PI2 VTS_I2 },

#define EVENT_STOCK_KEYPRESS() \

{ afxEventStock, DISPID_KEYPRESS, _T("KeyPress"), VTS_PI2 },

#define EVENT_STOCK_KEYUP() \

{ afxEventStock, DISPID_KEYUP, JTC'KeyUp") , VTS_PI2 VTS_I2 },

#define EVENT_STOCK_MOUSEDOWN() \

( afxEventStock, DISPID_MOUSEDOWN, _T("MouseDown"), \ VTS_I2 VTS_I2 VTS_XPOS_PIXELS VTS_YPOS_PIXELS },

#define EVENT_STOCK_MOUSEMOVE() \

{ afxEventStock, DISPID_MOUSEMOVE, _T("MouseMove"), \

VTS_i2 VTS_i2 vts_xpos_pixels vts_ypos_pixels },

#define EVENT_STOCK_MOOSEUP() \

{ afxEventStock, DISPID_MOUSEUP, _T("MouseUp"), \ VTS_I2 VTS_I2 VTS_XPOS_PIXELS VTS_YPOS_PIXELS },

#define EVENT STOCK ERROREVENTO \{ afxEventStock, DISPID_ERROREVENT, _T("Error"),\

VTS_I2 VTS_PBSTR VTS_SCODE VTS_BSTR VTS_BSTR VTS_I4 VTS_PBOOL },

#define EVENT_STOCK_READYSTATECHANGE () \

{ afxEvtentStock, DISPID_READYSTATECHANGE, _T("ReadyStateChange"), \ VTS 14 },

///////////////////////////////////
// Базовые свойства

#define DISP_PROPERTY_STOCK(theClass, szExternalName, dispid,

pfnGet, pfnSet, vtPropType) \

{ _T(szExternalName), dispid, NULL, vtPropType, \ (AFX_PMSG)(void (theClass::*)(void)JSpfnGet, \ (AFX_PMSG)(void (theClass::*)(void))SpfnSet, 0, afxDispStock ), \

#define D1SP_STOCKPROP_APPEARANCE() \

DISP_PROPERTY_STOCK(C01eControl, "Appearance", DISPID_APPEARANCE, \ COleControl::GetAppearance, COleControl::SetAppearance, VT_I2)

#define DISP_STOCKPROP_BACKCOLOR() \

DISP_PROPERTY_STOCK(COleControl, "BackColor", DISPID_BACKCOLOR, \ COleControl::GetBackColor, COleControl::SetBackColor, VT_COLOR)

#define DISP_STOCKPROP_BORDERSTYLE() \

DISP_PROPERTY_STOCK(COleControl, "BorderStyle", DISPID_BORDERSTYLE, \ COleControl::GetBorderStyle, COleControl::SetBorderStyle, VT_I2)

#define DISP_STOCKPROP_CAPTION() \

DISP_PROPERTY_STOCK(COleControl, "Caption", DISPID_CAPTION, \ COleControl::GetText, COleControl::SetText, VT_BSTR)

#define DISP_STOCKPROP_ENABLED() \

DISP_PROPERTY_STOCK(COleControl, "Enabled", DISPID_ENABLED, \ COleControl::GetEnabled, COleControl::SetEnabled, VT_BOOL)

#define DISP_STOCKPROP_FONT() \

DISP_PROPERTY_STOCK (COleControl, "Font",' DISPID FONT, \

COleControl::GetFont, COleControl::SetFont, VT_FONT)

Idefine DISP_STOCKPROP_FORECOLOR() \

DISP_PROPERTY_STOCK(COleControl, "ForeColor", DISPID_FORECOLOR, \ COleControl::GetForeColor, COleControl::SetForeColor, VT_COLOR)

Idefine DISP_STOCKPROP_HWND() \

DISP_PROPERTY_STOCK(COleControl, "hWnd", DISPID_HWND, \ COleControl::GetHwnd, SetNotSupported, VT_HANDLE)

Idefine DISP_STOCKPROP_TEXT() \

DISP_PROPERTY_STOCK(COleControl, "Text", DISPID_TEXT, \ COleControl::GetText, COleControl::SetText, VT_BSTR)

Idefine DISP_STOCKPROP_READYSTATE() Л

DISP_PROPERTY_STOCK(COleControl, "ReadyState", DISPID_READYSTATE, \ COleControl::GetReadyState, SetNotSupported, VT_I4)

///////////////////////////////////
// Базовые методы

Idefine DISP_FUNCT1ON_STOCK(theClass, szExternalName, dispid,

pfnMember, vtRetVal, vtsParams) \

( _T(szExternalName), dispid, vtsParams, vtRetVal, \ (AFX_PMSG)(void (theClass::*)(void))SpfnMember, (AFX_PMSG)0, 0, \ afxDispStock }, \

#define DISP_STOCKFDNC_REFRESH() \

DISP_FUNCTION_STOCK(COleControl, "Refresh", DISPID_REFRESH, \ COleControl::Refresh, VT_EMPTY, VTS_NONE)

#define DISP_STOCKFUNC_DOCLICK() \

DISP_FUNCTION_STOCK(COleControl, "DoClick", DISPID_DOCLICK, \ COleControl::DoClick, VT.EMPTY, VTS_NONE)

///////////////////////////////////
// COleControl — базовыйклассэлементауправления.

//    реализованного на C++ с использованием MFC
classCOleControl: publicCWnd

// Базовые методы

void Refresh () ; void DoClick () ;

// Базовые свойства

short GetAppearance();

void SetAppearance(short);

OLE_COLOR GetBackColorO ;

void SetBackColor(OLE_COLOR);

short GetBorderStyle();

void SetBorderStyle(short);

BOOL GetEnabledO ;

void SetEnabled(BOOL);

CFontHolderS InternalGetFont();

LPFONTDISP GetFontO;

void SetFont(LPFONTDISP);

OLE_COLOR GetForeColor();

void SetForeColor(OLE_COLOR);

OLE_HANDLE GetHwnd();

const CStringS InternalGetText () ;

BSTR GetText () ;

void SetText(LPCTSTR);

long GetReadyState();

void InternalSetReadyState(long INewReadyState) ,

// Внешние свойства

short AmbientAppearance();
OLE_COLOR AmbientBackColor();
CString AmbientDisplayNarae();
LPFONTDISP AmbientFont();
OLE_COLOR AmbientForeColor();
LCID AmbientLocalelDO;
CString AmbientScaleUnits(>; short AmbientTextAlign();
BOOL AmbientUserModeO;
BOOL AmbientUIDeadO;
BOOL AmbientshowGrabHandles();
BOOL AmbientShowHatching();

// Генерация событий

void AFX_CDECL FireEvent(DISPID dispid, BYTE* pbParams,

// Функции генерации базовых событий

void FireKeyDown(USHORT* pnChar, short nShiftState);
void FireKeyUp(USHORT* pnChar, short nShiftState);
void FireKeyPress(USHORT* pnChar); void FireMouseDown(short nButton, short nShiftState,

OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y) ;
void FireMouseDp(short nButton, short nShiftState,

OLE_XPOS_PIXELS X, OLE_YPOS_PIXELS y);
void FireMouseMove(short nButton, short nShiftState,

OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);
void FireClickO; void FireDblClickO ;
void FireError(SCODE scode, LPCTSTR IpszDescription,

UINT nHelpID. = 0) ;
void FireReadyStateChange(); 

// Базовые события

void KeyDown(USHORT* pnChar);

void KeyUp(USHORT* pnChar);

void ButtonDown(USHORT iButton, UINT nFlags, CPoint point);

void ButtonUp(USHORT iButton, UINT nFlags, CPoint point);

void ButtonDblClk(USHORT iButton, UINT nFlags, CPoint point);

// Базовые свойства

OLE_COLOR m_clrBackColor;    // Цвет фона

OLE_COLOR m_clrForeColor;    // Цвет переднего плана

CString m_strText;// Текст/Надпись

CFontHolder m_font;     // Шрифт

HFONT m_hFontPrev;// Предыдущий шрифт

short m_sAppearance; .  // Внешний вид

short m_sBorderStyle;   // Стиль рамки

BOOLm_bEnabled;  // Доступность

longm_lReadyState;     // Состояние готовности

// Схема сообщений

protected:

//{{AFX_MSG(COleControl)

afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags),
afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
afx_msg void OnMButtonDown(UINT nFlags, CPoint point);
afx_msg void OnMButtonUp(UINT nFlags, CPoint point);

afx_msg void OnMButtonDblClkfUINT nFlags, CPoint point);

afx_msg void OnRButtonDown(UINT nFlags, CPoint point);

afx_msg void OnRButtonUp(UINT nFlags, CPoint point);

afx_msg void OnRButtonDblClk(UINT nFlags, CPoint point);

afx_msg void OnlnitMenuPopup(CMenu*, UINT, BOOL);

afx_msg void OnMenuSelect(UINT nltemlD, UINT nFlags, HMENU hSysMenu),

afx_msg LRESULT OnSetMessageString(WPARAM wParam, LPARAM IParam);

afx_msg void OnEnterldle(UINT nWhy, CWnd* pWho);

afx_msg void OnCancelMode();

afx_msg void OnPaint(CDC* pDC);

afx_msg BOOL OnEraseBkgnd(CDC* pDC) ;

afx_msg void OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);

afx_msg void OnSysKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);

afx_msg int OnMouseActivate(CWnd *pDesktopWnd, UINT nHitTest,

UINT message);

afx_msg LRESULT OnSetText(WPARAM wParam, LPARAM IParam);
afx_msg BOOL OnNcCreate(LPCREATESTRUCT IpCreateStruct);
afx_msg void OnDestroyO; afx_msg void OnKillFocus(CWnd* pNewWnd);
afx_msg void OnSetFocus(CWnd* pOldWnd) ;
afx_msg void OnNcPaintO;
afx_msg void OnNcCalcSize(BOOL bCalcValidRects,

NCCALCSIZE_PARAMS* Ipncsp); afx_msg UINT OnNcHitTest(CPoint point);

afx_msg void OnNcLButtonDown(UINT nHitTest, CPoint point); afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message); afx_msg UINT OnGetDlgCode();

afx_msg int OnCreate(LPCREATESTRUCT IpCreateStruct); afx_msg void OnSize(UINT nType, int ex, int cy) ; afx_msg void OnMovelint x, int y);

afxjmsg void OnShowWindow(BOOL bShow, UINT nStatus); //))AFX_MSG

afx_msg LRESULT OnOcraCtlColorBtn(WPARAM wParam, LPARAM IParam);
afx_msg LRESULT OnOcmCtlColorDlg(WPARAM wParam, LPARAM IParam);
afx_msg LRESULT OnOcmCtlColorEdit(WPARAM wParam, LPARAM IParam);
afx_msg LRESULT OnOcmCtlColorListBox(WPARAM wParam, LPARAM IParam);
afx_msg LRESULT OnOcmCtlColorMsgBox(WPARAM wParam, LPARAM IParam);
afx_msg LRESULT OnOcmCtlColorScrollBar(WPARAM wParam, LPARAM IParam) ,
afx_msg LRESULT OnOcmCtlColorStatic(WPARAM wParam, LEARAM IParam);

DECLARE_MESSAGE_MAP()

// вспомогательные обработчики

void OnButtonUp(USHORT nButton, UINT nFlags, CPoint point);
void OnButtonDown(USHORT nButton, UINT nFlags, CPoint point);
void OnButtonDblClk(USHORT nButton, UINT nFlags, CPoint point);

// Схемы интерфейсов public:

// IPersistStorage

BEGIN_INTERFACE_PART(Persiststorage, IPersistStorage)


INIT_INTERFACE_PART(COleControl, PersistStorage)
STDMETHOD(GetClassID)(LPCLSID);
STDMETHOD (IsDirty) ();
STDMETHOD(InitNew)(LPSTORAGE);
STDMETHOD(Load)(LPSTORAGE);
STDMETHOD(Save)(LPSTORAGE, BOOL);
STDMETHOD(SaveCompleted)(LPSTORAGE);
STDMETHOD(HandsOffStorage)();
END_INTERFACE_PART_STATIC(PersistStorage)

// IOlelnPlaceObject

BEGIN_INTERFACE_PART(OlelnPlaceObject, IQlelnPlaceObjectWindowless)

INIT_INTERFACE_PART(COleGontrol, OlelnPlaceObject)

STDMETHOD(GetWindow)(HWND*); 

STDMETHOD(ContextSensitiveHelp)(BOOL);

STDMETHOD(InPlaceDeactivate)();

STDMETHOD(OIDeactivate)();

STDMETHOD(SetObjectRects)(LPCRECT, LPCRECT);

STDMETHOD(ReactivateAndUndo)();

STDMETHOD(OnWindowMessage)(UINT msg, WPARAM wParam, LPARAM Iparam,
  LRESULT* plResult);

STDMETHOD(GetDropTarget)(IDropTarget **ppDropTarget); END_INTERFACE_PART(OlelnPlaceObject)

// IQlelnPlaceActiveObject
BEGIN_INTERFACE_PART(OleInPlaceActiveObject, IQlelnPlaceActiveObject)

INIT_INTERFACE_PART(COleControl, OlelnPlaceActiveObject)

STDMETHOD(GetWindow)(HWND*);

STDMETHOD(ContextSensitiveHelp)(BOOL);

STDMETHOD(TranslateAccelerator)(LPMSG);

STDMETHOD(OnFrameWindowActivate)(BOOL);

STDMETHOD(OnDocWindowActivate)(BOOL);

STDMETHOD(ResizeBorder)(LPCRECT, LPOLEINPLACEOIWINDOW, BOOL);

STDMETHOD(EnableModeless)(BOOL); END_INTERFACE_PART(OlelnPlaceActiveObject)

}

События

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

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

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

Методы и свойства

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

Доступ к методам и свойствам элемента управления возможен на этапе раннего связывания. При этом методы рассматриваются как обычные функции-члены класса, а свойства представляются парами функций getxxx и setxxx. Доступ к элементу управления на этапе позднего связывания регулируется интерфейсом IDispatch. Контейнер автоматически определяет тип связывания, установленный пользователем. Интерфейс IProvideClassInfo содержит функцию GetClassInfo(), которая возвращает объект ITypelnfо с информацией об элементе управления, взятой из его библиотеки типов.

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

Постоянство

Элементы управления позволяют сохранять свои данные в потоках с помощью интерфейса iPersiststream, а в хранилищах — с помощью интерфейса iPersiststorage. Первый необходим для записи данных в простой последовательный поток, а второй реализует концепцию структурированного хранения составных документов, когда каждый внедренный или связанный объект имеет собственную область хранения в общем хранилище данных документа.

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

Контейнеры

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

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

Свойства контейнера, применимые ко всем внедренным элементам управления, называются внешними (ambient). К таковым, в частности, относятся заданные по умолчанию цвета и шрифты.

Создание простого элемента управления ActiveX с использованием MFC

В данном параграфе мы шаг за шагом пройдем процесс создания шаблона элемента управления ActiveX с помощью мастера MFCActiveXControlWizard. Затем мы изменим шаблон таким образом, чтобы получить элемент управления, отвечаю­щий нашим требованиям.

Создание ActiveX-шаблона

В окне компилятора MicrosoftVisualC++ выберите в меню File команду New и в открывшемся диалоговом окне установите опцию MFC ActiveX ControlWizard (рис. 22.1). Назовем наш проект TDCtrl.


Рис. 22.1. Выбор типа проекта

Мастер создает элемент управления в два этапа. Сначала задаются установки всего проекта, такие как число элементов управления, наличие лицензии на выполнение, добавление комментариев в программный код и создание файлов справки (рис. 22.2). Не меняйте опции, заданные по умолчанию.


Рис. 22.2. Шаг 1: принимаем опции, заданные по умолчанию

На втором этапе задаются параметры каждого элемента управления, включаемого в проект. Установите в этом окне опцию Available in "InsertObjects" dialog, чтобы название элемента управления появлялось в окне вставки объектов (рис. 22.3.)


Рис. 22.3. Шаг 2: параметры элемента управления TDCtrl

Далее на экране появится отчет с перечнем установок проекта (рис 22.4)


Рис. 22.4. Отчет о параметрах создаваемого элемента управления

После щелчка на кнопке ОК мастер автоматически сгенерирует код шаблона элемента управления. Список его классов и глобальных атрибутов представлен на вкладке ClassView(рис 22.5)


Рис. 22.5. Список классов и глобальных атрибутов элемента управления TDCtrl

Для того чтобы скомпилировать проект, выберите в меню Build команду Build илиRebuild All. После завершения этого процесса в папке DEBUG появится файл TDCTRL.OCX.

Элементы управления ActiveX представляют собой небольшие библиотеки динамической компоновки, которые можно тестировать в соответствующих контейнерах. Компания Microsoft предоставляет для этих целей специальную утилиту ActiveXControlTestContainer; запускаемую из меню Tools(рис. 22.6).


Рис. 22.6. Местонахождение инструментов ActiveX

В окне контейнера выберите в меню Edit команду InsertNewControl, и перед вами откроется диалоговое окно InsertControl, в котором будет представлен список всех зарегистрированных элементов управления . Теперь щелкните на кнопке ОК, чтобы внедрить выбранный элемент управления в окно контейнера .

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

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

Код, сгенерированный мастером

Для большинства элементов управления ActiveX мастер ControlWizard создает четыре программных файла. В нашем проекте это файлы STDAFX.CPP, TDCTRL.CPP, TDCTRLCTL.CPP и TDCTRLPPG.CPP. Каждому из них соответствует отдельный файл заголовков.

Файл STDAFX.CPPпредназначен для включения в программу всех файлов заголовков, необходимых для работы элемента управления ActiveX. Файл TDCTRL.CPPотвечает за реализацию класса CTDCtrlApp и регистрацию DLL-модуля.

В следующих параграфах мы проанализируем назначение файлов TDCTRLCTL.CPP и TDCTRLPPG.CPP.

Файл TDCTRLCTL.CPP

Файл TDCTRLCTL.CPPсодержит реализацию собственно класса элемента управления ActiveX. В нашем примере это класс CTDCtrlctrl. Именно данный файл больше других требует изменений со стороны программиста.

// TDCtrlCtl.cpp: Реализация класса CTDCtrlСtrl элемента управления.

#include "stdafx.h"

#include "TDCtrl.h"  

#include "TDCtrlCtl.h"

#include "TDCtrlPpg.h"

tifdef _DEBUG

#define new DEBUG_NEW

tundef THIS_FILE

static char THIS_FILE[] = _FILE_;

#endif

IMPLEMENT_DYNCREATE(CTDCtrlctrl, COleControl)
/////////////////////////////////
// Схема сообщений

BEGIN_MESSAGE_MAP(CTDCtrlctrl, COleControl)

//{{AFX_MSG_MAP(CTDCtrlctrl)

// ПРИМЕЧАНИЕ: мастер классов будет добавлять и удалять здесь

//   макросы схемы-сообщений.

// НЕ РЕДАКТИРУЙТЕ то, что здесь находится.

//)}AFX_MSG_MAP

ON_OLEVERB (AFX_IDS_VERB_EDIT, OnEdit)

ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties) END MESSAGE MAP ()

/////////////////////////////////
// Схема диспетчеризации

BEGIN DISPATCH MAP(CTDCtrlctrl, COleControl)


// { {AFX_DISPATCH_MAP (CTDCtrlctrl)

// ПРИМЕЧАНИЕ: мастер классов будет добавлять и удалять здесь

//   макросы схемы диспетчеризации.

//НЕРЕДАКТИРУЙТЕ то, что здесь находится.

//}}AFX_DISPATOt_MAP

DISP_FUNCTION_ID (CTDCtrlctrl, "AboutBox", DISPID_ABOUTBOX,

AboutBox, VT_EMPTY, VTSJTONE) END_DISPATCH_MAP ( )

/////////////////////////////////
// Схемасобытий

BEGIN_EVENT_MAP (CTDCtrlctrl, COleControl)

// { (AFX_EVENT_MAP (CTDCtrlctrl)

// ПРИМЕЧАНИЕ: мастер классов будет добавлять и удалять здесь

//        макросы схемы событий.

// НЕ РЕДАКТИРУЙТЕ то, что здесь находится.

// } }AFX_EVENT_MAP END_EVENT_MAP ( )

/////////////////////////////////
// Страницы свойств

// TODO: Сюда можно добавлять новые страницы свойств.
//      Не забудьте увеличить значение счетчика!
BEGIN_PROPPAGEIDS (CTDCtrlctrl, 1)

PROPPAGEID (CTDCtrlPropPage : : quid) END_PROPPAGEIDS (CTDCtrlctrl)

/////////////////////////////////

// Инициализация фабрики класса и формирование GUID

IMPLEMENT_OLECREATE_EX (CTDCtrlCtrl, "TDCTRL . TDCtrlCtrl .1",

Oxc0377506, Oxb276, Oxlldl, Oxba, Oxe9, 0, OxaO, Oxc9, Ox8c, Oxc4, Охбе)

/////////////////////////////////

// Идентификатор библиотеки типов и ее версия IMPLEMENt_OLETYPELIB (CTDCtrlCtrl, _tlid, _wVerMajor, _wVerMinor)

/////////////////////////////////

// Идентификаторы интерфейсов

const IID BASED_CODE IID_DTDCtrl =

{ Oxc0377504, Oxb276, Oxlldl, { Oxba, Oxe9, 0, OxaO, Oxc9,

Ox8c, Oxc4, Охбе } } ;

const IID BASED_CODE IID_DTDCtrlEvents =

{ Oxc0377505, Oxb276, Oxlldl, { Oxba, Oxe9, 0, OxaO, Oxc9,

Ox8c, Oxc4, Охбе } ) ;

/////////////////////////////////

// Информация о типе элемента управления


static const DWORD BASED_CODE _dwTDCtr!01e'Misc = OLEMISC_ACTIVATEWHENVISIBLE I OLEMISC_SETCLIENTSITEFIRST | OLEMISC_INSIDEOUT | OLEMISC_CANTLINKINSIDE I OLEMISC_RECOMPOSEONRESIZE;

IMPLEMENT_OLECTLTYPE (CTDCtrlCtrl, IDSJTDCTRL, _dwTDCtr!01eMisc)

/////////////////////////////////
CTDCtrlCtrl: :CTDCtrlCtrlFactory: :UpdateRegistry
// Добавляет и удаляет записи системного реестра
// для класса CTDCtrlCtrl

BOOL CTDCtrlCtrl: : CTDCtrlCtrlFactory : :UpdateRegistry (BOOL bReglster)

{

// TODO: Проверьте, соответствует ли элемент управления

// правилам изолированной потоковой модели.

// За информацией обратитесь к разделу документации

// MFCTechNote64.

// Если элемент управления не поддерживает эту модель,

// замените шестой параметр константой afxReglnsertable.

if (bRegister)

return AfxOleRegisterControlClass (

AfxGetlnstanceHandle () ,

m_clsid,

m_lpszProgID,

IDSJTDCTRL,

IDBJTDCTRL,

afxReglnsertable I afxRegApartmentThreadlng,

_dwTDCtr!01eMisc,

_tlid, _wVerMajor, _wVerMinor) ; else

return AfxOleUnregisterClass(m_clsid, m_lpszProgID); }

/////////////////////////////////
// CTDCtrlCtrl::CTDCtrlCtrl - Конструктор

CTDCtrlCtrl::CTDCtrlCtrl() {

InitializellDs(SIID_DTDCtrl, SIID_DTDCtrlEvents);

// TODO: В этом месте инициализируйте данные

//       текущего экземпляра элемента управления.

}

/////////////////////////////////
// CTDCtrlCtrl::-CTDCtrlCtrl - Деструктор


CTDCtrlCtrl: : -CTDCtrlCtrl( )

{

// TODO: В этом месте удалите данные

//      текущего экземпляра элемента управления.

)

/////////////////////////////////

I/ CTDCtrlCtrl::OnDraw— Функция отображения

void CTDCtrlCtrl::OnDraw(CDC* pdc, const CRect SrcBounds,

const CRect Srclnvalid) {

// TODO: Замените следующие операторы

//      собственными операторами отображения.

pdc->FillRect(rcBounds,

CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)))

pdc->Ellipse(rcBounds); }

//////////////////////////////////////////////
// CTDCtrlCtrl::DoPropExchange — Поддержка постоянства элемента

void CTDCtrlCtrl::DoPropExchange(CPropExchange* pPX) f

ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));

COleControl::DoPropExchange(pPX) ;

// TODO: Поместите здесь вызовы РХ_-функций для каждого
//      постоянного пользовательского свойства.

}  

/////////////////////////////////
// CTDCtrlCtrl::OnResetState— Восстанавливает первоначальное
//                    состояние элемента управления

void CTDCtrlCtrl::OnResetState() {

COleControl::OnResetState(); // Сброс значений, установленных

// в методе DoPropExchange()

// TODO: Восстановите здесь все остальные переменные состояния

//      элемента управления. )

/////////////////////////////////
// CTDCtrlCtrl::AboutBox- Отображает окно About

void CTDCtrlCtrl::AboutBox() {

CDialog dlgAbout(IDD_ABOUTBOX_TDCTRL) ;

dlgAbout.DoModal();   

/////////////////////////////////

// Обработчики сообщений класса CTDCtrlCtrl

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

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

Функция OnDraw() скоро станет объектом нашего особого внимания, поскольку это именно тот блок программы, который отвечает за вывод графического образа элемента управления. По умолчанию в созданном мастером элементе управления ActiveX вызывается функция Ellipse() , выводящая на экран эллипс.

/////////////////////////////////

// CTDCtrlCtrl :: OnDraw— Функция отображения

void CTDCtrlCtrl: : OnDraw (CDC* pdc, const CRect SrcBounds, const CRect Srclnvalid) {

// TODO: Замените следующие операторы

//      собственными операторами отображения.

pdc->FillRect (rcBounds,

CBrush: :FromHandle( (HBRUSH) GetStockObject (WHITE_BROSH) ) ) ;

pdc->Ellipse (rcBounds) ; }

Мастер ControlWizard также создает для разрабатываемого проекта диалоговое окно About. Содержимое окна можно изменить таким образом, чтобы в нем отражалась специфическая информация о проекте. Описание окна вы найдете в файле TDCTRL.RC, а выводится оно на экран посредством следующей функции:

/////////////////////////////////

// CTDCtrlCtrl: :AboutBox— Отображает окно About

void CTDCtrlCtrl.: :AboutBox () {

CDialog dlgAbout (IDD_ABOUTBOX_TDCTRL) ;

dlgAbout . DoModal ( } ;

Файл TDCTRLPPG.CPP

Этот файл содержит реализацию класса CTDCtrlPropPage, являющегося потомком класса COlePropertyPage.

// TDCtrlPpg.cpp: реализация класса CTDCtrlPropPageстраницы свойств.

#include "stdafx.h"
#include "TDCtrl.h"
#include "TDCtrlPpg.h"

#ifdef _DEBUG
#define new DEBUG NEW

#undef THIS_FILE

static char THIS_FILE[] = _FILE_;

#endif

IMPLEMENT_DYNCREATE(CTDCtrlPropPage, COlePropertyPage)

/////////////////////////////////
// Схема сообщений

BEGIN_MESSAGE_MAP(CTDCtrlPropPage, COlePropertyPage)

//{{AFX_MSG_MAP(CTDCtrlPropPage)

// ПРИМЕЧАНИЕ: мастер классов будет добавлять и удалять здесь

//         макросы схемы сообщений.

// НЕ РЕДАКТИРУЙТЕ то, что здесь находится.

//}}AFX_MSG_MAP END_MESSAGE_MAP ()

/////////////////////////////////
// Инициализация фабрики классов и формирование GUID

IMPLEMENT_OLECREATE_EX(CTDCtrlPropPage,

"TDCTRL.TDCtrlPropPage.1", Oxc0377507,
Oxb276, Oxlldl, Oxba, Oxe9, 0, OxaO,
Oxc9, Ox8c, Oxc4, 'Охбе)

/////////////////////////////////
// CTDCtrlPropPage::CTDCtrlPropPageFactory::UpdateRegistry —
// Добавляет и удаляет записи системного реестра
// для класса CTDCtrlPropPage

BOOL CTDCtrlPropPage::CTDCtrlPropPageFactory::UpdateRegistry

(BOOL bRegister) {

if (bRegister)

return AfxOleRegisterPropertyPageClass(AfxGetlnstanceHandle(),

m_clsid, IDS_TDCTRL_PPG) else

return AfxOleUnregisterClass(m_clsid, NULL); }

/////////////////////////////////
// CTDCtrlPropPage::CTDCtrlPropPage — Конструктор

CTDCtrlPropPage::CTDCtrlPropPage() :

COlePropertyPage(IDD, IDS_TDCTRL_PPG_CAPTION) !

//({AFX_DATA_INIT(CTDCtrlPropPage)

// ПРИМЕЧАНИЕ: мастер классов будет добавлять здесь
//        операторы инициализации переменных-членов.
// НЕ РЕДАКТИРУЙТЕ то, что здесь находится.
//}}AFXDATAINIT

}

/////////////////////////////////
//CTDCtrlPropPage::DoDataExchange- Осуществляет обмен данными

// между страницей и связанными с ней свойствами

void CTDCtrlPropPage::DoDataExchange(CDataExchange* pDX) { ' //{(AFX_DATA_MAP(CTDCtrlPropPage)

// ПРИМЕЧАНИЕ: мастер классов будет добавлять здесь

//        вызовы DDP-, DDX- и DDV-функций.

// НЕ РЕДАКТИРУЙТЕ то,что здесь находится.

//}}AFX_DATA_MAP  

DDP_PostProcessing(pDX); }

///////////////////////////////////////////////////

// Обработчики сообщений класса CTDCtrlPropPage

Функция AfxOleRegisterPropertyPageClass() предназначена для регистрации класса страницы свойств в системном реестре. Благодаря этому страница свойств может использоваться другими контейнерами, которые поддерживают внедрение элементов управления ActiveX. Регистрационная запись с указанием имени страницы свойств и ее местоположения в системе обновляется всякий раз при вызове данной функции.

Обратите внимание, что в конструкторе базового класса CDlePropertyPage, от которого порожден класс CTDCtrlPropPage, можно указать идентификатор шаблона диалогового окна, положенного в основу страницы свойств, а также идентификатор строки заголовка.

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

Модификация шаблона

Элемент управления ActiveX, автоматически созданный мастером ControlWizard, можно изменить с помощью мастера ClassWizard. Мы внесем следующие изменения в имеющийся шаблон:

Сделать все это можно путем внесения изменений в файлы TDCtrlCtl.CPPи TDCtrlCtl.H.

Изменение формы, размеров и цвета элемента управления TDCtrl

В окне компилятора VisualC++ в меню View выберите команду ClassWizard, чтобы запустить мастер ClassWizard, и выполните действия, перечисленные далее.

1.    В диалоговом окне MFCClassWizard перейдите на вкладку Automation.

2.    В списке Classname выберите класс CTDCtrlctrl.

3.   С помощью кнопки AddProperty откройте одноименное диалоговое  окно (рис. 22.9).


Рис. 22.9. Добавление свойства TDShape в окне AddProperty

4.    В поле Externalname введите имя свойства TDShape.

5.    В группе опций Implementation (тип реализации) выберите переключатель Membervariable(значение свойства будет храниться в переменной-члене).

6.    В списке Туре (тип свойства) выберите тип данных BOOL. Обратите внимание, что в ходе выполнения предыдущих установок в поле Notificationfunction(функция обработки изменений) автоматически появилась запись OnTDShapeChanged, а для переменной-члена (поле Variablename) было подобрано имя m_tDShape, также автоматически.

7.    Щелкните на кнопке ОК, чтобы принять заданные установки и вернуться к окну MFCClassWizard.

8.    Еще раз щелкните на кнопке AddProperty, и перед вами повторно откроется диалоговое окно AddProperty.

9.    В этот раз в списке Externalnameвыберите свойство BackColor.

10. В группе опций Implementationвыберите переключатель Stock(базовое свойство).

11. Щелкните на кнопке ОК, с тем чтобы вернуться к диалоговому окну MFCClassWizard(рис. 22.10).


Рис. 22.10. Мастер ClassWizard добавляет в класс TDCtrlCtrl новые свойства - BackColor и TDShape

12. Щелкните на кнопке ОК, и мастер ClassWizardвнесет изменения в код элемента управления.

Мастер классов добавит в файл TDCTRLCTL.Hобъявление функции OnTDShapeChanged ( ) и переменной m_tDShape, а в файл TDCTRLCTL.CPPдобавит стандартную реализацию указанной функции и поместит в схему диспетчеризации соответствующие макросы.

Все перечисленные изменения автоматически вносились мастером классов. Те­перь наступает наш черед производить изменения.

Возвращаемся к файлу TDCTRLCTL.CPP

В следующем листинге показаны изменения, вносимые в файл TDCTRLCTL.CPP(выделены полужирным шрифтом).

///////////////////////////////////////////

// CTDCtrlCtrl::OnDraw- Функция отображения

void CTDCtrlCtrl::OnDraw(CDC* pdc, const CRect SrcBounds,

const CRect srclnvalid) {

CBrush* pOldBrush;

CBrush NewBrush;

CPen* pOldPen;

CPen NewPen;

pdc->Fil!Rect(rcBounds, CBrush::FromHandle((HBRUSH)

GetStockObject(WHITE_BRUSH)));

NewPen.CreatePen(PS_SOLID, 3, RGB(0, 0,0));
pOldPen = {CPen*)pdo->SelectObject(SNewPen),

// Создание желтой кисти

NewBrush.CreateSolidBrush(RGB(255, 255, 0)); pOldBrush = (CBrush*)pdc->SelectObject(SNewBrush);

// Рисование прямоугольника с заливкой
pdc->Rectangle(rcBounds);
pdc->SelectObject(pOldPen);
pdc->SelectObject(pOldBrush);

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

Реакция на события мыши

В данном параграфе будет показано, как заставить элемент управления TDCtrlреагировать на действия, осуществляемые с помощью мыши. Мы сделаем так, что элемент управления после щелчка на нем указателем мыши изменит свой цвет на светло-серый и в нем отобразятся системное время и дата.

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

1.   В диалоговом окне MFClassWizard перейдите на вкладку Automation.

2.  В списке Classname выберите класс CTDCtrlctrl.

3.   С помощью кнопки AddProperty откройте диалоговое окно AddProperty.

4.  В поле External names введитеHitTDCtrl.

5.   В группе опций Implementation выберите переключатель Member variable.

6.   В списке Туре выделите тип данных ole^color и очистите поле Notification function.

7.    Щелкните на кнопке ОК, чтобы принять сделанные установки и вернуться к диалоговому окну MFCClassWizard(рис. 22.11).

8.   Перейдите на вкладку MessageMaps.

9.   В списке Classname выберите класс CTDCtrlctrl.

10. В списке Messages выберите сообщение wm_lbuttondown.

11. Щелкните на кнопке AddFunction.

12. Повторите действия, указанные в пунктах 10 и 11, по отношению к сообщению wm_lbuttonup. Теперь ваше диалоговое окно должно выглядеть так, как показано на рис. 22.12.


Рис. 22.11. Свойство HitTDCtrl позволит элементу управления реагировать на события мыши


Рис. 22.12. Добавление обработчиков событий мыши

13. Щелкните на кнопке ОК, и мастер ClassWizard внесет изменения в код элемента управления.

Мастер классов автоматически добавит в файл TDCTRLCTL.Hобъявление переменной m_hitTDCtrl, а в файл TDCTRLCTL.CPP— стандартные реализации обработчиков OnLButtonDown() и OnLButtonUp().

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

Файл TDCTRLCTL.H

В данный файл заголовков, сразу после объявления деструктора, следует помес­тить прототип новой функции HitTDCtrl(), предназначенной для изменения цвета элемента управления в ответ на щелчок мышью.

// Реализация

protected:
-CTDCtrlCtrl() ;

void HitTDCtrl(CDC* pdc); // изменение цвета

Далее нужно написать код этой функции.

Возвращаемся к файлу TDCTRLCTL.CPP

При щелчке левой кнопкой мыши на элементе управления должен измениться цвет его прямоугольной области. Частично это событие обрабатывается функцией DoPropExchange(). Ниже показана исправленная версия данной функции в ЛОбав-ленной строкой, которая выделена полужирным шрифтом:

/////////////////////////////////////////

// CTDCtrlCtrl::DoPropExchange— Поддержка постоянства элемента

void CTDCtrlCtrl::DoPropExchange(CPropExchange* pPX) {

ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));

COleControl::DoPropExchange(pPX);

}

// TODO: Поместите здесь вызовы РХ_-функций для каждого
//      постоянного пользовательского свойства. ;

// Цвет элемента управления меняется на светло-серый
PX_Color(pPX, _T("HitTDCtrl"), m_hitTDCtrl, RGB(200, 200, 200)); }

Эта функция отвечает за инициализацию переменной m_hitTDCtrl, которой присваивается значение, соответствующее светло-серому цвету. Функция рх_соlоr () осуществляет обмен данными между переменной m_hitTDCtrl и свойством HitTDCtrl.

Мастер классов создал стандартные реализации функций OnLButtonDown() и OnLButtonUp() .Ниже показано, какие изменения вносятся в эти функции.

Функция OnLButtonDown() получает контекст устройства для рисования и вызывает функцию HitTDCtrl(), осуществляющую вывод текущих значений времени и даты. После отпускания левой кнопки мыши функция OnLButtonUp() вызывает функцию InvalidateControl(), что является сигналом системе послать элементу управления сообщение wm_paint.

void CTDCtrlCtrl::OnLButtonDown(DINT nFlags, CPoint point) {

// TODO: Здесь добавьте собственный код обработчика

CDC* pdc;

// Отображение даты к времени
pdc = GetDC() ;
HitTDCtrl(pdc); ReleaseDC(pdc);

COleControl::OnLButtonDown(nFlags, point); }

void CTDCtrlCtrl::OnLButtonUp(OINT nFlags, CPoint point)
{                                 
// TODO: Здесь добавьте собственный код обработчика

InvalidateControl();

COleControl::OnLButtonUp(nFlags, point); }

Ниже показан код функции HitTDCtrl(),который следует добавить в конец файла TDCTRLCTL.CPP:

void CTDCtrlCtrl::HitTDCtrl(CDC* pdc)
{

CBrush* pOldBrush;

CBrush hitBrush (TranslateColor(m_hitTDCtrl));

CRect re;

TEXTMETRIC tm;

struct tm *date_time;

time_t timer;

// Устанавливается прозрачный фон pdc->SetBkMode(TRANSPARENT);

GetClientRect(re);

pQldBrush = pdc->SelectObject(ShitBrush);

// Рисование прямоугольника с заливкой pdc->Rectangle(re);

// Получение даты и времени time(Stimer);

date_tirae = localtime(stimer); 
const CStringS strtime = asctime(date_time);

// Получение информации о шрифте и вывод надписи
pdc->GetTextMetrics (Ј.tm); pdc->SetTextAlign(TA_CENTER | TA_TOP);

pdc->ExtTextOut((re.left + re.right) /2,  
(re.top + re.bottom - tm.tmHeight) /2,
ETO__CLIPPED, re, strtime,
strtime.GetLengthO - I, NULL);
pdc->SelectObject(pOldBrush);
}

Цвет кисти определяется на основании переменной m_hitTDCtrl, которая была модифицирована в функции DoPropExchange(). Заливке подвергается вся область элемента управления. Значения даты и времени запрашиваются с применением стандартных функций языка С — time() и localtime().

Тестирование элемента управления TDCtrl

Для тестирования окончательной версии элемента управления TDCtrl воспользуемся программой MicrosoftWord. Вызовите окно вставки объектов и выберите из списка элемент управления TDCtrl. После вставки в документ элемент управления можно масштабировать (рис. 22.13).


Рис. 22.13. Измененные размеры элемента управления TDCtrl


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

Теперь наведите указатель мыши на элемент управления и щелкните левой кнопкой мыши, в результате чего желтый цвет прямоугольной области будет заменен серым и в ней отобразятся текущие значения времени и даты (рис. 22.14).