ActiveX с нуля


         

Активное и неактивное состояния Active X.


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

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

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



Базовые компоненты ActiveX.


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



Добавление базового свойства.


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



Для добавления базового свойства ForeColor выполните следующие действия.

1.

В меню View выберите ClassWizard.

2.      Выберите вкладку Automation.

3.      Из списка Class name выберите CMyCtrlCtrl.

4.      Щелкните на кнопке Add Property.

5.      В диалоге Add Property

из списка External name выберите ForeColor.

6.      В разделе Implementation выберите Stock ( базовый).

7.      Щелкните на кнопке OK.

ClassWizard изменит файлы класса MyCtrlCtrl и MyCtrl.odl. Значение свойства ForeColor можно модифицировать из контейнера, используя методы GetForeColor и SetForeColor, входящие в класс COleControl.

Значение свойства ForeColor поддерживается классом COleControl. Функция SetForeColor вызывает после установки значения свойства автоматически функцию OnForeColorChanged, которая, в свою очередь вызывает метод InvalidateControl, что ведет к перерисовке элемента управления.



Добавление базовых методов к контрольному элементу.


Базовые методы уже реализованы в классе COleControl. Например, COleControl содержит функцию, которая поддерживает Refresh метод для ActiveX. Вход в карте диспетчеризации для этого метода DISP_STOCKFUNC_REFRESH.

COleControl поддерживает два базовых метода – DoClick и Refresh. Метод Refresh вызывается для обновления отображения элемента, а метод DoClick – вызывается для пересылки события Click в контейнер. Вход в карте диспетчеризации для метода DoClick - DISP_STOCKFUNC_DOCLICK.

Для добавления базового метода к элементу Вы должны:

1.

Загрузить Ваш проект.

2.      В меню View выбрать ClassWizard.

3.      Выбрать вкладку Automation.

4.      Выберете имя класса из списка Class name.

5.      Щелкнуть на кнопке Add Method.

6.      В списке External name выберете имя Refresh или DoClick.

7.      Щелкните Ok.

8.      Закройте ClassWizard, нажав на кнопку OK.



Добавление базовых событий в контрольный элемент.


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

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

Событие

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

Примечания

Click

void FireClick( )

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

DblClick

void FireDblClick( )

Посылается, когда  BUTTONDBLCLK

сообщение принимается.

Вход в карте событий -  EVENT_STOCK_DBLCLICK( )

Error

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

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

Вход в карте событий - EVENT_STOCK_ERROREVENT( )

KeyDown

void FireKeyDown( short nChar, short nShiftState )

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

Вход в карте событий - EVENT_STOCK_KEYDOWN( )

KeyPress

void FireKeyPress( short* pnChar

)

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

Вход в карте событий - EVENT_STOCK_KEYPRESS( )

KeyUp

void FireKeyUp( short nChar, short nShiftState )

Посылается, когда  WM_SYSKEYUP

или WM_KEYUP сообщения получены.

Вход в карте событий -  EVENT_STOCK_KEYUP( )

MouseDown

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

Полылается, когда BUTTONDOWN

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

MouseMove

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

Посылается, когда WM_MOUSEMOVE

событие получено элементом.

Вход в карте событий - EVENT_STOCK_MOUSEMOVE( )

MouseUp

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

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

ReadyStateChange

void FireReadyStateChange( )

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

Добавление базовых событий к контрольному элементу проще, чем пользовательских, так как посылка события в контейнер уже реализована в базовом классе COleControl. Для того, чтобы добавить к контрольному элементу, созданному с помощью СontrolWizard, событие, например, KeyPress, надо выполнить следующие действия с помощью Class Wizard:

1.

Загрузить проект.

2.      В меню View выбрать Class Wizard.

3.      Щелкнуть на вкладке ActiveX Events.

4.      Выбрать имя Вашего контрольного элемента в списке Class Name.

5.      Щелкнуть на кнопке Add Event.

6.      В списке External Name выбрать KeyPress или другое из встроенных событий.

7.      Нажать  кнопку OK.

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



Добавление базовых свойств к контрольному элементу.


Для добавления любого базового свойства Вы должны выполнить следующие действия:

1.

Загрузить Ваш проект.

2.      В меню View выбрать ClassWizard.

3.      Выбрать вкладку Automation.

4.      Выберете имя класса из списка Class name.

5.      Щелкнуть на кнопке Add Property.

6.      В списке External Name выбрать нужное свойство, например, Caption или Font.

При этом в группе Implementation автоматически выбирается Stock.

7.      Щелкнуть на кнопке OK для закрытия диалога Add Property.

8.      Щелкнуть на кнопке Ok для закрытия ClassWizard.



Добавление другой страницы свойств.


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

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

Для того, чтобы создать дополнительную страницу Вы должны выполнить следующие действия:

1.      Выбрать закладку ResourceView  в Project Workspace.

2.      В меню Insert щелкнуть на строке Resource.

3.      Выбрать ресурс Dialog для создания нового диалога и дважды щелкнуть мышкой.

4.      Удалить кнопки OK и Cancel.

5.      Щелкнуть правой кнопкой мыши и откройте окно Dialog Properties. Впечатайте идентификатор диалога, например, IDD_PROPPAGE_NEWPAGE.

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

7.      Дважды щелкнуть кнопкой мыши на окне диалога для запуска ClassWizard.

8.      Добавить новый класс в диалоге Adding a Class.

9.      Ввести имя нового класса в диалоге New Class в элементе Class Name.

10.   Для изменения имена файлов можно щелкнуть на кнопке Change.

11.  В качестве базового класса Вы должны выбрать ColePropertyPage в списке Base Class.

12.   В элементе Dialog ID выбрать IDD_PROPPAGE_NEWPAGE.

13.  Щелкнуть на OK, чтобы создать класс. Можно закрыть ClassWizard.


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

BEGIN_PROPPAGEIDS(CMyCtrlCtrl, 2)

    PROPPAGEID(CMyCtrlPropPage::guid)

   PROPPAGEID(CAddtlPropPage::guid)

...

END_PROPPAGEIDS(CMyCtrlCtrl)

Здесь CAddtlPropPage – имя нового класса. В макросе BEGIN_PROPPAGEIDS измените второй параметр (счетчик страниц) , увеличив его на 1.

В этот же файл реализации элемента включите h-файл нового класса страницы.

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

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

2.      Дважды щелкните на папке String Table.

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

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

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

передавался в качестве параметра идентификатор диалога-страницы свойств:

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

{

    if (bRegister)

        return AfxOleRegisterPropertyPageClass(AfxGetInstanceHandle(),

                     m_clsid, IDS_ADDPAGE);

    else

        return AfxOleUnregisterClass(m_clsid, NULL);

}

6.      Измените конструктор нового класса CAddtlPropPage

так, чтобы IDS_ADDPPG_CAPTION передавался в COlePropertyPage конструктор:

CAddtlPropPage::CAddtlPropPage() :

// ****** Add your code below this line ********** //

     COlePropertyPage(IDD,  IDS_ADDPPG_CAPTION)

// ****** Add your code above this line ********** //

{

    //{{AFX_DATA_INIT(CAddtlPropPage)

    // NOTE: ClassWizard will add member initialization here

    //    DO NOT EDIT what you see in these blocks of generated code !

    //}}AFX_DATA_INIT

}

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


Добавление интерфейса окна свойств.


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

1.

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

Откройте папку Dialog и щелкните мышью на IDD_PROPPAGE_MYCTRL, чтобы запустить редактор диалоговых окон.

2.      Вставьте статический элемент для текста – Форма.

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

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



Добавление элемента для задания текста.


1.

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

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

3.      Вставьте статический элемент с заголовком Текст.

4.      Добавьте элемент редактирования, задав ему идентификатор IDC_CAPTION.

5.     


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

В классе MyCtrlPpg добавлена автоматически переменная m_Caption, а также изменен текст метода DoDataExchange:

void CMyCtrlPropPage::DoDataExchange(CDataExchange* pDX)

{

            //{{AFX_DATA_MAP(CMyCtrlPropPage)

            DDP_CBIndex(pDX, IDC_CBSHAPE, m_nShape, _T("Shape") );

            DDX_CBIndex(pDX, IDC_CBSHAPE, m_nShape);

            DDP_Text(pDX, IDC_CAPTION, m_Caption, _T("Caption") );

            DDX_Text(pDX, IDC_CAPTION, m_Caption);

            //}}AFX_DATA_MAP

            DDP_PostProcessing(pDX);

}



Добавление элемента управления в приложение.


Давайте попробуем использовать наш контрольный элемент в приложении.

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

Для этого выполним следующие действия:

1.


В меню Project выберем пункт Add To Project, а затем пункт Components and Controls.

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

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

4.     

Обратите внимание на появление в панели Controls значка – картинки нашего элемента. Вставьте элемент в диалог, щелкните правой кнопкой мыши на элементе и выберите пункт Properties. Установите свойства элемента. Задайте идентификатор элемента – IDC_MYCTRL.

5.     

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

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



Добавление кода в файлы, созданные мастером.


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

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

CMyCtrlCtrl::CMyCtrlCtrl()

{

     InitializeIIDs(&IID_DMyCtrl, &IID_DMyCtrlEvents);

     // TODO: Initialize your control's instance data here.

     // Добавьте инициализацию Вашего элемента здесь.

}

CMyCtrlCtrl::~CMyCtrlCtrl()

{

     // TODO: Cleanup your control's instance data here.

}

Обратите внимание, что базовый класс для нашего контрольного элемента – класс СOleControl.



Добавление методов в элемент ActiveX.


Методы добавлябтся в элемент ActiveX для того, чтобы контейнер мог вызвать их их для управления элементом. Методы делятся на две группы: базовые ( их только два – Refresh и DoClick) и пользовательские.


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

Измените метод DoChangeTimer:

BOOL CMyCtrlCtrl::DoChangeTimer()

{

  if ( timerOn )

  {

       KillTimer( idTimer );

       timerOn =FALSE;   

  }

  else

  {

       idTimer = SetTimer( ID_TIMER, m_timeSleep, NULL );

       timerOn =TRUE;

  }

  return timerOn;

}

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



Добавление новых свойств, методов и событий в элемент ActiveX MyCtrl.


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

Добавление пользовательского свойства Sleep – задержки между срабатываниями таймера не должно вызвать у Вас затруднений. Пусть задание значения этого свойства выполняется через методы Set/Get, тип свойства – long. В классе CmyCtrlCtrl добавьте переменную типа long m_timeSleep, измените методы Get/Set, как показано ниже:

long CMyCtrlCtrl::GetSleep()

{

     return m_timeSleep;

}

void CMyCtrlCtrl::SetSleep(long nNewValue)

{

     m_timeSleep = nNewValue;

     SetModifiedFlag();

}

Добавьте в функцию  CMyCtrlCtrl::DoPropExchange следующую строку, обеспечивающую сохранение свойства Sleep:

     PX_Long(pPX, _T("Sleep"),  m_timeSleep, 1000);

Добавьте в страницу свойств элемента элемент типа редактирования для задания численного значения m_timeSleep и свяжите его со свойством Sleep.

Для добавления таймера включите в класс CMyCtrlCtrl переменные

           

UINT idTimer;            // для идентификатора таймера

            BOOL timerOn;          // для определения состояния таймера – включен.отключен.

Установку таймера нужно добавить не в конструкторе, а в методе по обработке сообщения WM_CREATE. Для обработки этого сообщения используйте ClassWizard:

#define ID_TIMER   1001 //идентификатор таймера

int CMyCtrlCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

     if (COleControl::OnCreate(lpCreateStruct) == -1)

          return -1;

    

     idTimer = SetTimer( ID_TIMER, m_timeSleep, NULL );

     timerOn = FALSE;  

     return 0;

}

Удаление таймера можно выполнить при обработке сообщения WM_CLOSE. Добавьте обработчик этого сообщения с помощью ClassWizard:


void CMyCtrlCtrl::OnClose()

{

     if ( timerOn )

     {

          KillTimer( idTimer );

          timerOn =FALSE;   

     }

     CWnd::OnClose();

}

Используя ClassWizard, добавьте обработчик события WM_TIMER в класс CMyCtrlCtrl.

void CMyCtrlCtrl::OnTimer(UINT nIDEvent)

{

            m_tick++;                   

            SetModifiedFlag();

            InvalidateControl();

           

            COleControl::OnTimer(nIDEvent);

}

Здесь m_tick – переменная, добавленная в класс CMyCtrlCtrl для подсчета тиков таймера:

            ULONG m_tick;

Метод просто изменяет значение переменной m_tick и вызывает перерисовку элемента.

Для вывода значения m_tick измените метод отрисовки элемента OnDraw -  добавьте в конец его строки:

  CString strTick;

  strTick.Format("%d",m_tick);

  pdc->ExtTextOut((rcBounds.left + rcBounds.right)/2,(rcBounds.top + tm.tmHeight),

                  ETO_CLIPPED, rcBounds, strTick, strTick.GetLength(),NULL);

Эти строки выведут значение m-tick в верхней строке элемента.


Добавление пользовательских методов к контрольному элементу.


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

Для этого Вы должны:

1.

Загрузить Ваш проект.

2.      В меню View выбрать ClassWizard.

3.      Выбрать вкладку Automation.

4.      Выберете имя класса из списка Class name.

5.      Щелкнуть на кнопке Add Method.

6.      Впечатать в списке External name имя метода MyMethod.

7.      В элемент Internal name впечатайте имя внутренней функции или оставьте значение, предлагаемое ClassWizard по умолчанию.

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

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

10.  Щелкните Ok.

11.  Закройте ClassWizard, нажав на кнопку OK.



Добавление пользовательских событий в контрольный элемент.


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



Добавление пользовательских свойств c помощью ClassWizard.


Для добавления пользовательского свойства с применением ClassWizard Вы должны выполнить следующие действия:

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.

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

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



Добавление пользовательских свойств в элемент ActiveX.


Пользовательские свойства не реализованы в классе COleControl. Эти свойства обычно используются для выделения определенного состояния ActiveX. Пользовательские свойства осуществляются с помощью четырех способов:

1. Переменной-члена ( member variable );

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

2.      Переменной и оповещения  ( member variable with notification );

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

3.      Методов Get/Set;

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

4.      С помощью параметров.

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



Добавление пользовательского события с помощью ClassWizard.


Следующий пример добавляет  событие пользователя с именем ClickIn. Для другого события просто смените имя и параметры события.

1.

Загрузите Ваш проект.

2.      В меню View выберете  ClassWizard.

3.      Щелкните на вкладке ActiveX Events.

4.      Выберете имя контрольного элемента в списке Class Name.

5.      Нажмите на кнопке Add Event.

6.      В поле External  name впечатайте имя события – ClickIn.

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

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

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

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



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


Для добавления свойства цвета фигуры в невыбранном состоянии выполните следующие действия.

1.

Выберите в меню View пункт ClassWizard.

2.      Выберите в открывшемся диалоге закладку Automation.

3.      В выпадающем списке Class Name выберите класс CMyCtrlCtrl (рис. 8).

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

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

6.      Добавьте переменную типа OLE_COLOR в файл MyCtrlCtrl.h.

7.      Добавьте строку

            PX_Long(pPX, _T("SelectColor"), (long&) m_selectColor, RGB(255,0,0));

в функцию void CMyCtrlCtrl::DoPropExchange(CPropExchange* pPX):

void CMyCtrlCtrl::DoPropExchange(CPropExchange* pPX)

{

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

     COleControl::DoPropExchange(pPX);

     PX_Short(pPX, _T("Shape"), m_nShape, 0);

     PX_Long(pPX, _T("SelectColor"), (long&) m_selectColor, RGB(255,0,0));

}

8.      Внесите изменения в методы GetSelectColor() и SetSelectColor():

OLE_COLOR CMyCtrlCtrl::GetSelectColor()

{

     return m_selectColor;

}

void CMyCtrlCtrl::SetSelectColor(OLE_COLOR nNewValue)

{

     m_selectColor = nNewValue;

     SetModifiedFlag();

     InvalidateControl();

}



Добавление пользовательского свойства для отображения картинок.


Для этого Вы должны выполнить следующие действия:

1.

Загрузить Ваш проект.

2.      В меню View

выбрать ClassWizard.

3.      Выбрать вкладку Automation.

4.      Выберете имя класса из списка Class name.

5.      Щелкнуть на кнопке Add Property.

6.      В списке External

Name напечатать нужное свойство, например, ControlPicture.

7.      В группе Implementation

указать Get/Set Methods.

8.      В списке Type

выбрать тип свойства LPPICTUREDISP.

9.      В элементах Get Function и SetFunction установить имена функций.

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

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

ClassWizard добавит код в файл (.H):

afx_msg LPPICTUREDISP GetControlPicture();

afx_msg void SetControlPicture(LPPICTUREDISP newValue);

Также будут внесены изменения в код (.CPP) файла:

DISP_PROPERTY_EX(CMyCtrlCtrl, "ControlPicture", GetControlPicture, SetControlPicture, VT_PICTURE)

– в карту диспетчеризации и

LPPICTUREDISP СMyCtrlCtrl::GetControlPicture()

{

    // TODO: Add your property handler here

    return NULL;

}

void CMyCtrlCtrl::SetControlPicture(LPPICTUREDISP newValue)

{

    // TODO: Add your property handler here

    SetModifiedFlag();

}

Имена класса и методов могут, естественно, отличаться от указанных.



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


Элемент будет обрабатывать сообщения от мыши.

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

События добавляются к элементу управления с помощью ClassWizard.


Чтобы добавить событие Select:

1.

Щелкните на вкладке ActiveX Events в диалоге ClassWizard (рис. 10).

2.      Щелкните на кнопке Add Event (рис. 11).

3.     

Введите в поле External name имя свойства Select. При этом мастер устанавливает внутреннее имя (имя функции, вызывающей запуск события) FireSelect. Событие должно иметь параметр. Укажем его в списке Parameter list: это IsSelected типа BOOL.

Щелкните кноку OK.

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

В списке Messages выберите событие WM_LBUTTONDOWN и нажмите кнопку Add Function.

5.      Внесите изменения в функцию OnLButtonDown:

void CMyCtrlCtrl::OnLButtonDown(UINT nFlags, CPoint point)

{

     COleControl::OnLButtonDown(nFlags, point);

     m_bSelected = !m_bSelected;

    

InvalidateControl();

     FireSelect(m_bSelected);

}



Добавление события, посылаемого в контейнер.


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


 

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



Добавление страницы базовых свойств.


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

BEGIN_PROPPAGEIDS(CMyCtrlCtrl, 2)

            PROPPAGEID(CMyCtrlPropPage::guid)

            PROPPAGEID(CLSID_CColorPropPage)

END_PROPPAGEIDS(CMyCtrlCtrl)

Здесь CLSID_CcolorPropPage – идентификатор страницы для задания цвета. Обратите внимание на значение 2 в первой строке раздела – это количество страниц свойств.

Откомпилируйте и постройте новый контрольный элемент и проверьте, как он работает с использованием ActiveX Control Test Container. Задайте свойства цвета в странице свойств. Проверьте, как меняется цвет фигуры при щелчке на ней левой кнопки мыши.



Добавление страницы свойств для шрифта.


Измените вручную раздел страниц свойств в файле MyCtrlCtrl.

BEGIN_PROPPAGEIDS(CMyCtrlCtrl, 3)

            PROPPAGEID(CMyCtrlPropPage::guid)

            PROPPAGEID(CLSID_CColorPropPage)

            PROPPAGEID(CLSID_CFontPropPage)

END_PROPPAGEIDS(CMyCtrlCtrl)

Здесь CLSID_CСolorPropPage – идентификатор страницы для задания цвета, а CLSID_CFontPropPage  - идентификатор страницы для задания шрифта. Обратите внимание на значение 3 в первой строке раздела – это количество страниц свойств.



Добавление свойств пользователя к элементу управления.


К элементу ActiveX можно добавить свойства двух видов: базовые и пользовательские. Базовые свойства уже предусмотрены в базовом классе COleControl.

Добавим к элементу управления два пользовательских свойства: Shape – вид отображаемой в элементе фигуры, Selected – определяет состояние элемента ( включен/выключен).

Для добавления свойства Shape необходимо выполнить следующие действия:

1.

Выберите в меню View пункт ClassWizard.

2.      Выберите в открывшемся диалоге закладку Automation.

3.      В выпадающем списке Class Name выберите класс CmyCtrlCtrl (рис. 8).

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

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

6.     

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

7.      При выходе из окна мастера ClassWizard c помощью щелчков на кнопках OK генерируются три новые функции в классе элемента управления:

short CMCtrlCtrl::GetShape();

void CMCtrlCtrl::SetShape(short nNewValue);

BOOL CMCtrlCtrl::GetSelected();

Обратите внимание на изменение текста в файле CMyCtrlCtrl.cpp. В карте диспетчеризации (если Вы не знаете, что это такое, не пугайтесь – после общего знакомства с процессом создания элемента ActiveX мы рассмотрим тексты файлов более подробно)  появились дополнительные строчки:

BEGIN_DISPATCH_MAP(CMyCtrlCtrl, COleControl)

            //{{AFX_DISPATCH_MAP(CMyCtrlCtrl)


            DISP_PROPERTY_EX(CMyCtrlCtrl, "Shape", GetShape, SetShape, VT_I2)

            DISP_PROPERTY_EX(CMyCtrlCtrl, "Selected", GetSelected, SetNotSupported, VT_BOOL)

            //}}AFX_DISPATCH_MAP

            DISP_FUNCTION_ID(CMyCtrlCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)

END_DISPATCH_MAP()

Каждое добавленное свойство приводит к появлению нового макроса DISP_PROPERTY_EX в карте диспетчеризации.

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

BOOL m_bSelected;

short m_nShape;

Эти переменные надо инициализировать разумными значениями в конструкторе класса CMyCtrlCtrl:

            m_bSelected = FALSE;

            m_nShape = 0;

А теперь изменим добавленные мастером функции Get/Set:

short CMyCtrlCtrl::GetShape()

{

     return m_nShape;

}

void CMyCtrlCtrl::SetShape(short nNewValue)

{

     m_nShape = nNewValue;

     SetModifiedFlag();

     InvalidateControl();

}

BOOL CMyCtrlCtrl::GetSelected()

{

     return m_bSelected;

}


Добавление свойства с переменной членом и сообщением об изменении его.


Пусть MyProp – свойство, при изменении которого не нужна специальная обработка контрольного элемента. Тогда для его реализации можно использовать способ, связанный только с переменной-членом класса, и с оповещением об изменении свойства.

Для добавления свойства MyProp необходимо:

1.

Загрузить Ваш проект.

2.      В меню View

выбрать ClassWizard.

3.      Выбрать вкладку Automation.

4.      Выберете имя класса из списка Class name.

5.      Щелкнуть на кнопке Add Property.

6.      В списке External

Name напечатать нужное свойство, например, MyProp.

7.      В группе Implementation

указать Member variable ods.

8.      В списке Type

выбрать тип свойства, например, short.

9.      Проверить, что Notification function содержит OnMyPropChanged, а Variable name – m_myProp.

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

Заметьте, что в разделе Implementation появился текст:

short m_myProp;

void OnMyPropChanged();

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

ClassWizard изменяет код и в файле .H, и в файле .ODL, и в файле .CPP.

Така наше свойство – переменная- член класса, то в файле .H появилась соответствующие строки:

            short m_myProp;

            afx_msg void OnMyPropChanged();

В файле .CPP в карте диспетчеризации добавится макрос:

DISP_PROPERTY_NOTIFY(CMyCtrlCtrl, "MyProp", m_myProp, OnMyPropChanged, VT_I2)

Макрос DISP_PROPERTY_NOTIFY связывает имя свойства MyProp с:

·         Переменной класса M_myProp;

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

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

ClassWizard также добавляет определение функции OnMyPropChanged в файл .CPP:

void CMyCtrlCtrl::OnMyPropChanged()

{

     // TODO: Add notification handler code

     SetModifiedFlag();

}

В файл .ODL добавляется строка, определяющая уникальный идентификатор свойства:

            [id(1)] short MyProp;

Если Вы не хотите использовать нотификационную функцию, то удалите ее имя в мастере.



Добавление текста в элемент управления.


Добавим базовое свойство  Caption для вывода текстового сообщения в элемент управления, а также базовое свойство Font для задания шрифта для этого сообщения.

1.

Выберите в меню View пункт ClassWizard.

2.      Выберите в открывшемся диалоге закладку Automation.

3.      В выпадающем списке Class Name выберите класс CMyCtrlCtrl (рис. 8).

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

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

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

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

ClassWizard создает код для добавления свойств, изменяя CMyCtrlCtrl класс и MyCtrl.odl файл.

Карта диспетчеризации приобретает следующий вид:

BEGIN_DISPATCH_MAP(CMyCtrlCtrl, COleControl)

            //{{AFX_DISPATCH_MAP(CMyCtrlCtrl)

            DISP_PROPERTY_EX(CMyCtrlCtrl, "Shape", GetShape, SetShape, VT_I2)

            DISP_PROPERTY_EX(CMyCtrlCtrl, "Selected", GetSelected, SetNotSupported, VT_BOOL)

            DISP_PROPERTY_EX(CMyCtrlCtrl, "SelectColor", GetSelectColor, SetSelectColor, VT_COLOR)

            DISP_STOCKPROP_FORECOLOR()

            DISP_STOCKPROP_CAPTION()

            DISP_STOCKPROP_FONT()

            //}}AFX_DISPATCH_MAP

            DISP_FUNCTION_ID(CMyCtrlCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)

END_DISPATCH_MAP()

Макросы DISP_STOCKPROP_FORECOLOR(), DISP_STOCKPROP_CAPTION(), DISP_STOCKPROP_FONT() разрешают использование базовых свойств. Для доступа к ним можно использовать методы Get/Set, определенные в классе COleControl, а также методы OnTextChanged(), OnFontChanged(), OnForeColorChanged(), которые вызываются при выполнении метода Set.

Все методы можно переопределить.

Получить значение свойства Caption можно не только с помощью метода GetText(), но и используя метод InternalGetText. Последний рекомендуется использовать всегда, когда возвращаемый текст нельзя модифицировать, так как этот метод возвращает ссылку на константную строку:

const CString& strCaption = InternalGetText();



Добавление взаимодействия элемента управления и контейнера.


Посмотрите внимательно файлы, созданные мастером: MyСtrl.h b MyСtrl.cpp.

В файле MyCtrl.h приведены методы, которые Вы можете использовать для управления элементом ActiveX (эти методы выделены жирным шрифтом):

class CMyCtrl : public CWnd

{

protected:

     DECLARE_DYNCREATE(CMyCtrl)

public:

     CLSID const& GetClsid()

     {

          static CLSID const clsid

              = { 0x49e2f37f, 0xb48d, 0x11d3, { 0xa2, 0xb0, 0x0, 0xa0, 0xc9, 0x55, 0xfa, 0x9e } };

          return clsid;

     }

     virtual BOOL Create(LPCTSTR lpszClassName,

          LPCTSTR lpszWindowName, DWORD dwStyle,

          const RECT& rect,

          CWnd* pParentWnd, UINT nID,

          CCreateContext* pContext = NULL)

     { return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID); }

    BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle,

          const RECT& rect, CWnd* pParentWnd, UINT nID,

          CFile* pPersist = NULL, BOOL bStorage = FALSE,

          BSTR bstrLicKey = NULL)

     { return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID,

          pPersist, bStorage, bstrLicKey); }

// Attributes

public:

     short GetShape();

     void SetShape(short);

     short Get_Shape();

     void Set_Shape(short);

     BOOL GetSelected();

     void SetSelected(BOOL);

     OLE_COLOR GetForeColor();

     void SetForeColor(OLE_COLOR);

     unsigned long GetSelectColor();

     void SetSelectColor(unsigned long);

     CString GetCaption();

     void SetCaption(LPCTSTR);

     COleFont GetFont();

     void SetFont(LPDISPATCH);

     unsigned long GetTextColor();

     void SetTextColor(unsigned long);

// Operations

public:

     void AboutBox();

};

 

Добавьте в диалог кнопки, позволяющие менять свойства элемента. В примере добавлены 4 элемента. Элемент – кнопка “Сменить форму”с идентификатором IDC_SHAPE позволит менять форму, отображаемую в элементе. Элемент редактирования с идентификатором IDC_NEWTEXT позволит указать текст, выводимый в элементе ActiveX. Кнопка “Сменить текст” с идентификатором IDC_TEXT позволит при нажатии на нее  изменить текст в элементе ActiveX.


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

Запустите ClassWizard и на закладке Member Variables задайте переменные для элементов с иденгтификаторами IDC_MESSAGE, IDC_NEWTEXT, IDC_MYCTRL.

На закладке Message Map выберете класс СtestMyCtrlDlg. Задайте обработку событий, связанных с щелчками на кнопках IDC_TEXT и IDC_SHAPE. Если Вы выберете в списке Object Ids элемент IDC_MYCTRL, то в списке событий Вы увидите то единственное событие, которое мы посылаем в контейнер – Select. Добавьте обработчик для него – функцию с именем OnSelectMyCtrl.

Измените текст в файле TestMyCtrl.cpp:

void CTestMyCtrlDlg::OnSelectMyctrl(BOOL IsSelected)

{

     if ( IsSelected )

     m_Message = "Выбран";

     else

     m_Message = "Не выбран";    

     UpdateData(FALSE);

}

void CTestMyCtrlDlg::OnShape()

{

     short shape = m_MyCtrl.Get_Shape();

     if ( shape < 2 )

     m_MyCtrl.Set_Shape(++shape);

     else

     m_MyCtrl.Set_Shape(0);

}

void CTestMyCtrlDlg::OnText()

{

     UpdateData(TRUE);

     m_MyCtrl.SetCaption(m_newText);

         

}

Обратите внимание на использование методов элемента ActiveX: вызовы методов выделены жирным шрифтом.

Перестройте приложение и запустите его (рис. 22).




Доступ к свойствам контейнера.


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

Для доступа к свойствам контейнера можно использовать метод класса COleControl::GetAmbientProperty:

BOOL GetAmbientProperty( DISPID dwDispid, VARTYPE

vtProp, void* pvProp );

 Первый параметр этого метода – идентификатор для свойства контейнера. Стандартные свойства контейнера приводятся в файле OLECTL.H:

#define DISPID_AMBIENT_BACKCOLOR                     (-701)

#define DISPID_AMBIENT_DISPLAYNAME                 (-702)

#define DISPID_AMBIENT_FONT                                                (-703)

#define DISPID_AMBIENT_FORECOLOR                                  (-704)

#define DISPID_AMBIENT_LOCALEID                          (-705)

#define DISPID_AMBIENT_MESSAGEREFLECT          (-706)

#define DISPID_AMBIENT_SCALEUNITS                      (-707)

#define DISPID_AMBIENT_TEXTALIGN                                    (-708)

#define DISPID_AMBIENT_USERMODE                                    (-709)

#define DISPID_AMBIENT_UIDEAD                               (-710)

#define DISPID_AMBIENT_SHOWGRABHANDLES    (-711)

#define DISPID_AMBIENT_SHOWHATCHING             (-712)

#define DISPID_AMBIENT_DISPLAYASDEFAULT      (-713)

#define DISPID_AMBIENT_SUPPORTSMNEMONICS  (-714)

#define DISPID_AMBIENT_AUTOCLIP                           (-715)

#define DISPID_AMBIENT_APPEARANCE                    (-716)

#define DISPID_AMBIENT_CODEPAGE                         (-725)

#define DISPID_AMBIENT_PALETTE                              (-726)

#define DISPID_AMBIENT_CHARSET                             (-727)

#define DISPID_AMBIENT_TRANSFERPRIORITY       (-728)


#define DISPID_AMBIENT_RIGHTTOLEFT                    (-732)

#define DISPID_AMBIENT_TOPTOBOTTOM                 (-733)

Второй параметр функции GetAmbientProperty определяет ожидаемый тип свойства, а третий – адрес, по которому будет записано значение. При успешном выполнении и поддержке свойства контейнером функция возвращает TRUE, в противном случае –FALSE.

Ниже приводится фрагмент кода для получения свойства контейнера UserMode:

BOOL bUserMode;

   if( !GetAmbientProperty( DISPID_AMBIENT_USERMODE,

      VT_BOOL, &bUserMode ) )

      bUserMode = TRUE;

Класс COleControl содержит методы для доступа к часто используемым свойствам контейнера (таблица 5):

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

Функция

Назначение

OLE_COLOR AmbientBackColor()

Возвращает значение фонового цвета окружения.

CString AmbientDisplayName()

Возвращает имя элемента в контейнере.

OLE_COLOR

AmbientForeColor()

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

LPFONTDISP

AmbientFont()

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

short AmbientTextAlign()

Возвращает тип тектового выравнивания в контейнере:

0 – обычное выравнивание;

1 – по левому краю;

2 – по центру;

3 – по правому краю.

BOOL AmbientUIDead()

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

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

BOOL

AmbientUserMode()

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

BOOL GetAmbientProperty( DISPID dwDispid, VARTYPE

vtProp, void*

pvProp );

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

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


Файл MyCtrl.cpp


// MyCtrl.cpp : Implementation of CMyCtrlApp and DLL registration.

#include "stdafx.h"

#include "MyCtrl.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

CMyCtrlApp NEAR theApp;

const GUID CDECL BASED_CODE _tlid =

                        { 0x49e2f37b, 0xb48d, 0x11d3, { 0xa2, 0xb0, 0, 0xa0, 0xc9, 0x55, 0xfa, 0x9e } };

const WORD _wVerMajor = 1;

const WORD _wVerMinor = 0;

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

// CMyCtrlApp::InitInstance - DLL initialization

BOOL CMyCtrlApp::InitInstance()

{

            BOOL bInit = COleControlModule::InitInstance();

            if (bInit)

            {

                        // TODO: Add your own module initialization code here.

            }

            return bInit;

}

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

// CMyCtrlApp::ExitInstance - DLL termination

int CMyCtrlApp::ExitInstance()

{

            // TODO: Add your own module termination code here.

            return COleControlModule::ExitInstance();

}

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

// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)

{

            AFX_MANAGE_STATE(_afxModuleAddrThis);

            if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))

                        return ResultFromScode(SELFREG_E_TYPELIB);

            if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))

                        return ResultFromScode(SELFREG_E_CLASS);

            return NOERROR;

}

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

// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)

{

            AFX_MANAGE_STATE(_afxModuleAddrThis);

            if (!AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor))

                        return ResultFromScode(SELFREG_E_TYPELIB);

            if (!COleObjectFactoryEx::UpdateRegistryAll(FALSE))

                        return ResultFromScode(SELFREG_E_CLASS);

            return NOERROR;

}


}

CString CMyCtrl::GetCaption()

{

            CString result;

            GetProperty(DISPID_CAPTION, VT_BSTR, (void*)&result);

            return result;

}

void CMyCtrl::SetCaption(LPCTSTR propVal)

{

            SetProperty(DISPID_CAPTION, VT_BSTR, propVal);

}

COleFont CMyCtrl::GetFont()

{

            LPDISPATCH pDispatch;

            GetProperty(DISPID_FONT, VT_DISPATCH, (void*)&pDispatch);

            return COleFont(pDispatch);

}

void CMyCtrl::SetFont(LPDISPATCH propVal)

{

            SetProperty(DISPID_FONT, VT_DISPATCH, propVal);

}

unsigned long CMyCtrl::GetTextColor()

{

            unsigned long result;

            GetProperty(0x4, VT_I4, (void*)&result);

            return result;

}

void CMyCtrl::SetTextColor(unsigned long propVal)

{

            SetProperty(0x4, VT_I4, propVal);

}

long CMyCtrl::GetSleep()

{

            long result;

            GetProperty(0x5, VT_I4, (void*)&result);

            return result;

}

void CMyCtrl::SetSleep(long propVal)

{

            SetProperty(0x5, VT_I4, propVal);

}

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

// CMyCtrl operations

void CMyCtrl::DoClick()

{

            InvokeHelper(DISPID_DOCLICK, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);

}

BOOL CMyCtrl::DoChangeTimer()

{

            BOOL result;

            InvokeHelper(0x6, DISPATCH_METHOD, VT_BOOL, (void*)&result, NULL);

            return result;

}

void CMyCtrl::AboutBox()

{

            InvokeHelper(0xfffffdd8, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);

}






Файл MyCtrl.h


#if !defined(AFX_MYCTRL_H__49E2F385_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED_)

#define AFX_MYCTRL_H__49E2F385_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

// MyCtrl.h : main header file for MYCTRL.DLL

#if !defined( __AFXCTL_H__ )

            #error include 'afxctl.h' before including this file

#endif

#include "resource.h"       // main symbols

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

// CMyCtrlApp : See MyCtrl.cpp for implementation.

class CMyCtrlApp : public COleControlModule

{

public:

            BOOL InitInstance();

            int ExitInstance();

};

extern const GUID CDECL _tlid;

extern const WORD _wVerMajor;

extern const WORD _wVerMinor;

//{{AFX_INSERT_LOCATION}}

// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_MYCTRL_H__49E2F385_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED)


#if !defined(AFX_MYCTRL_H__80E61EB0_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_)

#define AFX_MYCTRL_H__80E61EB0_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

// Machine generated IDispatch wrapper class(es) created by Microsoft Visual C++

// NOTE: Do not modify the contents of this file.  If this class is regenerated by

//  Microsoft Visual C++, your modifications will be overwritten.

// Dispatch interfaces referenced by this interface

class COleFont;

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

// CMyCtrl wrapper class

class CMyCtrl : public CWnd

{

protected:

            DECLARE_DYNCREATE(CMyCtrl)

public:

            CLSID const& GetClsid()

            {

                        static CLSID const clsid

                                    = { 0x49e2f37f, 0xb48d, 0x11d3, { 0xa2, 0xb0, 0x0, 0xa0, 0xc9, 0x55, 0xfa, 0x9e } };

                        return clsid;

            }

            virtual BOOL Create(LPCTSTR lpszClassName,

                        LPCTSTR lpszWindowName, DWORD dwStyle,

                        const RECT& rect,

                        CWnd* pParentWnd, UINT nID,

                        CCreateContext* pContext = NULL)

            { return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID); }

    BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle,

                        const RECT& rect, CWnd* pParentWnd, UINT nID,

                        CFile* pPersist = NULL, BOOL bStorage = FALSE,

                        BSTR bstrLicKey = NULL)

            { return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID,

                        pPersist, bStorage, bstrLicKey); }

// Attributes

public:

            short GetShape();

            void SetShape(short);

            short Get_Shape();

            void Set_Shape(short);

            BOOL GetSelected();

            void SetSelected(BOOL);

            OLE_COLOR GetForeColor();

            void SetForeColor(OLE_COLOR);

            unsigned long GetSelectColor();

            void SetSelectColor(unsigned long);

            CString GetCaption();

            void SetCaption(LPCTSTR);

            COleFont GetFont();

            void SetFont(LPDISPATCH);

            unsigned long GetTextColor();

            void SetTextColor(unsigned long);

            long GetSleep();

            void SetSleep(long);

// Operations

public:

            void DoClick();

            BOOL DoChangeTimer();

            void AboutBox();

};

//{{AFX_INSERT_LOCATION}}

// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_MYCTRL_H__80E61EB0_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_)



Файл MyCtrl.odl


// MyCtrl.odl : type library source for ActiveX Control project.

// This file will be processed by the Make Type Library (mktyplib) tool to

// produce the type library (MyCtrl.tlb) that will become a resource in

// MyCtrl.ocx.

#include <olectl.h>

#include <idispids.h>

[ uuid(49E2F37B-B48D-11D3-A2B0-00A0C955FA9E), version(1.0),

  helpfile("MyCtrl.hlp"),

  helpstring("MyCtrl ActiveX Control module"),

  control ]

library MYCTRLLib

{

            importlib(STDOLE_TLB);

            importlib(STDTYPE_TLB);

            //  Primary dispatch interface for CMyCtrlCtrl

            [ uuid(49E2F37D-B48D-11D3-A2B0-00A0C955FA9E),

              helpstring("Dispatch interface for MyCtrl Control"), hidden ]

            dispinterface _DMyCtrl

            {

                        properties:

                                    // NOTE - ClassWizard will maintain property information here.

                                    //    Use extreme caution when editing this section.

                                    //{{AFX_ODL_PROP(CMyCtrlCtrl)

                                    [id(1)] short Shape;

                                    [id(0)] short _Shape;

                                    [id(2)] boolean Selected;

                                    [id(DISPID_FORECOLOR), bindable, requestedit] OLE_COLOR ForeColor;

                                    [id(3)] OLE_COLOR SelectColor;

                                    [id(DISPID_CAPTION), bindable, requestedit] BSTR Caption;

                                    [id(DISPID_FONT), bindable] IFontDisp* Font;

                                    [id(4)] OLE_COLOR TextColor;

                                    [id(5)] long Sleep;

                                    //}}AFX_ODL_PROP

                        methods:

                                    // NOTE - ClassWizard will maintain method information here.

                                    //    Use extreme caution when editing this section.


                                    //{{AFX_ODL_METHOD(CMyCtrlCtrl)

                                    [id(6)] boolean DoChangeTimer();

                                    //}}AFX_ODL_METHOD

                                    [id(DISPID_ABOUTBOX)] void AboutBox();

            };

            //  Event dispatch interface for CMyCtrlCtrl

            [ uuid(49E2F37E-B48D-11D3-A2B0-00A0C955FA9E),

              helpstring("Event interface for MyCtrl Control") ]

            dispinterface _DMyCtrlEvents

            {

                        properties:

                                    //  Event interface has no properties

                        methods:

                                    // NOTE - ClassWizard will maintain event information here.

                                    //    Use extreme caution when editing this section.

                                    //{{AFX_ODL_EVENT(CMyCtrlCtrl)

                                    [id(1)] void Select(boolean IsSelected);

                                    [id(2)] void Tick(long ltick);

                                    //}}AFX_ODL_EVENT

            };

            //  Class information for CMyCtrlCtrl

            [ uuid(49E2F37F-B48D-11D3-A2B0-00A0C955FA9E),

              helpstring("MyCtrl Control"), control ]

            coclass MyCtrl

            {

                        [default] dispinterface _DMyCtrl;

                        [default, source] dispinterface _DMyCtrlEvents;

            };

            //{{AFX_APPEND_ODL}}

            //}}AFX_APPEND_ODL}}

};


Файл MyCtrlCtl.cpp


// MyCtrlCtl.cpp : Implementation of the CMyCtrlCtrl ActiveX Control class.

#include "stdafx.h"

#include "MyCtrl.h"

#include "MyCtrlCtl.h"

#include "MyCtrlPpg.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

IMPLEMENT_DYNCREATE(CMyCtrlCtrl, COleControl)

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

// Message map

BEGIN_MESSAGE_MAP(CMyCtrlCtrl, COleControl)

            //{{AFX_MSG_MAP(CMyCtrlCtrl)

            ON_WM_LBUTTONDOWN()

            ON_WM_TIMER()

            ON_WM_CREATE()

            ON_WM_CLOSE()

            //}}AFX_MSG_MAP

            ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)

END_MESSAGE_MAP()

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

// Dispatch map

BEGIN_DISPATCH_MAP(CMyCtrlCtrl, COleControl)

            //{{AFX_DISPATCH_MAP(CMyCtrlCtrl)

            DISP_PROPERTY_EX(CMyCtrlCtrl, "Shape", GetShape, SetShape, VT_I2)

            DISP_PROPERTY_EX(CMyCtrlCtrl, "Selected", GetSelected, SetNotSupported, VT_BOOL)

            DISP_PROPERTY_EX(CMyCtrlCtrl, "SelectColor", GetSelectColor, SetSelectColor, VT_COLOR)

            DISP_PROPERTY_EX(CMyCtrlCtrl, "TextColor", GetTextColor, SetTextColor, VT_COLOR)

            DISP_PROPERTY_EX(CMyCtrlCtrl, "Sleep", GetSleep, SetSleep, VT_I4)

            DISP_FUNCTION(CMyCtrlCtrl, "DoChangeTimer", DoChangeTimer, VT_BOOL, VTS_NONE)

            DISP_DEFVALUE(CMyCtrlCtrl, "Shape")

            DISP_STOCKPROP_FORECOLOR()

            DISP_STOCKPROP_CAPTION()

            DISP_STOCKPROP_FONT()

            //}}AFX_DISPATCH_MAP

            DISP_FUNCTION_ID(CMyCtrlCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)

END_DISPATCH_MAP()

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


// Event map

BEGIN_EVENT_MAP(CMyCtrlCtrl, COleControl)

            //{{AFX_EVENT_MAP(CMyCtrlCtrl)

            EVENT_CUSTOM("Select", FireSelect, VTS_BOOL)

            EVENT_CUSTOM("Tick", FireTick, VTS_I4)

            //}}AFX_EVENT_MAP

END_EVENT_MAP()

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

// Property pages

// TODO: Add more property pages as needed.  Remember to increase the count!

BEGIN_PROPPAGEIDS(CMyCtrlCtrl, 3)

            PROPPAGEID(CMyCtrlPropPage::guid)

            PROPPAGEID(CLSID_CColorPropPage)

            PROPPAGEID(CLSID_CFontPropPage)

END_PROPPAGEIDS(CMyCtrlCtrl)

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

// Initialize class factory and guid

IMPLEMENT_OLECREATE_EX(CMyCtrlCtrl, "MYCTRL.MyCtrlCtrl.1",

            0x49e2f37f, 0xb48d, 0x11d3, 0xa2, 0xb0, 0, 0xa0, 0xc9, 0x55, 0xfa, 0x9e)

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

// Type library ID and version

IMPLEMENT_OLETYPELIB(CMyCtrlCtrl, _tlid, _wVerMajor, _wVerMinor)

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

// Interface IDs

const IID BASED_CODE IID_DMyCtrl =

                        { 0x49e2f37d, 0xb48d, 0x11d3, { 0xa2, 0xb0, 0, 0xa0, 0xc9, 0x55, 0xfa, 0x9e } };

const IID BASED_CODE IID_DMyCtrlEvents =

                        { 0x49e2f37e, 0xb48d, 0x11d3, { 0xa2, 0xb0, 0, 0xa0, 0xc9, 0x55, 0xfa, 0x9e } };

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

// Control type information

static const DWORD BASED_CODE _dwMyCtrlOleMisc =

            OLEMISC_ACTIVATEWHENVISIBLE |

            OLEMISC_SETCLIENTSITEFIRST |

            OLEMISC_INSIDEOUT |

            OLEMISC_CANTLINKINSIDE |

            OLEMISC_RECOMPOSEONRESIZE;

IMPLEMENT_OLECTLTYPE(CMyCtrlCtrl, IDS_MYCTRL, _dwMyCtrlOleMisc)



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

// CMyCtrlCtrl::CMyCtrlCtrlFactory::UpdateRegistry -

// Adds or removes system registry entries for CMyCtrlCtrl

BOOL CMyCtrlCtrl::CMyCtrlCtrlFactory::UpdateRegistry(BOOL bRegister)

{

            // TODO: Verify that your control follows apartment-model threading rules.

            // Refer to MFC TechNote 64 for more information.

            // If your control does not conform to the apartment-model rules, then

            // you must modify the code below, changing the 6th parameter from

            // afxRegApartmentThreading to 0.

            if (bRegister)

                        return AfxOleRegisterControlClass(

                                    AfxGetInstanceHandle(),

                                    m_clsid,

                                    m_lpszProgID,

                                    IDS_MYCTRL,

                                    IDB_MYCTRL,

                                    afxRegApartmentThreading,

                                    _dwMyCtrlOleMisc,

                                    _tlid,

                                    _wVerMajor,

                                    _wVerMinor);

            else

                        return AfxOleUnregisterClass(m_clsid, m_lpszProgID);

}

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

// CMyCtrlCtrl::CMyCtrlCtrl - Constructor

CMyCtrlCtrl::CMyCtrlCtrl()

{

            InitializeIIDs(&IID_DMyCtrl, &IID_DMyCtrlEvents);

            m_bSelected = FALSE;

            m_nShape = 0;

            SetForeColor(RGB(0,255,0));

            m_timeSleep = 1000;

            m_tick = 0;

}

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

// CMyCtrlCtrl::~CMyCtrlCtrl - Destructor

CMyCtrlCtrl::~CMyCtrlCtrl()

{

}

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



// CMyCtrlCtrl::OnDraw - Drawing function

void CMyCtrlCtrl::OnDraw(

                                    CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)

{

            CPen pen;

            CBrush foreBrush, backBrush;

            CPoint points[3];

            pdc->SaveDC();

            pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));

            ULONG foreColor = TranslateColor(GetForeColor());

            backBrush.CreateSolidBrush(TranslateColor(AmbientBackColor()));

            foreBrush.CreateSolidBrush(GetSelected() ? foreColor : TranslateColor(m_selectColor));

            pdc->FillRect(rcBounds, &backBrush);

            pdc->SelectObject(&pen);

            pdc->SelectObject(&foreBrush);

            switch (m_nShape)

            {

            case 0:

                        pdc->Ellipse(rcBounds);

                        break;

            case 1:

                        pdc->Rectangle(rcBounds);

                        break;

            case 2:

                        points[0].x = rcBounds.left;

                        points[0].y = rcBounds.bottom - 1;

                        points[1].x = (rcBounds.left + rcBounds.right - 1) / 2;

                        points[1].y = rcBounds.top;

                        points[2].x = rcBounds.right - 1;

                        points[2].y = rcBounds.bottom - 1;

                        pdc->Polygon(points, 3);

                        break;

            }

            CFont *pOldFont;

            TEXTMETRIC tm;

           

            pdc->SetTextColor(TranslateColor(m_textColor));

            pdc->SetBkMode(TRANSPARENT);

           

            CString strCaption = GetText();

            pOldFont = SelectStockFont(pdc);

            pdc->GetTextMetrics(&tm);

            pdc->SetTextAlign(TA_CENTER|TA_TOP);

            pdc->ExtTextOut((rcBounds.left + rcBounds.right)/2,(rcBounds.top + rcBounds.bottom - tm.tmHeight)/2,



                                                            ETO_CLIPPED, rcBounds, strCaption, strCaption.GetLength(),NULL);

           

            CString strTick;

            strTick.Format("%d",m_tick);

            pdc->ExtTextOut((rcBounds.left + rcBounds.right)/2,(rcBounds.top + tm.tmHeight),

                                                            ETO_CLIPPED, rcBounds, strTick, strTick.GetLength(),NULL);

           

            pdc->RestoreDC(-1);

}

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

// CMyCtrlCtrl::DoPropExchange - Persistence support

void CMyCtrlCtrl::DoPropExchange(CPropExchange* pPX)

{

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

            COleControl::DoPropExchange(pPX);

            PX_Short(pPX, _T("Shape"), m_nShape, 0);

            PX_Long(pPX, _T("SelectColor"), (long&) m_selectColor, RGB(255,0,0));

            PX_Long(pPX, _T("TextColor"), (long&) m_textColor, RGB(0,0,0));

            PX_Long(pPX, _T("Sleep"),  m_timeSleep, 1000);

}

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

// CMyCtrlCtrl::OnResetState - Reset control to default state

void CMyCtrlCtrl::OnResetState()

{

            COleControl::OnResetState();  // Resets defaults found in DoPropExchange

            // TODO: Reset any other control state here.

}

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

// CMyCtrlCtrl::AboutBox - Display an "About" box to the user

void CMyCtrlCtrl::AboutBox()

{

            CDialog dlgAbout(IDD_ABOUTBOX_MYCTRL);

            dlgAbout.DoModal();

}

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

// CMyCtrlCtrl message handlers

short CMyCtrlCtrl::GetShape()

{

            return m_nShape;



}

void CMyCtrlCtrl::SetShape(short nNewValue)

{

            m_nShape = nNewValue;

            SetModifiedFlag();

            InvalidateControl();

}

BOOL CMyCtrlCtrl::GetSelected()

{

            return m_bSelected;

}

void CMyCtrlCtrl::OnLButtonDown( UINT nFlags, CPoint point)

{

            COleControl::OnLButtonDown(nFlags, point);

            m_bSelected = !m_bSelected;

            InvalidateControl();

            FireSelect(m_bSelected);

}

OLE_COLOR CMyCtrlCtrl::GetSelectColor()

{

            return m_selectColor;

}

void CMyCtrlCtrl::SetSelectColor(OLE_COLOR nNewValue)

{

            m_selectColor = nNewValue;

            SetModifiedFlag();

            InvalidateControl();

}

OLE_COLOR CMyCtrlCtrl::GetTextColor()

{

            return m_textColor;

}

void CMyCtrlCtrl::SetTextColor(OLE_COLOR nNewValue)

{

            m_textColor = nNewValue;

            SetModifiedFlag();

            InvalidateControl();

}

long CMyCtrlCtrl::GetSleep()

{

            return m_timeSleep;

}

void CMyCtrlCtrl::SetSleep(long nNewValue)

{

            m_timeSleep = nNewValue;

            SetModifiedFlag();

}

void CMyCtrlCtrl::OnTimer(UINT nIDEvent)

{

            m_tick++;                   

            FireTick(m_tick);

            SetModifiedFlag();

            InvalidateControl();

           

            COleControl::OnTimer(nIDEvent);

}

#define ID_TIMER    1001

int CMyCtrlCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

            if (COleControl::OnCreate(lpCreateStruct) == -1)

                        return -1;

           

            idTimer = SetTimer( ID_TIMER, m_timeSleep, NULL );

            timerOn = FALSE;    

            return 0;

}

void CMyCtrlCtrl::OnClose()

{

            if ( timerOn )

            {

                        KillTimer( idTimer );

                        timerOn =FALSE;     

            }

            CWnd::OnClose();

}

BOOL CMyCtrlCtrl::DoChangeTimer()

{

            if ( timerOn )

            {

                        KillTimer( idTimer );

                        timerOn =FALSE;     

            }

            else

            {

                        idTimer = SetTimer( ID_TIMER, m_timeSleep, NULL );

                        timerOn =TRUE;       

            }

            return timerOn;

}


Файл MyCtrlCtrl.h


#if !defined(AFX_MYCTRLCTL_H__49E2F38D_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED_)

#define AFX_MYCTRLCTL_H__49E2F38D_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

// MyCtrlCtl.h : Declaration of the CMyCtrlCtrl ActiveX Control class.

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

// CMyCtrlCtrl : See MyCtrlCtl.cpp for implementation.

class CMyCtrlCtrl : public COleControl

{

            DECLARE_DYNCREATE(CMyCtrlCtrl)

// Constructor

public:

            CMyCtrlCtrl();

// Overrides

            // ClassWizard generated virtual function overrides

            //{{AFX_VIRTUAL(CMyCtrlCtrl)

            public:

            virtual void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid);

            virtual void DoPropExchange(CPropExchange* pPX);

            virtual void OnResetState();

            //}}AFX_VIRTUAL

            BOOL m_bSelected;

            short m_nShape;

            OLE_COLOR m_selectColor;

            OLE_COLOR m_textColor;

            long m_timeSleep;

            UINT idTimer;

            BOOL timerOn;

            ULONG m_tick;

protected:

            ~CMyCtrlCtrl();

            DECLARE_OLECREATE_EX(CMyCtrlCtrl)    // Class factory and guid

            DECLARE_OLETYPELIB(CMyCtrlCtrl)      // GetTypeInfo

            DECLARE_PROPPAGEIDS(CMyCtrlCtrl)     // Property page IDs

            DECLARE_OLECTLTYPE(CMyCtrlCtrl)              // Type name and misc status

// Message maps

            //{{AFX_MSG(CMyCtrlCtrl)

            afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

            afx_msg void OnTimer(UINT nIDEvent);

            afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

            afx_msg void OnClose();

            //}}AFX_MSG

            DECLARE_MESSAGE_MAP()

// Dispatch maps

            //{{AFX_DISPATCH(CMyCtrlCtrl)

            afx_msg short GetShape();

            afx_msg void SetShape(short nNewValue);


            afx_msg BOOL GetSelected();

            afx_msg OLE_COLOR GetSelectColor();

            afx_msg void SetSelectColor(OLE_COLOR nNewValue);

            afx_msg OLE_COLOR GetTextColor();

            afx_msg void SetTextColor(OLE_COLOR nNewValue);

            afx_msg long GetSleep();

            afx_msg void SetSleep(long nNewValue);

            afx_msg BOOL DoChangeTimer();

            //}}AFX_DISPATCH

            DECLARE_DISPATCH_MAP()

            afx_msg void AboutBox();

// Event maps

            //{{AFX_EVENT(CMyCtrlCtrl)

            void FireSelect(BOOL IsSelected)

                        {FireEvent(eventidSelect,EVENT_PARAM(VTS_BOOL), IsSelected);}

            void FireTick(long ltick)

                        {FireEvent(eventidTick,EVENT_PARAM(VTS_I4), ltick);}

            //}}AFX_EVENT

            DECLARE_EVENT_MAP()

// Dispatch and event IDs

public:

            enum {

            //{{AFX_DISP_ID(CMyCtrlCtrl)

            dispidShape = 1L,

            dispidSelected = 2L,

            dispidSelectColor = 3L,

            dispidTextColor = 4L,

            dispidSleep = 5L,

            dispidDoChangeTimer = 6L,

            eventidSelect = 1L,

            eventidTick = 2L,

            //}}AFX_DISP_ID

            };

};

//{{AFX_INSERT_LOCATION}}

// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_MYCTRLCTL_H__49E2F38D_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED)


Файл MyCtrlPpg.cpp


// MyCtrlPpg.cpp : Implementation of the CMyCtrlPropPage property page class.

#include "stdafx.h"

#include "MyCtrl.h"

#include "MyCtrlPpg.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

IMPLEMENT_DYNCREATE(CMyCtrlPropPage, COlePropertyPage)

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

// Message map

BEGIN_MESSAGE_MAP(CMyCtrlPropPage, COlePropertyPage)

            //{{AFX_MSG_MAP(CMyCtrlPropPage)

            // NOTE - ClassWizard will add and remove message map entries

            //    DO NOT EDIT what you see in these blocks of generated code !

            //}}AFX_MSG_MAP

END_MESSAGE_MAP()

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

// Initialize class factory and guid

IMPLEMENT_OLECREATE_EX(CMyCtrlPropPage, "MYCTRL.MyCtrlPropPage.1",

            0x49e2f380, 0xb48d, 0x11d3, 0xa2, 0xb0, 0, 0xa0, 0xc9, 0x55, 0xfa, 0x9e)

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

// CMyCtrlPropPage::CMyCtrlPropPageFactory::UpdateRegistry -

// Adds or removes system registry entries for CMyCtrlPropPage

BOOL CMyCtrlPropPage::CMyCtrlPropPageFactory::UpdateRegistry(BOOL bRegister)

{

            if (bRegister)

                        return AfxOleRegisterPropertyPageClass(AfxGetInstanceHandle(),

                                    m_clsid, IDS_MYCTRL_PPG);

            else

                        return AfxOleUnregisterClass(m_clsid, NULL);

}

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

// CMyCtrlPropPage::CMyCtrlPropPage - Constructor

CMyCtrlPropPage::CMyCtrlPropPage() :

            COlePropertyPage(IDD, IDS_MYCTRL_PPG_CAPTION)

{

            //{{AFX_DATA_INIT(CMyCtrlPropPage)

            m_nShape = -1;

            m_Caption = _T("");

            m_timeSleep = _T("");

            //}}AFX_DATA_INIT

}

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

// CMyCtrlPropPage::DoDataExchange - Moves data between page and properties

void CMyCtrlPropPage::DoDataExchange(CDataExchange* pDX)

{

            //{{AFX_DATA_MAP(CMyCtrlPropPage)

            DDP_CBIndex(pDX, IDC_CBSHAPE, m_nShape, _T("Shape") );

            DDX_CBIndex(pDX, IDC_CBSHAPE, m_nShape);

            DDP_Text(pDX, IDC_CAPTION, m_Caption, _T("Caption") );

            DDX_Text(pDX, IDC_CAPTION, m_Caption);

            DDP_Text(pDX, IDC_SLEEP, m_timeSleep, _T("Sleep") );

            DDX_Text(pDX, IDC_SLEEP, m_timeSleep);

            //}}AFX_DATA_MAP

            DDP_PostProcessing(pDX);

}

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

// CMyCtrlPropPage message handlers



Файл MyCtrlPpg.h


#if !defined(AFX_MYCTRLPPG_H__49E2F38F_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED_)

#define AFX_MYCTRLPPG_H__49E2F38F_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

// MyCtrlPpg.h : Declaration of the CMyCtrlPropPage property page class.

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

// CMyCtrlPropPage : See MyCtrlPpg.cpp.cpp for implementation.

class CMyCtrlPropPage : public COlePropertyPage

{

            DECLARE_DYNCREATE(CMyCtrlPropPage)

            DECLARE_OLECREATE_EX(CMyCtrlPropPage)

// Constructor

public:

            CMyCtrlPropPage();

// Dialog Data

            //{{AFX_DATA(CMyCtrlPropPage)

            enum { IDD = IDD_PROPPAGE_MYCTRL };

            int                    m_nShape;

            CString            m_Caption;

            CString            m_timeSleep;

            //}}AFX_DATA

// Implementation

protected:

            virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Message maps

protected:

            //{{AFX_MSG(CMyCtrlPropPage)

                        // NOTE - ClassWizard will add and remove member functions here.

                        //    DO NOT EDIT what you see in these blocks of generated code !

            //}}AFX_MSG

            DECLARE_MESSAGE_MAP()

};

//{{AFX_INSERT_LOCATION}}

// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_MYCTRLPPG_H__49E2F38F_B48D_11D3_A2B0_00A0C955FA9E__INCLUDED)



Файл TestMyCtrl.cpp


// TestMyCtrl.cpp : Defines the class behaviors for the application.

//

#include "stdafx.h"

#include "TestMyCtrl.h"

#include "TestMyCtrlDlg.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

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

// CTestMyCtrlApp

BEGIN_MESSAGE_MAP(CTestMyCtrlApp, CWinApp)

            //{{AFX_MSG_MAP(CTestMyCtrlApp)

                        // NOTE - the ClassWizard will add and remove mapping macros here.

                        //    DO NOT EDIT what you see in these blocks of generated code!

            //}}AFX_MSG

            ON_COMMAND(ID_HELP, CWinApp::OnHelp)

END_MESSAGE_MAP()

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

// CTestMyCtrlApp construction

CTestMyCtrlApp::CTestMyCtrlApp()

{

            // TODO: add construction code here,

            // Place all significant initialization in InitInstance

}

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

// The one and only CTestMyCtrlApp object

CTestMyCtrlApp theApp;

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

// CTestMyCtrlApp initialization

BOOL CTestMyCtrlApp::InitInstance()

{

            AfxEnableControlContainer();

            // Standard initialization

            // If you are not using these features and wish to reduce the size

            //  of your final executable, you should remove from the following

            //  the specific initialization routines you do not need.

#ifdef _AFXDLL

            Enable3dControls();                            // Call this when using MFC in a shared DLL

#else

            Enable3dControlsStatic();      // Call this when linking to MFC statically

#endif

            CTestMyCtrlDlg dlg;

            m_pMainWnd = &dlg;

            int nResponse = dlg.DoModal();

            if (nResponse == IDOK)

            {

                        // TODO: Place code here to handle when the dialog is

                        //  dismissed with OK

            }

            else if (nResponse == IDCANCEL)

            {

                        // TODO: Place code here to handle when the dialog is

                        //  dismissed with Cancel

            }

            // Since the dialog has been closed, return FALSE so that we exit the

            //  application, rather than start the application's message pump.

            return FALSE;

}



Файл TestMyCtrl.h


// TestMyCtrl.h : main header file for the TESTMYCTRL application

//

#if !defined(AFX_TESTMYCTRL_H__80E61EA5_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_)

#define AFX_TESTMYCTRL_H__80E61EA5_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

#ifndef __AFXWIN_H__

            #error include 'stdafx.h' before including this file for PCH

#endif

#include "resource.h"              // main symbols

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

// CTestMyCtrlApp:

// See TestMyCtrl.cpp for the implementation of this class

//

class CTestMyCtrlApp : public CWinApp

{

public:

            CTestMyCtrlApp();

// Overrides

            // ClassWizard generated virtual function overrides

            //{{AFX_VIRTUAL(CTestMyCtrlApp)

            public:

            virtual BOOL InitInstance();

            //}}AFX_VIRTUAL

// Implementation

            //{{AFX_MSG(CTestMyCtrlApp)

                        // NOTE - the ClassWizard will add and remove member functions here.

                        //    DO NOT EDIT what you see in these blocks of generated code !

            //}}AFX_MSG

            DECLARE_MESSAGE_MAP()

};

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

//{{AFX_INSERT_LOCATION}}

// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_TESTMYCTRL_H__80E61EA5_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_)



Файл TestMyCtrlDlg.cpp


// TestMyCtrlDlg.cpp : implementation file

//

#include "stdafx.h"

#include "TestMyCtrl.h"

#include "TestMyCtrlDlg.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

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

// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog

{

public:

            CAboutDlg();

// Dialog Data

            //{{AFX_DATA(CAboutDlg)

            enum { IDD = IDD_ABOUTBOX };

            //}}AFX_DATA

            // ClassWizard generated virtual function overrides

            //{{AFX_VIRTUAL(CAboutDlg)

            protected:

            virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

            //}}AFX_VIRTUAL

// Implementation

protected:

            //{{AFX_MSG(CAboutDlg)

            //}}AFX_MSG

            DECLARE_MESSAGE_MAP()

};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)

{

            //{{AFX_DATA_INIT(CAboutDlg)

            //}}AFX_DATA_INIT

}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)

{

            CDialog::DoDataExchange(pDX);

            //{{AFX_DATA_MAP(CAboutDlg)

            //}}AFX_DATA_MAP

}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)

            //{{AFX_MSG_MAP(CAboutDlg)

                        // No message handlers

            //}}AFX_MSG_MAP

END_MESSAGE_MAP()

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

// CTestMyCtrlDlg dialog

CTestMyCtrlDlg::CTestMyCtrlDlg(CWnd* pParent /*=NULL*/)

            : CDialog(CTestMyCtrlDlg::IDD, pParent)

{

            //{{AFX_DATA_INIT(CTestMyCtrlDlg)

            m_Message = _T("");

            m_newText = _T("");

            m_Tick = _T("");

            //}}AFX_DATA_INIT

            // Note that LoadIcon does not require a subsequent DestroyIcon in Win32

            m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

}

void CTestMyCtrlDlg::DoDataExchange(CDataExchange* pDX)


{

            CDialog::DoDataExchange(pDX);

            //{{AFX_DATA_MAP(CTestMyCtrlDlg)

            DDX_Text(pDX, IDC_MESSAGE, m_Message);

            DDX_Control(pDX, IDC_MYCTRL, m_MyCtrl);

            DDX_Text(pDX, IDC_NEWTEXT, m_newText);

            DDX_Text(pDX, IDC_TICK, m_Tick);

            //}}AFX_DATA_MAP

}

BEGIN_MESSAGE_MAP(CTestMyCtrlDlg, CDialog)

            //{{AFX_MSG_MAP(CTestMyCtrlDlg)

            ON_WM_SYSCOMMAND()

            ON_WM_PAINT()

            ON_WM_QUERYDRAGICON()

            ON_BN_CLICKED(IDC_TEXT, OnText)

            ON_BN_CLICKED(IDC_TIMER, OnTimer)

            ON_BN_CLICKED(IDC_SHAPE, OnShape)

            //}}AFX_MSG_MAP

END_MESSAGE_MAP()

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

// CTestMyCtrlDlg message handlers

BOOL CTestMyCtrlDlg::OnInitDialog()

{

            CDialog::OnInitDialog();

            // Add "About..." menu item to system menu.

            // IDM_ABOUTBOX must be in the system command range.

            ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

            ASSERT(IDM_ABOUTBOX < 0xF000);

            CMenu* pSysMenu = GetSystemMenu(FALSE);

            if (pSysMenu != NULL)

            {

                        CString strAboutMenu;

                        strAboutMenu.LoadString(IDS_ABOUTBOX);

                        if (!strAboutMenu.IsEmpty())

                        {

                                    pSysMenu->AppendMenu(MF_SEPARATOR);

                                    pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);

                        }

            }

            // Set the icon for this dialog.  The framework does this automatically

            //  when the application's main window is not a dialog

            SetIcon(m_hIcon, TRUE);                             // Set big icon

            SetIcon(m_hIcon, FALSE);               // Set small icon

           

            // TODO: Add extra initialization here



           

            return TRUE;  // return TRUE  unless you set the focus to a control

}

void CTestMyCtrlDlg::OnSysCommand(UINT nID, LPARAM lParam)

{

            if ((nID & 0xFFF0) == IDM_ABOUTBOX)

            {

                        CAboutDlg dlgAbout;

                        dlgAbout.DoModal();

            }

            else

            {

                        CDialog::OnSysCommand(nID, lParam);

            }

}

// If you add a minimize button to your dialog, you will need the code below

//  to draw the icon.  For MFC applications using the document/view model,

//  this is automatically done for you by the framework.

void CTestMyCtrlDlg::OnPaint()

{

            if (IsIconic())

            {

                        CPaintDC dc(this); // device context for painting

                        SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

                        // Center icon in client rectangle

                        int cxIcon = GetSystemMetrics(SM_CXICON);

                        int cyIcon = GetSystemMetrics(SM_CYICON);

                        CRect rect;

                        GetClientRect(&rect);

                        int x = (rect.Width() - cxIcon + 1) / 2;

                        int y = (rect.Height() - cyIcon + 1) / 2;

                        // Draw the icon

                        dc.DrawIcon(x, y, m_hIcon);

            }

            else

            {

                        CDialog::OnPaint();

            }

}

// The system calls this to obtain the cursor to display while the user drags

//  the minimized window.

HCURSOR CTestMyCtrlDlg::OnQueryDragIcon()

{

            return (HCURSOR) m_hIcon;

}

BEGIN_EVENTSINK_MAP(CTestMyCtrlDlg, CDialog)

    //{{AFX_EVENTSINK_MAP(CTestMyCtrlDlg)

            ON_EVENT(CTestMyCtrlDlg, IDC_MYCTRL, 1 /* Select */, OnSelectMyctrl, VTS_BOOL)

            ON_EVENT(CTestMyCtrlDlg, IDC_MYCTRL, 2 /* Tick */, OnTickMyctrl, VTS_I4)

            //}}AFX_EVENTSINK_MAP



END_EVENTSINK_MAP()

void CTestMyCtrlDlg::OnSelectMyctrl( BOOL IsSelected)

{

            if ( IsSelected )

            m_Message = "Âûáðàí";

            else

            m_Message = "Íå âûáðàí";      

            UpdateData(FALSE);

}

void CTestMyCtrlDlg::OnShape()

{

            short shape = m_MyCtrl.Get_Shape();

            if ( shape < 2 )

            m_MyCtrl.Set_Shape(++shape);

            else

            m_MyCtrl.Set_Shape(0);

}

void CTestMyCtrlDlg::OnText()

{

            UpdateData(TRUE);

            m_MyCtrl.SetCaption(m_newText);

                       

}

void CTestMyCtrlDlg::OnTimer()

{

            m_MyCtrl.DoChangeTimer();

}

void CTestMyCtrlDlg::OnTickMyctrl(long ltick)

{

            m_Tick.Format("%d", ltick);  

            CWnd * pwnd = GetDlgItem(IDC_TICK);

            pwnd->SetWindowText(m_Tick);

}


Файл TestMyCtrlDlg.h


// TestMyCtrlDlg.h : header file

//

//{{AFX_INCLUDES()

#include "myctrl.h"

//}}AFX_INCLUDES

#if !defined(AFX_TESTMYCTRLDLG_H__80E61EA7_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_)

#define AFX_TESTMYCTRLDLG_H__80E61EA7_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

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

// CTestMyCtrlDlg dialog

class CTestMyCtrlDlg : public CDialog

{

// Construction

public:

            CTestMyCtrlDlg(CWnd* pParent = NULL);            // standard constructor

// Dialog Data

            //{{AFX_DATA(CTestMyCtrlDlg)

            enum { IDD = IDD_TESTMYCTRL_DIALOG };

            CString            m_Message;

            CMyCtrl          m_MyCtrl;

            CString            m_newText;

            CString            m_Tick;

            //}}AFX_DATA

            // ClassWizard generated virtual function overrides

            //{{AFX_VIRTUAL(CTestMyCtrlDlg)

            protected:

            virtual void DoDataExchange(CDataExchange* pDX);       // DDX/DDV support

            //}}AFX_VIRTUAL

// Implementation

protected:

            HICON m_hIcon;

            // Generated message map functions

            //{{AFX_MSG(CTestMyCtrlDlg)

            virtual BOOL OnInitDialog();

            afx_msg void OnSysCommand(UINT nID, LPARAM lParam);

            afx_msg void OnPaint();

            afx_msg HCURSOR OnQueryDragIcon();

            afx_msg void OnSelectMyctrl(BOOL IsSelected);

            afx_msg void OnTickMyctrl(long ltick);

            afx_msg void OnText();

            afx_msg void OnTimer();

            afx_msg void OnShape();

            DECLARE_EVENTSINK_MAP()

            //}}AFX_MSG

            DECLARE_MESSAGE_MAP()

};

//{{AFX_INSERT_LOCATION}}

// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_TESTMYCTRLDLG_H__80E61EA7_5D6E_11D4_BCC0_00A0C955FA9E__INCLUDED_)



Идентификаторы DISPID


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



Идентификаторы интерфейсов (IID) автоматизации


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



Использование базового свойства шрифтов в элементах ActiveX.


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

Доступ к шрифтам осуществляется с помощью методов  GetFont, SetFont и  InternalGetFont класса COleControl. Методы GetFont, SetFont используются как обычные методы Get и Set. Если требуется получить параметры шрифта внутри элемента, рекомендуется использовать метод InternalGetFont.

Добавление базового свойства шрифта выполняется, как было описано выше.

Обратите внимание на код, добавляемый к файлу .H:

DISP_STOCKPROP_FONT(),

А ткже на код, добавленный к файлу .ODL:

[id(DISPID_FONT), bindable] IFontDisp* Font;



Использование базовых страниц свойств.


MFC обеспечивает три базовых страницы для использования с ActiveX: CLSID_CColorPropPage, CLSID_CFontPropPage, и CLSID_CpicturePropPage. Эти страницы позволяют пользователю изменить базовый цвет, шрифт и свойства картинки, соответственно.

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

BEGIN_PROPPAGEIDS( CMyCtrlCtrl, 4 )

    PROPPAGEID( CMyCtrlPropPage::guid )

    PROPPAGEID( CLSID_CFontPropPage )

    PROPPAGEID( CLSID_CColorPropPage )

    PROPPAGEID( CLSID_CPicturePropPage )

END_PROPPAGEIDS(CSampleCtrl)

Отметьте, что счетчик страниц в макросе BEGIN_PROPPAGEIDS – 4 и соответствует числу страниц.

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



Использование картинок в ActiveX.


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

Для реализации работы с картинками можно использовать следующее:

·

Класс CPictureHolder.

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

·         Поддержка структуры LPPICTUREDISP, используемой методами Get/Set.

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

·         Страницу свойств для манипулирования свойствами Picture.

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

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



Использование MFC


Ниже описывается создание элементов ActiveX с использованием MFC.

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

Перечислим основные моменты создания ActiveX-элемента управления:

·

создание остова проекта;

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

·         добавление новых свойств;

·         добавление новых методов;

·         добавление событий;

·         корректировка базовой страницы свойств и добавление новых страниц;

·         связывание данных.

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



Использование пользовательского свойства шрифта.


Чтобы реализовать пользовательское свойство, добавьте его с помощью ClassWizard, а затем вручную измените код:

1.

Загрузите Ваш проект.

2.      В меню View выберете ClassWizard.

3.      Выберете вкладку Automation.

4.      Выберете имя класса из списка Class name.

5.      Щелкните на кнопке Add Property.

6.      В списке External Name впечатайте нужное свойство, например, HeadingFont.

7.      При этом в группе Implementation выберете Get/Set Methods.

8.      В списке Return Type выберете LPFONTDISP.

9.      Щелкните на кнопке OK для закрытия диалога Add Property.

10.  Щелкните на кнопке Ok для закрытия ClassWizard.

ClassWizard создаст код в карте диспетчеризации:

DISP_PROPERTY_EX(CSampleCtrl, "HeadingFont", GetHeadingFont,

   SetHeadingFont, VT_DISPATCH)

Макрос DISP_PROPERTY_EX

связывает свойство HeadingFont с соответствующими методами Get and Set -  GetHeadingFont and SetHeadingFont. Тип этого свойства -  VT_DISPATCH.

ClassWizard также добавляет объявление методов GetHeadingFont и SetHeadingFont в файлы .H, а их реализацию – в файл .CPP:

LPDISPATCH CMyCtrlCtrl::GetHeadingFont()

{

 // TODO: Add your property handler here

 return NULL;

}

void CMyCtrlCtrl::SetHeadingFont(LPDISPATCH newValue)

{

 // TODO: Add your property handler here

 SetModifiedFlag();

}

Наконец, ClassWizard изменяет .ODL файл:

[id(1)] IDispatch* HeadingFont;



Использование шрифтов в элементах ActiveX.


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



Изменение диалога “About…” для элемента управления.


ClassWizard создает шаблон для диалога “About…”. Он добавляет также и метод AboutBox, который может быть вызван из контейнера, куда вставляется элемент управления. В качестве иконки в диалоге создается иконка по умолчанию.

Для модификации диалога Вы можете выполнить следующие действия:

1.      Во вкладке Resource откройте папку Icon.

2.      Дважды щелкните мышью на IDI_ABOUTDL и измените иконку по Вашему желанию.

3.     

Во вкладке Resource откройте папку Dialog и измените диалог с IDD_ABOUTBOX_MYCTRL по Вашему желанию (рис. 7).



Изменение кода.


Для добавления страницы свойств вставьте следующую строку после макроса BEGIN_PROPPAGEIDS в файле (.CPP):

PROPPAGEID(CLSID_CPicturePropPage)

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

BEGIN_PROPPAGEIDS(CmyCtrlCtrl, 2)

Для того, чтобы добавить CPictureHolder

объект в класс элемента, Вы долюны вставить следующие строки в файл (.H):

CPictureHolder    m_pic;

Конечно имя переменной типа CPictureHolder может отличаться от m_pic.

Теперь добавьте свойство, используя ClassWizard.



Изменение метода Draw.


Внесенные изменения показаны жирным шрифтом.

void CMyCtrlCtrl::OnDraw(

              CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)

{

     CPen pen;

     CBrush foreBrush, backBrush;

     CPoint points[3];

     pdc->SaveDC();

     pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));

     ULONG foreColor = TranslateColor(GetForeColor());

     backBrush.CreateSolidBrush(TranslateColor(AmbientBackColor()));

     foreBrush.CreateSolidBrush(GetSelected() ? foreColor : TranslateColor(m_selectColor));

     pdc->FillRect(rcBounds, &backBrush);

     pdc->SelectObject(&pen);

     pdc->SelectObject(&foreBrush);

     switch (m_nShape)

     {

     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);

}



Изменение метода OnDraw.


Для выдода тектового сообщения добавьте следующие строки в метод OnDraw(показано жирным шрифтом):

void CMyCtrlCtrl::OnDraw(

                                    CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)

{

     CPen pen;

     CBrush foreBrush, backBrush;

     CPoint points[3];

     pdc->SaveDC();

     pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));

     ULONG foreColor = TranslateColor(GetForeColor());

     backBrush.CreateSolidBrush(TranslateColor(AmbientBackColor()));

     foreBrush.CreateSolidBrush(GetSelected() ? foreColor : TranslateColor(m_selectColor));

     pdc->FillRect(rcBounds, &backBrush);

     pdc->SelectObject(&pen);

     pdc->SelectObject(&foreBrush);

     switch (m_nShape)

     {

     case 0:

           pdc->Ellipse(rcBounds);

           break;

     case 1:

           pdc->Rectangle(rcBounds);

           break;

     case 2:

           points[0].x = rcBounds.left;

           points[0].y = rcBounds.bottom - 1;

           points[1].x = (rcBounds.left + rcBounds.right - 1) / 2;

           points[1].y = rcBounds.top;

           points[2].x = rcBounds.right - 1;

           points[2].y = rcBounds.bottom - 1;

           pdc->Polygon(points, 3);

           break;

     }

     CFont *pOldFont;

     TEXTMETRIC tm;

     //pdc->SetTextColor(TranslateColor(m_textColor));

     //pdc->SetBkMode(TRANSPARENT);

 

     CString strCaption = GetText();

     pOldFont = SelectStockFont(pdc);

     pdc->GetTextMetrics(&tm);

     pdc->SetTextAlign(TA_CENTER|TA_TOP);

 

     pdc->ExtTextOut((rcBounds.left + rcBounds.right)/2,(rcBounds.top + rcBounds.bottom - tm.tmHeight)/2,

                           ETO_CLIPPED, rcBounds, strCaption, strCaption.GetLength(),NULL);

           

            pdc->RestoreDC(-1);

}

Если Вы хотите, добавьте как свойство (TextColor) цвет текста, добавьте переменную m_textColor, измените методы GetTextColor и setTextColor, добавьте строки в метод OnDraw, позволяющие рисовать текст указанного цвета ( в приведенном выше примере эти строки закомментарены).



Изменение приложения с использованием элемента ActiveX.


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

void CTestMyCtrlDlg::OnTimer()

{

            m_MyCtrl.DoChangeTimer();

}

Добавим обработку события Tick, получаемого от элемента ( в ClassWizard при выборе закладки Message Map для класса CtestMyCtrlDlg и элемента IDC_MYCTRL Вы должны увидеть оба пользовательских события – Select и Tick. Добавьте в диалог элемент статичекого типа для вывода строки текста и установите для этого элемента переменную m_Tick типа long.

Для обработки события Tick добавьте метод:

void CTestMyCtrlDlg::OnTickMyctrl(long ltick)

{

  m_Tick.Format("%d", ltick); 

  UpdateData(FALSE);

}

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

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

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



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


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

Для изменения этого изображения Вы можете использовать редактор битмар-изображений.

1.


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

2.      Откройте папку Bitmap.

3.      Дважды щелкните мышкой на IDB_MYCTRL для запуска редактора bitmap.

4.      Измените картинку, как Вам нравится (рис. 6).

5.      В меню File выберите Save для сохранения изображения.

6.     


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