Элемент управления ActiveX можно разрабатывать самим, затем любой программист в состоянии его использовать точно также, как стандартный элемент управления. Готовый элемент управления размещается на диалоговой панели и настраивается установкой значений свойств и обработчиков событий.
Поскольку расширением файла элемента управления ActiveX является ., то иногда элементы управления называют элементами .
Далее в этой работе мы создадим и протестируем свой собственный элемент управления ActiveX - MyClock., который будет выполнять задачу непрерывного вывода текущего времени. Далее мы присоединим готовый компонент в панель Toolbox оболочки, чтобы его можно было использовать в любом приложении наряду со стандартными компонентами.
Войдите в свой каталог выполнения лабораторных работ
Запустите оболочку Microsoft Visual Studio .NET 2003
Создайте новый проект и назовите его
Настройте мастер 

Установите в раскрывающемся списке Solution Configurations режим построения решения ReleaseПосле того, как с помощью мастера мы создали проект и каркасы файлов элемента управления MyClock, дальнейшая работа заключается в его настройке так, чтобы он вел себя нужным образом. Но прежде давайте построим и протестируем проект, чтобы убедиться, что даже каркасный код уже в определенной мере работоспособен.
Выполните компиляцию и компоновку проекта
Войдите в каталог проекта Release и убедитесь, что создан файл MyClock.ocx Чтобы протестировать элемент управления, нужно создать тестовое приложение, которое его использует, как это было с календарем в Lab17. Но на начальной стадии проще воспользоваться утилитой ActiveX Control Test Container, которая предоставляет такие возможности без построения тестового приложения.
Выполните команду главного меню оболочки Tools/ActiveX Control Test Container
В появившемся окне утилиты
В появившемся диалоговом окне
Щелкните по кнопке Элемент управления MyClock рисует в окне утилиты эллипс для начального контроля за способностью изменять размеры. Этот эллипс создан каркасным кодом оболочки в функции OnDraw(). Рамка с маркерами позволяет изменять размеры элемента управления, а сам элемент можно перетаскивать в окне утилиты. Можно поместить в окно утилиты несколько элементов управления.
Через кнопку Таким образом, с помощью утилиты ActiveX Control Test Container на данном этапе мы протестировали визуальный аспект элемента управления MyClock: способность размещаться в родительском окне-контейнере, изменять размеры и местоположение, размещаться в контейнере несколькими копиями.
Утилита позволяет протестировать свойства и события элемента управления, но на данном этапе нам тестировать нечего, поскольку свойства и события в MyClock нами еще не определены.
Закройте ( без сохранения изменений) утилиту ActiveX Control Test Container, чтобы завершить процесс предварительного тестированияВсе управляющие элементы, размещенные в панели Toolbox оболочки, представлены на ней кнопками с пиктограммой и надписью. Подготовим подходящий значок для нашего элемента управления.
В панели Resource View раскройте узел Bitmap и вызовите на редактирование ресурс IDB_MYCLOCK
Воспользуйтесь расположенными в верхней части оболочки кнопками редактора и замените значок по умолчанию

на более подходящий, например такой

Пока элемент управления выводит только пустой эллипс. Нам нужно, чтобы в нем отображалось текущее время в виде текста. Напишем соответствующий код.
В панели Solution Explorer вызовите на редактирование файл MyClockCtrl.cpp
Найдите в нем функцию OnDraw(). Сейчас она выглядит такКод функции OnDraw(), сгенерированный мастером
void CMyClockCtrl::OnDraw(
CDC* pdc,
const CRect rcBounds,
const CRect rcInvalid)
{
if (!pdc)
return;
// TODO: Replace the following code with your own drawing code.
pdc->FillRect(rcBounds,
CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
pdc->Ellipse(rcBounds);
}
Обратите внимание на два последних оператора (выделены красным). Первый оператор заливает прямоугольную область элемента управления белым цветом, второй рисует в ней эллипс пером, установленным по умолчанию.
Замените в функции OnDraw() последний оператор так, чтобы вместо эллипса для проверки внутри выводилась текстовая "затычка" ( заглушка )Измененный код функции OnDraw()
void CMyClockCtrl::OnDraw(
CDC* pdc,
const CRect rcBounds,
const CRect rcInvalid)
{
if (!pdc)
return;
// TODO: Replace the following code with your own drawing code.
// Залить элемент управления
pdc->FillRect(rcBounds,
CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
// pdc->Ellipse(rcBounds); // Построить эллипс
// Вывести текст
CString m_str = "Это мой первый ActiveX";
pdc->ExtTextOut(rcBounds.left,
rcBounds.top,
ETO_CLIPPED,
rcBounds,
m_str,
m_str.GetLength(),
NULL);
}
Функция вывода текста MFC -класса имеет такие аргументы
ETO_CLIPPED устанавливает, что текст будет усечен границами прямоугольной области.CString .NULL, то пробелы между символами заданы по умолчанию.Чтобы увидеть результаты внесенный изменений, выполните следующие действия:
Постройте проект и проверьте его в утилите ActiveX Control Test ContainerДолжен получиться такой результат
Заменим нашу текстовую заглушку на строку, содержащую текущую дату и время. Подобную информацию мы уже получали в Lab6, поэтому повторим эти шаги
Добавьте объявление двух переменных m_strDate и m_strTime в объявление класса CMyClockCtrl файла MyClockCtrl.h для хранения текущих даты и времени. Выполните это без применения мастера - вручнуюИзменение в файле MyClockCtrl.h
class CMyClockCtrl : public COleControl
{
............................................
private:
// Переменная-член текущей даты
CString m_strDate;
// Переменная-член текущего времени
CString m_strTime;
};
В панели
Вновь в панели
Заготовку функции GetCurDate() в файле MyClockCtrl.cpp наполните следующим кодомФункция GetCurDate() в файле MyClockCtrl.cpp
// Заполняет переменную-член m_strDate текущей датой
void CMyClockCtrl::GetCurDate(void)
{
// массив месяцев
char *month[] = {"января"
,"февраля"
,"марта"
,"апреля"
,"мая"
,"июня"
,"июля"
,"августа"
,"сентября"
,"октября"
,"ноября"
,"декабря"};
// массив дней недели
char *week[] = { "воскресенье"
,"понедельник"
,"вторник"
,"среда"
,"четверг"
,"пятница"
,"суббота" };
int m_day, m_month, m_week, m_year;
// Получить текущее время
CTime curTime = CTime::GetCurrentTime();
m_day = curTime.GetDay(); // Число месяца
m_month = curTime.GetMonth(); // 1 - Январь
m_week = curTime.GetDayOfWeek(); // 1 - Воскресенье
m_year = curTime.GetYear(); // Год
char tmp[5]; // Для размещения числа месяца, года и нуль-символа
itoa(m_day, tmp, 10);// Преобразуем целое в строку в десятичном формате
m_strDate = ""; // Заполнить пустой строкой
m_strDate += tmp;
m_strDate += " ";
m_strDate += month[m_month - 1];
m_strDate += " (";
m_strDate += week[m_week - 1];
m_strDate += ") - ";
itoa(m_year, tmp, 10); // Преобразуем число-год в строку-год
m_strDate += tmp;
m_strDate += " г.";
}
Заготовку функции GetCurTime() в файле MyClockCtrl.cpp наполните следующим кодомФункция GetCurTime() в файле MyClockCtrl.cpp
// Заполняет переменную m_strTime текущим временем
void CMyClockCtrl::GetCurTime(void)
{
// Получить текущее время
CTime curTime = CTime::GetCurrentTime();
// Сохранить текущее время в нужном формате
m_strTime = " "; // Два пробела
m_strTime += curTime.Format("%H:%M:%S");
}
Добавьте объявление суммарной переменной m_str в объявление класса CMyClockCtrl файла MyClockCtrl.h для хранения текущих даты и времени. Выполните это без применения мастера - вручнуюИзменение в файле MyClockCtrl.h
class CMyClockCtrl : public COleControl
{
............................................
// Переменная-член текущей даты
CString m_strDate;
// Переменная-член текущего времени
CString m_strTime;
// Заполняет переменную-член m_strDate текущей датой
void GetCurDate(void);
// Заполняет переменную m_strTime текущим временем
void GetCurTime(void);
CString m_str;
};
Отредактируйте функцию OnDraw() так, чтобы она выводила текущие дату и времяФункция OnDraw() в файле MyClockCtrl.cpp
void CMyClockCtrl::OnDraw(
CDC* pdc,
const CRect rcBounds,
const CRect rcInvalid)
{
if (!pdc)
return;
// TODO: Replace the following code with your own drawing code.
// Залить элемент управления
pdc->FillRect(rcBounds,
CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
// pdc->Ellipse(rcBounds); // Построить эллипс
// Получить и вывести текст текущей даты и времени
GetCurDate();
m_str = m_strDate;
GetCurTime();
m_str += m_strTime;
pdc->ExtTextOut(rcBounds.left,
rcBounds.top,
ETO_CLIPPED,
rcBounds,
m_str,
m_str.GetLength(),
NULL);
}
Постройте проект и проверьте его в утилите ActiveX Control Test ContainerДолжен получиться такой результат
Код вывода текстовой информации о текущей дате и времени обновляется только при разрушении окна. Поэтому нужно создать таймер с ежесекундным интервалом срабатывания и разместить код обновления вывода текста управляющего элемента в обработчик таймера. Поскольку дата в обозримый промежуток времени остается неизменной и нет смысла обновлять ее постоянно, ежесекундно вычисляя достаточно объемный код, поместим в обработчик только вызов функции обновления времени.
Чтобы однозначно определить таймер, ему нужно присвоить уникальный идентификатор. Для этого:
Выделите в панели ресурсов Resource View узел MyClock.rc, затем либо через меню Edit оболочки, либо через контекстное меню узла выполните опцию Resource Symbols (символы резурсов)
В диалоговом окне Resource Symbols щелкните на New (создать)
В диалоговом окне
Значение поля Value окна New Symbol можно изменить или оставить по умолчаниюТаймер необходимо установить сразу после создания элемента управления, так что нам нужно связать код, устанавливающий таймер, с событием WM_CREATE элемента управления.
В панели
Создайте обработчик OnCreate(), который заполните такОбработчик OnCreate() в файле MyClockCtrl.cpp
int CMyClockCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (COleControl::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
// Установить таймер
SetTimer(ID_CLOCK_TIMER, 1000, NULL);
return 0;
}
Введенный код создает и запускает таймер с 1000-миллисекундным периодом срабатывания. Начиная с этого момента, каждую секунду Windows будет посылать нашему элементу управления сообщение WM_TIMER, которое мы и будем перехватывать и обрабатывать, обновляя строку времени.
В панели
Найдите сообщение WM_TIMER, создайте для него обработчик, который заполните такОбработчик OnTimer() в файле MyClockCtrl.cpp
void CMyClockCtrl::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
// Переключить вызов на функцию OnDraw()
InvalidateControl();
COleControl::OnTimer(nIDEvent);
}
Предусмотрим остановку
В панели
Найдите сообщение WM_DESTROY, создайте для него обработчик, который заполните такОбработчик OnDestroy() в файле MyClockCtrl.cpp
void CMyClockCtrl::OnDestroy()
{
COleControl::OnDestroy();
// TODO: Add your message handler code here
// Остановить таймер
KillTimer(ID_CLOCK_TIMER);
}
Постройте проект и проверьте его в утилите ActiveX Control Test Container. Время должно меняться ежесекундно
Сохраните в своем каталоге сессию тестирования утилиты Любой элемент управления имеет некоторый неизменный ограниченный набор свойств, которые определяют его основные характеристики. Этот набор называется базовым. Базовые свойства любого элемента управления ActiveX приведены в таблице
| Базовые свойства элементов управления | |
|---|---|
| Свойство | Значение |
Apperance |
Внешний вид (например, 3-мерный или плоский) |
BackColor |
Цвет фона |
BorderStyle |
|
Caption |
Заголовок |
Enabled |
Состояние доступности |
Font |
Шрифт для выводимого текста |
ForeColor |
Цвет переднего плана |
hWnd |
Маркер окна |
ReadyState |
Состояние готовности |
Text |
Текст |
Для практики включим два базовых свойства в наш элемент управления: BackColor и ForeColor.
Откройте панель
Через контекстное меню элемента
В появившемся диалоговом окне Add Property Wizard включите радиокнопку Stock (Готовое, Базовое)
В раскрывающемся списке выберите свойство
Повторите те же действия для добавления в элемент управления свойства ForeColor
Убедитесь, что в панели
Убедитесь, что файл MyClock.idl получил две новых строки кодаНовые строки кода в файле MyClock.idl
// MyClock.idl : type library source for ActiveX Control project.
// This file will be processed by the MIDL compiler tool to
// produce the type library (MyClock.tlb) that will become a resource in
// MyClock.ocx.
#include
#include
[ uuid(30BE1A13-3CBE-4525-A8B6-D126D4CFD220), version(1.0),
helpfile("MyClock.hlp"),
helpstring("MyClock ActiveX Control module"),
control ]
library MyClockLib
{
importlib(STDOLE_TLB);
// Primary dispatch interface for CMyClockCtrl
[ uuid(15870940-495C-4FFD-8C1A-32B6C2C87C25),
helpstring("Dispatch interface for MyClock Control")]
dispinterface _DMyClock
{
properties:
[id(DISPID_BACKCOLOR), helpstring("property BackColor")] OLE_COLOR BackColor;
[id(DISPID_FORECOLOR), helpstring("property ForeColor")] OLE_COLOR ForeColor;
methods:
[id(DISPID_ABOUTBOX)] void AboutBox();
};
// Event dispatch interface for CMyClockCtrl
[ uuid(1E47AE17-F799-44FC-9C13-9E3DD23D5540),
helpstring("Event interface for MyClock Control") ]
dispinterface _DMyClockEvents
{
properties:
// Event interface has no properties
methods:
};
// Class information for CMyClockCtrl
[ uuid(21FD49D6-3D30-40EA-AF5A-043ABDAD8074),
helpstring("MyClock Control"), control ]
coclass MyClock
{
[default] dispinterface _DMyClock;
[default, source] dispinterface _DMyClockEvents;
};
};
Чтобы проверить, что элемент управления имеет сейчас свойства BackColor и ForeColor, выполните следующее
Постройте проект и запустите утилиту ActiveX Control Test Container
Откройте сохраненную нами ранее сессию под именем TestMyClock с размещенным для тестирования элементом управления MyClock
Выберите команду Container/Ambient Properties тестовой утилиты или нажмите кнопку с изображением лупы на панели инструментов этой утилиты
рис25Появится диалоговое окно изменения свойств, в котором можно проверить добавленные нами свойства, а также изменить значения этих и других свойств. Однако установка новых значений свойства не отражается на элементе управления, потому, что мы еще не написали код, который делает свойство функционирующим.
Напишем код, который делает свойства BackColor и ForeColor функциональными. Это позволит после задания новых значений свойствам сразу повлиять на элемент управления.
Откройте файл MyClockCtrl.cpp и найдите функцию OnDraw()
Отредактируйте ее, чтобы она приняла видФункция OnDraw() в файле MyClockCtrl.cpp
void CMyClockCtrl::OnDraw(
CDC* pdc,
const CRect rcBounds,
const CRect rcInvalid)
{
if (!pdc)
return;
// TODO: Replace the following code with your own drawing code.
// Задать цвет переднего плана (т.е. цвет текста)
// согласно значения свойства ForeColor
pdc->SetTextColor(TranslateColor(GetForeColor()));
// Установить режим прозрачного фона
pdc->SetBkMode(TRANSPARENT);
// Создать кисть на основе значения свойства BackColor
CBrush bkBrush(TranslateColor(GetBackColor()));
// Залить элемент управления, используя значение BackColor
pdc->FillRect(rcBounds, bkBrush);
// Получить и вывести текст текущей даты и времени
GetCurDate();
m_str = m_strDate;
GetCurTime();
m_str += m_strTime;
pdc->ExtTextOut(rcBounds.left,
rcBounds.top,
ETO_CLIPPED,
rcBounds,
m_str,
m_str.GetLength(),
NULL);
}
Установите начальные размеры элемента управления, добавив в конструктор класса CMyClockCtrl следующий кодДобавления в конструктор CMyClockCtrl в файле MyClockCtrl.cpp
CMyClockCtrl::CMyClockCtrl()
{
InitializeIIDs(IID_DMyClock, IID_DMyClockEvents);
// TODO: Initialize your control's instance data here.
// Установить начальные размеры
SetInitialSize(300, 15);
}
Базовые свойства предусмотрены оболочкой при создании каркаса. Но во многих случаях может понадибиться включение своих собственных свойств в элемент управления, со своим названием и кодом, который делает свойство функционирующим. Эти свойства называются специальными ( Custom ). Включим в элемент управления MyClock дополнительное свойство с именем UpdateInterval, которое будем использовать для хранения чисел, устанавливающих период обновления таймера.
Откройте панель
Через контекстное меню элемента
Появится мастер, в котором установите переключатель в положение
Нажмите кнопку Таким образом мы определили, что новое свойство будет иметь имя UpdateInterval и что это свойство будет использоваться для хранения чисел в переменной m_UpdateInterval типа long. В поле Notification function (OnUpdateIntervalChanged(), который будет срабатывать всякий раз, когда значение свойства UpdateInterval изменяется.
Необходимо инициализировать свойство UpdateInterval начальным значением. Для этого
Откройте файл MyClockCtrl.cpp и найдите функцию DoPropExchange()
Дополните код этой функции такДобавления кода в функцию DoPropExchange()
void CMyClockCtrl::DoPropExchange(CPropExchange* pPX)
{
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);
// TODO: Call PX_ functions for each persistent custom property.
// Инициализировать свойство UpdateInterval
// значением 1000
PX_Long(pPX, _T("UpdateInterval"), m_UpdateInterval, 1000);
}
Для инициализации свойства UpdateInterval в приведенном коде используется функция PX_Long(), поскольку свойство имеет тип long. Для инициализации свойств других типов можно использовать другие PX -функции, например, PX_Bool(), PX_Short(), PX_String().
Свойство UpdateInterval должно устанавливать частоту срабатывания OnCreate() класса CMyClockCtrl. Сейчас нам нужно модифицировать код элемента управления так, чтобы он свое состояние каждые X миллисекунд, где X - текущее значение свойства UpdateInterval.
Откройте файл MyClockCtrl.cpp и найдите функцию OnUpdateIntervalChanged(), которую создал мастер при добавлении нового свойства
Введите следующий код в функцию OnUpdateIntervalChanged()Добавления кода в функцию OnUpdateIntervalChanged()
void CMyClockCtrl::OnUpdateIntervalChanged(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your property handler code here
// Заново установить таймер с периодом,
// заданным значением свойства UpdateInterval
SetTimer(ID_CLOCK_TIMER, (UINT)m_UpdateInterval, NULL);
SetModifiedFlag();
}
Оператор UINT - это переопределенный тип unsigned int. Он обеспечивает явное приведение типа long свойства к ожидаемому функцией SetTimer () значению.
Поскольку у нас появилось свойство, где при создании элемента управления хранится значение периода OnCreate() также внесите следующие коррективыКорректирование кода в функции OnCreate()
int CMyClockCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (COleControl::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
// Установить таймер
SetTimer(ID_CLOCK_TIMER, (UINT)m_UpdateInterval, NULL);
return 0;
}
Напишем код, который защитит элемент управления от некорректного ввода значения в свойство UpdateInterval (например, отрицательного). Для этого скорректируем функцию OnUpdateIntervalChanged().
Откройте файл MyClockCtrl.cpp и найдите функцию OnUpdateIntervalChanged(), которую создал мастер при добавлении нового свойства и которую мы уже корректировали
Введите следующий код в функцию OnUpdateIntervalChanged()Повторное корректирование функции OnUpdateIntervalChanged()
void CMyClockCtrl::OnUpdateIntervalChanged(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your property handler code here
// Проверка того, что введенное пользователем
// значение свойства неотрицательно
if(m_UpdateInterval < 0){
MessageBox("Недопустимое значение!!!"
"\nУстанавливаю 1000!!!");
m_UpdateInterval = 1000;
}
// Заново установить таймер с периодом,
// заданным значением свойства UpdateInterval
SetTimer(ID_CLOCK_TIMER, (UINT)m_UpdateInterval, NULL);
SetModifiedFlag();
}
Использование утилиты ActiveX Control Test Container для тестирования и просмотра элемента управления на этапе проектирования - это только один из возможных способов, и мы им уже пользовались. Другим способом доступа к компоненту на этапе проектирование с целью просмотра и установки свойств являются страницы свойств, реализованные в виде закладок окна свойств.
Если мы, как разработчик компонента, предусмотрим страницы свойств в нашем проекте, то появится возможность на этапе проектирования этого компонента просматривать и изменять его свойства визуальными средствами. Каждая страница свойств должна обеспечить доступ к набору взаимосвязанных свойств разрабатываемого компонента. Например, можно просмотреть и выбрать свойства цвета на странице свойств Colors, а свойства шрифта - на странице свойств Fonts.
Для ознакомления с такой возможностью включим в элемент управления две закладки окна свойств: Цвет и Шрифт. Закладка Цвет позволит нам просматривать и устанавливать введенные ранее свойства BackColor и ForeColor. Закладку Шрифт введем для практики, но она будет пустой, поскольку свойство шрифта мы в компонент не вводили. Перед тем, как написать код, реализующий появление закладок, убедимся через утилиту ActiveX Control Test Container, что элемент управления MyClock не имеет закладок Цвета и Шрифта.
Выполните команду главного меню оболочки Tools/ActiveX Control Test Container
В появившемся окне утилиты
В появившемся диалоговом окне
Щелкните по кнопке OK и в клиентской области окна утилиты появится элемент управления MyClock
Выделите элемент управления (дайте ему фокус ввода) и через главное меню утилиты выполните команду То же самое можно сделать через кнопку Properties панели инструментов (при

Убедитесь, что в появившемся диалоговом окне свойств нет закладок Цвет и Шрифт
Страницы Цвет и Шрифт появятся тогда, когда мы введем в компонент соответствующий код. Закладка General (страница свойств General ) содержит сейчас следующий текст: "ЧТО ДЕЛАТЬ: Разместите в этом диалоге элементы управления свойствами компонента MyClock ".
Страница свойств General (закладка General ) должна содержать общие свойства, которые не относятся к другим страницам свойств. Позже мы настроим закладку General, а сейчас добавим к свойствам закладки Цвет и Шрифт.
Закройте утилиту без сохранения и вернитесь к проекту управляющего элемента.
Откройте файл Автоматически сгенерированная мастером проекта таблица свойств имеет вид
Прежний код таблицы свойств // Property pages // TODO: Add more property pages as needed. // Remember to increase the count! BEGIN_PROPPAGEIDS(CMyClockCtrl, 1) PROPPAGEID(CMyClockPropPage::guid) END_PROPPAGEIDS(CMyClockCtrl)
Модифицируйте таблицу Property pages следующим образомМодифицированный код таблицы свойств // Property pages // TODO: Add more property pages as needed. // Remember to increase the count! BEGIN_PROPPAGEIDS(CMyClockCtrl, 3) PROPPAGEID(CMyClockPropPage::guid) PROPPAGEID(CLSID_CColorPropPage) PROPPAGEID(CLSID_CFontPropPage) END_PROPPAGEIDS(CMyClockCtrl)
Начинает таблицу свойств макрос
BEGIN_PROPPAGEIDS(CMyClockCtrl, 3)
Первый параметр является именем класса элемента управления, второй определяет количество вкладок в окне свойств. По мере включения в компонент все новых страниц свойств этот параметр нужно увеличивать. Следующие три макроса определяют вкладки General и добавленные вкладки свойств Цвета и Шрифта. Последний макрос завершает таблицу свойств.
Чтобы убедиться, что закладки после модификации кода таблицы свойств появились в компоненте и позволят нам визуально его настраивать на этапе проектирования, снова испытайте компонент в утилите ActiveX Control Test Container. Для этого:
Постройте проект, выполнив команду Build/Build Solution
Выполните команду главного меню оболочки Tools/ActiveX Control Test Container
В появившемся окне утилиты
В появившемся диалоговом окне
Щелкните по кнопке OK и в клиентской области окна утилиты появится элемент управления MyClock
Выделите элемент управления (дайте ему фокус ввода) и через главное меню утилиты выполните команду То же самое можно сделать через кнопку Properties панели инструментов (при

Убедитесь, что в открывшемся диалоговом окне появились две дополнительные закладки: Цвета и ШрифтыЗакладка цвет позволяет в диалоговом режиме экспериментировать со свойствами компонента, определяющими цвет.

Завершите работу утилиты без сохраненияВкладка General пока не включает в себя никаких свойств. Настроим ее так, чтобы она позволяла просматривать и устанавливать свойство UpdateInterval, добавленное к компоненту нами ранее и управляющее частотой
Для настройки страницы свойств General нам нужно работать с диалоговой панелью IDD_PROPPAGE_MYCLOCK, автоматически созданной оболочкой проекта.
В панели
Вызовите на редактирование диалоговый ресурс IDD_PROPPAGE_MYCLOCK
Удалите статический текст из формы
Поместите на форму текстовую метку Static Text и дайте ей значение UpdateInterval:. Она будет служить маркером простого поля редактирования
Поместите на форму правее текстовой метки простое поле редактирования Edit Control и присвойте ему идентификатор IDC_UPDATE_INTERVAL
Выровняйте компоненты относительно формы, чтобы они смотрелись так
Ни в коем случае не меняйте размеры диалогового окна!!! Они автоматически генерируются оболочкой и должны быть равны 250x62 или 250x110. Любые другие размеры вкладки свойств General нестандартны и при их использовании могут возникнуть проблемы или, по крайней мере, предупреждения.
Теперь нам нужно связать введенную ранее переменную m_UpdateInterval, хранящую значение периода
Выберите в панели Class View проекта класс CMyClockPropPage
Через контекстное меню вызовите для этого класса мастер добавления переменной, который настройте так
Теперь модифицируем обмен данными с целью привязки переменной m_UpdateInterval поля редактирования вкладки General с переменной m_UpdateInterval элемента управления MyClock. Это нужно выполнить вручную, потому что средства в оболочке для такого действия нет.
Откройте файл исходного кода MyClockPropPage.cpp
Найдите реализацию функции DoDataExchange(). Модифицируйте ее путем добавления к функции DDX функции DDP , чтобы окончательный результат выглядел такМодифицированный код функции DoDataExchange() файла MyClockPropPage.cpp
void CMyClockPropPage::DoDataExchange(CDataExchange* pDX)
{
DDX_Text(pDX, IDC_UPDATE_INTERVAL, m_UpdateInterval);
DDP_Text(pDX, IDC_UPDATE_INTERVAL, m_UpdateInterval,
_T("UpdateInterval"));
DDP_PostProcessing(pDX);
}
Код организует двухступенчатый обмен данными: сначало между полем редактирования и ассоциированной с ним переменной, затем между этой переменной и переменной, хранящей значение свойства элемента управления. Обратите внимание, что DDP_PostProcessing(pDX) .
Проверьте работу вкладки General через тестовую утилиту, как мы это уже неоднократно проделывали, и убедитесь, что поле вкладки управляет периодом MyClockРезультат наших усилий внешне должен выглядеть так
Включение событий в элемент управления открывает для него канал приема сообщений от Windows о действиях пользователя, когда элемент получает фокус ввода. Различают базовые ( Stock - готовые) события и специальные ( Custom - пользовательские).
Базовые события ( Stock ), которые мы можем добавить в элемент управления - предопределены и приведены в таблице
| Базовые события элементов управления | ||||
|---|---|---|---|---|
Click |
DblClick |
Error |
KeyDown |
KeyPress |
KeyUp |
MouseDown |
MouseMove |
MouseUp |
ReadyStateChanged |
Имена базовых событий построены на основании того, что происходит. Если мы предусмотрим события в элементе управления, то будущий пользователь нашего элемента (программист) или мы сами можем в дальнейшем посадить на них обработчики. Добавление событий выполняет мастер Add Events Wizard.
Для практики включим в MyClock два события: Click и DblClick.
В панели Class View выберите CMyClockCtrl
Щелкните на нем правой кнопкой мыши и выберите из контекстного меню пункты Появится мастер добавления событий

Мастер имеет два режима добавления события: Stock (готовый, базовый) и Custom (пользовательский, прочий). В нижней части мастера расположена область для добавления параметров, которые можно передать из элемента управления в приложение-контейнер (то, что использует элемент управления и организует управление им) вместе с событием.
В раскрывающемся списке поля Event name установите событие Click и нажмите кнопку Finish
Такое же действие повторите для добавления события DblClickВ файле MyClockCtrl.cpp мастер добавит код в карту событий
Карта событий файла MyClockCtrl.cpp // Event map BEGIN_EVENT_MAP(CMyClockCtrl, COleControl) EVENT_STOCK_CLICK() EVENT_STOCK_DBLCLICK() END_EVENT_MAP()
В файле MyClock.idl мастер добавит код объявлений обработчиков событий
Объявление обработчиков событий файла MyClock.idl
....................................
// Event dispatch interface for CMyClockCtrl
[ uuid(1E47AE17-F799-44FC-9C13-9E3DD23D5540),
helpstring("Event interface for MyClock Control") ]
dispinterface _DMyClockEvents
{
properties:
// Event interface has no properties
methods:
[id(DISPID_CLICK)] void Click(void);
[id(DISPID_DBLCLICK)] void DblClick(void);
};
....................................
Мы закончили включение базовых событий Click и DblClick в элемент управления MyClock. Это означает, что другие лица, которые будут использовать наш элемент управления в своих программах, такие, например, как программисты-господа Панасюк, Сизых, Рублев или просто-программисты Жук, Фирстов, Вильгельм, Косенок, Цех, Даниленко, Непомнящий будут иметь возможность связать свой код не только с салом, но и с этими событиями.
Для того, чтобы убедиться, что элемент управления MyClock сейчас имеет события Click и DblClick, выполните следующее:
Постройте приложение
Выполните команду главного меню оболочки Tools/ActiveX Control Test Container
В появившемся окне утилиты
В появившемся диалоговом окне
Щелкните по кнопке OK и в клиентской области окна утилиты появится элемент управления MyClock
Убедитесь, что элемент управления выделен в окне утилиты
Выполните команду
В появившемся диалоговом окне Мы видим, что события Click и DblClick действительно добавлены в элемент управления.
Закройте диалоговое окно Control Logging Options
Пощелкайте мышью на элементе управленияМы видим, что в нижней части окна тестовая утилита регистрирует генерируемые элементом управления события Click и DblClick

Заметьте, что не имеет значения, какой кнопкой мыши мы щелкнули. При выполнении двойного щелчка утилита регистрирует сразу два события - Click и DblClick, поскольку первый щелчок в двойном щелчке является обычным щелчком.
Щелкните правой кнопкой мыши в нижней части окна утилиты по списку регистрации событий и через контекстное меню выполните команду Clear, чтобы очистить список
Закройте утилиту ActiveX Control Test Container без сохраненияСобытия, которые разрабатывает программист элемента управления (пользователь оболочки), называются пользовательскими ( Custom ) или специальными. Специальные события добавляют к элементу управления тогда, когда базовых событий не хватает.
На данном этапе мы включим в наш элемент управления MyClock специальное событие NewMinute (новая минута), а позже напишем для него обработчик.
В панели Class View выберите CMyClockCtrl
Щелкните на нем правой кнопкой мыши и выберите из контекстного меню пункты Появится мастер добавления событий
Заполните мастер, как показано на рисунке
Нажмите кнопку Finish и мастер добавит соответствующий код так, как это было при добавлении событий Click и DblClick
Карта событий файла MyClockCtrl.cpp
// Event map
BEGIN_EVENT_MAP(CMyClockCtrl, COleControl)
EVENT_STOCK_CLICK()
EVENT_STOCK_DBLCLICK()
EVENT_CUSTOM_ID("NewMinute", eventidNewMinute, NewMinute, VTS_NONE)
END_EVENT_MAP()
Объявление обработчиков событий файла MyClock.idl
....................................
// Event dispatch interface for CMyClockCtrl
[ uuid(1E47AE17-F799-44FC-9C13-9E3DD23D5540),
helpstring("Event interface for MyClock Control") ]
dispinterface _DMyClockEvents
{
properties:
// Event interface has no properties
methods:
[id(DISPID_CLICK)] void Click(void);
[id(DISPID_DBLCLICK)] void DblClick(void);
[id(1)] void NewMinute(void);
};
....................................
Кроме того, в класс CMyClockCtrl была добавлена функция генерации события
Функция NewMinute() генерации события в файле MyClockCtrl.h
class CMyClockCtrl : public COleControl
{
........................................................
void NewMinute(void)
{
FireEvent(eventidNewMinute, EVENT_PARAM(VTS_NONE));
}
};
Постройте проект и проверьте в тестовой утилите ActiveX Control Test Container наличие добавленного события, как мы это делали раньше
Результат будет таким

Как мы видим, в списке присутствуют все три события, которые мы добавляли в элемент управления.
Когда мы включаем в элемент управления базовое событие, такое, например, как Click или DblClick, то оно уже содержит встроенный код, который генерирует это событие и делать дополнительно ничего не надо, чтобы оно происходило. Например, событие Click происходит автоматически каждый раз, когда выполняется щелчок на элементе управления.
Когда включается в элемент управления специальное событие, то нужно написать код, который генерирует это событие в ответ на выполнение некоторых условий, для регистрации возникновения которых событие собственно и создается. Этот код должен распознать, что условия возникновения события выполнены, после чего код должен обратиться к функции FireEvent() данного события, которая и сгенерирует само событие.
Функция FireEvent() нашего события NewMinute вызывается одноименной функцией NewMinute(). Таким образом, чтобы событие NewMinute произошло, нужно составить код, который должен зарегистрировать выполнение условий генерации этого события и вызвать функцию FireEvent() генерации события через функцию NewMinute().
Определим, что событие NewMinute должно происходить всякий раз, когда начинается отсчет каждой новой минуты NewMinute, выполните следующее
В панели Solution Explorer оболочки откройте файл MyClockCtrl.cpp нашего проекта
В файле MyClockCtrl.cpp через верхний раскрывающийся список найдите функцию OnDraw()
В конец функции OnDraw() вставьте следующий кодДобавление генератора события NewMinute
void CMyClockCtrl::OnDraw(
CDC* pdc,
const CRect rcBounds,
const CRect rcInvalid)
{
if (!pdc)
return;
// TODO: Replace the following code with your own drawing code.
// Задать цвет переднего плана (т.е. цвет текста)
// согласно значения свойства ForeColor
pdc->SetTextColor(TranslateColor(GetForeColor()));
// Установить режим прозрачного фона
pdc->SetBkMode(TRANSPARENT);
// Создать кисть на основе значения свойства BackColor
CBrush bkBrush(TranslateColor(GetBackColor()));
// Залить элемент управления, используя значение BackColor
pdc->FillRect(rcBounds, bkBrush);
// Получить и вывести текст текущей даты и времени
GetCurDate();
m_str = m_strDate;
GetCurTime();
m_str += m_strTime;
pdc->ExtTextOut(rcBounds.left,
rcBounds.top,
ETO_CLIPPED,
rcBounds,
m_str,
m_str.GetLength(),
NULL);
// Получить текущее время и сгенерировать событие NewMinute
CTime curTime = CTime::GetCurrentTime();
if(curTime.Format("%S") == CString("00"))
NewMinute();
}
Постройте проект и проверьте в тестовой утилите ActiveX Control Test Container, что элемент управления действительно генерирует событие NewMinute всякий раз, когда обнуляется количество секундРезультат будет примерно таким
Помимо свойств и событий в элемент управления можно включать и методы, которые аналогичны функциям-членам классов C++. После того, как мы включим в элемент управления какой-либо метод, другие программисты, которые будут использовать наш элемент управления в своих программах, такие, например, как Панасюк, Сизых, Рублев и другие, сразу же заимеют возможность обращаться к этому методу как к своему кровному.
Предположим, что мы включили в элемент управления метод с условным именем MyMethod(). Предположим также, что некий студент-программист с условной фамилией Иванов включил впоследствии наш элемент управления MyClock в свою программу и назвал его экземпляр условно SaLo. После этого в своей программе студент-программист Иванов сможет написать следующий оператор
SaLo.MyMethod();
и тем самым применит метод к экземпляру SaLo элемента управления.
При создании заготовки проекта элемента управления оболочка автоматически генерирует код для метода AboutBox(). Когда программист, использующий наш элемент управления, вызывает в своей программе метод AboutBox(), появляется диалоговое окно About.
Перед тем, как включить в элемент управления свой собственный метод, проверим работу метода AboutBox(), который создал мастер приложения.
Постройте проект и поместите в тестовую утилиту ActiveX Control Test Container наш элемент управления MyClock
Обращая внимание, чтобы элемент управления был выделен, выполните пункт меню утилиты
В появившемся диалоговом окне На экране появится заготовка диалогового окна About

Метод существует и он работает!
Закройте тестовую утилиту и отредактируйте диалоговое окно About так, чтобы не стыдно было показать самой студентке Ельцовой - которая в этом деле добре разбирается (Ельцова, я восхищаюсь!)Ресурс About имеет имя IDD_ABOUTBOX_MYCLOCK в панели ресурсов Resource View. Мой About выглядит так
Базовые методы предопределены в оболочке VS.NET 2003. Их только два: DoClick() и Refresh().
Метод DoClick() имитирует щелчок на элементе управления, как будто бы пользователь щелкнул на нем. При выполнении метода Refresh() элемент управления перерисовывает сам себя, вызывая функцию OnDraw().
Для практики включим в элемент управления MyClock метод Refresh().
Откройте панель
Щелкните правой кнопкой мыши на узле _DMyClock или узле _DMyClockEvents (это безразлично).
Через контекстное меню Add/Add Method вызовите мастер добавления метода.
Раскройте список Method name: и выберите имя Refresh Сейчас окно мастера должно выглядеть так

Щелкните по кнопке Finish, чтобы добавить метод Refresh() в проект элемента управления
Вновь откройте панель Вспомним, что при выполнении метода Refresh() элемент управления MyClock должен перерисовывать сам себя, вызывая функцию OnDraw(). Сейчас же пока функция OnDraw() вызывается по сообщению WM_TIMER, генерируемому таймером. Если мы установим период OnDraw() будет обновлять окно элемента управления очень редко. Если в больших промежутках между Refresh(), который должен обновлять окно, то сможем убедиться, что он работает.
Испытания метода Refresh() проведем следующим образом
Постройте проект, чтобы обновить внесенные в MyClock изменения
Запустите утилиту тестирования ActiveX Control Test Container и поместите в нее наш элемент управления MyClock
При выделенном элементе управления щелкните по кнопке
В появившемся окне свойств выберите отредактированную нами ранее вкладку
Нажмите кнопку Применить и закройте окно свойств. Таймер элемента управления MyClock будет переведен на интервал срабатывания 55 секунд и можно продолжить испытание метода Refresh()
Нажмите кнопку OK или Отмена, чтобы закрыть окно свойств
При выделенном элементе управления выполните команду меню утилиты
В появившемся диалоговом окне
Сдвиньте окно Invoke Methods так, чтобы видеть элемент управления
Щелкая по кнопке Invoke убедитесь, что окно принудительно перерисовывается функцией OnDraw(), в тело которой мы ранее разместили код обновления времениДобавленный нами метод Refresh() работает!
Закройте окно тестовой утилиты без сохраненияПоскольку, как мы уже убедились, базовых методов всего два, а этого даже на сало не хватит, рассмотрим возможность добавления пользовательских ( Custom ) методов. Для примера добавим три пользовательских метода
StartClock() - для запуска часовBeepClock() - для запуска или остановки будильникаДля добавления метода StartClock() выполните следующее
Откройте панель Class View и раскройте узел MyClockLib, а затем узел интерфейса _DMyClock
Щелкните правой кнопкой мыши на узле _DMyClock и через контекстное меню Add/Add Method вызовите мастер добавления метода
Укажите в поле Method name имя добавляемого метода StartClock. Поле Internal name внутреннего имени заполнится автоматически на основе введенного внешнего имени
В поле Return type из раскрывающегося списка выбирете тип void
Щелкните по кнопке Finish
Для добавления метода StopClock() повторите те же шаги, которые выполнили для StartClock()Метод BeepClock() создадим с аргументом типа BYTE (unsigned char) , который будет принимать ненулевое (включить звонок) или нулевое (выключить звонок) значения.
Снова откройте панель Class View и раскройте узел MyClockLib, а затем узел интерфейса _DMyClock
Щелкните правой кнопкой мыши на узле _DMyClock и через контекстное меню Add/Add Method вызовите мастер добавления метода
Укажите в поле Method name имя добавляемого метода BeepClock Поле Internal name внутреннего имени заполнится автоматически на основе введенного внешнего имени.
В поле Return type из раскрывающегося списка выбирете тип void
В поле Parameter type из раскрывающегося списка выбирете значение BYTE, а в поле Parameter name введите beep Окно мастера должно выглядеть так

Щелкните по кнопке Add
Щелкните по кнопке Finish
Откройте панель
Перейдите в панель Solution Explorer и откройте в ней файл MyClockCtrl.cpp
Перейдите в конец файла MyClockCtrl.cpp и убедитесь, что мастер вместе с добавлением пользовательских методов создал и заготовки кода для их реализацииОбратите внимание, что заготовка для встроенного метода Refresh() отсутствует.
Для реализации методов StartClock() и StopClock() введем внутреннюю ( private ) логическую переменную flagClock типа bool в класс CMyClockCtrl. Инициализируем ее в конструкторе класса значением true, чтобы часы после создания объекта-экземпляра элемента управления MyClock сразу шли. В функцию OnDraw() введем условие обновления выходного текста в зависимости от значения переменной flagClock. В реализациях методов StartClock() и StopClock() будем устанавливать соответствующее значение флага.
Откройте файл MyClockCtrl.h и добавьте в конец объявления класса CMyClockCtrl код Добавление флага остановки часов в класс CMyClockCtrl
class CMyClockCtrl : public COleControl
{
.........................................................
private:
// Переменная-член текущей даты
CString m_strDate;
// Переменная-член текущего времени
CString m_strTime;
// Заполняет переменную-член m_strDate текущей датой
void GetCurDate(void);
// Заполняет переменную m_strTime текущим временем
void GetCurTime(void);
CString m_str;
public:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnDestroy();
protected:
void OnUpdateIntervalChanged(void);
LONG m_UpdateInterval;
void NewMinute(void)
{
FireEvent(eventidNewMinute, EVENT_PARAM(VTS_NONE));
}
void StartClock(void);
void StopClock(void);
void BeepClock(BYTE beep);
private:
bool flagClock;
};
Откройте файл MyClockCtrl.cpp и добавьте в конструктор класса CMyClockCtrl кодДобавление кода инициализации флага остановки часов в конструкторе класса CMyClockCtrl
CMyClockCtrl::CMyClockCtrl()
{
InitializeIIDs(IID_DMyClock, IID_DMyClockEvents);
// TODO: Initialize your control's instance data here.
// Установить начальные размеры
SetInitialSize(300, 15);
// Установить начальный ход часов
flagClock = true;
}
В функцию OnDraw() класса CMyClockCtrl добавьте условие перерисовки времениДобавление кода в функцию OnDraw() класса CMyClockCtrl
void CMyClockCtrl::OnDraw(
CDC* pdc,
const CRect rcBounds,
const CRect rcInvalid)
{
if (!pdc)
return;
// TODO: Replace the following code with your own drawing code.
// Задать цвет переднего плана (т.е. цвет текста)
// согласно значения свойства ForeColor
pdc->SetTextColor(TranslateColor(GetForeColor()));
// Установить режим прозрачного фона
pdc->SetBkMode(TRANSPARENT);
// Создать кисть на основе значения свойства BackColor
CBrush bkBrush(TranslateColor(GetBackColor()));
// Залить элемент управления, используя значение BackColor
pdc->FillRect(rcBounds, bkBrush);
// Получить и вывести текст текущей даты и времени
if(flagClock){ // Заморозить время
GetCurDate();
m_str = m_strDate;
GetCurTime();
m_str += m_strTime;
} // Разморозить время
pdc->ExtTextOut(rcBounds.left,
rcBounds.top,
ETO_CLIPPED,
rcBounds,
m_str,
m_str.GetLength(),
NULL);
// Получить текущее время и сгенерировать событие NewMinute
CTime curTime = CTime::GetCurrentTime();
if(curTime.Format("%S") == CString("00"))
NewMinute();
}
Отредактируйте реализации методов StartClock() и StopClock() в файле MyClockCtrl.cpp такРеализации методов StartClock() и StopClock()
void CMyClockCtrl::StartClock(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your dispatch handler code here
flagClock = true; // Запустить часы
}
void CMyClockCtrl::StopClock(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your dispatch handler code here
flagClock = false; // Остановить часы
}
Проверьте работу методов на тестовой утилите ActiveX Control Test Container
Теперь осталось реализовать добавленный пользовательский метод BeepClock(). Для простоты построим его таким же образом, что и реализацию предыдущих методов, только флаг введем с именем flagBeep. Функция просто будет устанавливать или сбрасывать флаг, а подачу звукового сигнала организуем в функции-обработчике NewMinute. Таким образом, при включенном методом BeepClock() флаге flagBeep в начале каждой минуты компьютер будет выдавать звуковой сигнал.
Откройте файл MyClockCtrl.h и добавьте в конец объявления класса CMyClockCtrl кодДобавление флага flagBeep в класс CMyClockCtrl
class CMyClockCtrl : public COleControl
{
.........................................................
private:
bool flagClock;
bool flagBeep;
};
Откройте файл MyClockCtrl.cpp и добавьте в конструктор класса CMyClockCtrl кодДобавление кода инициализации флага flagBeep в конструкторе класса CMyClockCtrl
CMyClockCtrl::CMyClockCtrl()
{
InitializeIIDs(IID_DMyClock, IID_DMyClockEvents);
// TODO: Initialize your control's instance data here.
// Установить начальные размеры
SetInitialSize(300, 15);
// Установить начальный ход часов
flagClock = true;
flagBeep = false;
}
В функцию NewMinute() файла MyClockCtrl.h добавьте условие подачи сигналаДобавление кода в функцию NewMinute() класса CMyClockCtrl
class CMyClockCtrl : public COleControl
{
...........................................
void NewMinute(void)
{
FireEvent(eventidNewMinute, EVENT_PARAM(VTS_NONE));
if(flagBeep)
MessageBeep((WORD) - 1);
}
void StartClock(void);
void StopClock(void);
void BeepClock(BYTE beep);
private:
bool flagClock;
bool flagBeep;
};
Отредактируйте реализации метода BeepClock() в файле MyClockCtrl.cpp такРеализации метода BeepClock()
void CMyClockCtrl::BeepClock(BYTE beep)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your dispatch handler code here
flagBeep = beep ? true : false;
}
Проверьте работу метода на тестовой утилите ActiveX Control Test ContainerПри создании заготовки проекта элемента управления оболочка автоматически присвоила ему имя MyClock Control. Изменим это имя на Часы.
Откройте тестовую утилиту и командой меню
Найдите элемент MyClock Control и снимите его с Unregister
Откройте ресурс String Table в панели Resource View оболочки проекта
Найдите идентификатор IDS_MYCLOCK и измените его значение с MyClock Control на новое значение Часы
Постройте проект и через тестовую утилиту вновь зарегистрируйте его в системе под новым название Часы. Проверьте через команду утилиты Теперь подключим наш элемент к оболочке VS.NET 2003.
Откройте ресурс диалогового окна IDD_PROPPAGE_MYCLOCK или IDD_ABOUTBOX_MYCLOCK, чтобы активизировать панель Toolbox оболочки
Раскройте одну из вкладок панели Toolbox, например, General (можно открыть и Dialog Editor, но для пробы General практически пустая)
Щелкните на ней правой кнопкой мыши и из контекстного меню выберите команду Add/Remove Items...
В появившемся диалоговом окне установите вкладку
После щелчка по кнопке Перейдите к разработке тестового приложения, где будет использован наш компонент Часы.
Для испытания нашего компонента "Часы" построим тестовое диалоговое приложение, в котором поместим элементы для управления всеми заложенными в компонент возможностями.
Перейдите в свой каталог и постройте в нем новый MFC Application - проект и именем Test
Настройте вкладки мастера проекта так


Используя клавишу
Смените стандартную иконку приложения на другую
Смените название приложения на Испытание элемента управления "Часы"
Установите режим построения проекта Solution Configurations в значение Release
Откройте диалоговую форму IDD_TEST_DIALOG и удалите с нее все компоненты, сгенерированные мастеромРазместите в верхней части диалоговой формы элемент управления "Часы", разработанный ранее, для чего:
Щелкните на форме правой кнопкой мыши и через контекстное меню выполните команду Insert ActiveX Control...
В открывшемся окне выберите элемент Часы из списка зарегистрированных в компьютере элементов (процедуру регистрации компонента в компьютере мы рассмотрели при разработке самого управляющего элемента, а также в
Измените идентификатор компонента Часы на IDC_MYCLOCKCTRL
Разместите на диалоговой форме стандартные элементы управления и задайте их свойства в соответствии с таблицей.| Элементы управления тестового приложения | |||
|---|---|---|---|
| Тип элемента управления | Имя свойства | Значение свойства | Пояснения |
Group Box |
ID |
IDC_STATIC |
Рамка для управления часами |
Caption |
Режимы | ||
|
ID |
IDC_CHECK_BEEP |
Включение/Выключение звука ActiveX |
Caption |
Звуковой сигнал | ||
|
ID |
IDC_CHECK_START |
Включение/Выключение хода часов на экране |
Caption |
Ход часов | ||
Group Box |
ID |
IDC_STATIC |
Рамка для элементов управления цветом |
Caption |
Цвет | ||
|
ID |
IDC_RADIO_FORE |
Регулируется цвет текста |
Caption |
Текст | ||
|
ID |
IDC_RADIO_BACK |
Регулируется цвет фона |
Caption |
Фон | ||
Left Text |
True |
||
|
ID |
IDC_COMBO_COLOR |
Список цветов |
Vertical Scrollbar |
True |
||
Sort |
False |
Не сортировать названия цвета | |
Group Box |
ID |
IDC_STATIC |
Рамка для управления интервалом |
Caption |
Интервал таймера | ||
Static Text |
ID |
IDC_SHOWINTERVAL |
Отображение интервала |
Caption |
1000 | ||
Align Text |
Right |
||
|
ID |
IDC_SLIDER_INTERVAL |
Регулирование интервала |
Orientation |
Horizontal |
Расположение по горизонтали | |
Button |
ID |
IDC_BUTTON_EXIT |
Завершить приложение |
Caption |
Выход | ||
Button |
ID |
IDC_BUTTON_ABOUTCLOCK |
Об авторах MyClock |
Caption |
About Часов |
||
Интерфейс тестового приложения должен выглядеть примерно так
Добавьте для элементов управления диалогового окна приложения ассоциированные переменные в соответствии с таблицей ниже
Для каждого элемента управления вызывайте мастер Add Variable через контекстное меню для этого элемента
Следите за тем, чтобы флажок
Control ID Access Variable t ype Variable name Category Comment IDC_MYCLOCKCTRLprivateCMyclockctrl m_MyClock Control IDC_CHECK_BEEP private BOOLm_bCheckBeepValue IDC_CHECK_START private BOOL m_bCheckStart Value IDC_RADIO_FORE privateint m_iSwapColor ValueIDC_COMBO_COLORprivateCComboBoxm_ComboColorControl IDC_SHOWINTERVALprivate CString m_sShowIntervalValue IDC_SLIDER_INTERVALprivate CSliderCtrl m_SliderIntervalControlИнициализацию ассоциированных переменных выполним в функции OnInitDialog() класса CTestDlg.
Откройте файл TestDlg.cpp тестового приложения
Найдите и дополните функцию OnInitDialog() следующим кодомДополнение функции OnInitDialog()
BOOL CTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
..........................................
// TODO: Add extra initialization here
// Переменные для элементов управления тестового приложения
m_bCheckBeep = false;
m_bCheckStart = true;
m_iSwapColor = 0;
m_ComboColor.AddString("Черный");
m_ComboColor.AddString("Синий");
m_ComboColor.AddString("Зеленый");
m_ComboColor.AddString("Бирюзовый");
m_ComboColor.AddString("Красный");
m_ComboColor.AddString("Сиреневый");
m_ComboColor.AddString("Желтый");
m_ComboColor.AddString("Белый");
m_ComboColor.SetCurSel(0);
long tmp = m_MyClock.GetUpdateInterval();
m_sShowInterval.Format("%ld", tmp);
m_SliderInterval.SetRangeMin(1000);
m_SliderInterval.SetRangeMax(5000);
UpdateData(FALSE); // Выдать значения на экран
return TRUE; // return TRUE unless you set the focus to a control
}
Постройте обработчик для кнопки Выход и заполните его такОбработчик для кнопки 'Выход'
void CTestDlg::OnBnClickedButtonExit()
{
// TODO: Add your control notification handler code here
OnOK();
}
Постройте обработчик для кнопки About Часов и заполните его такОбработчик для кнопки 'About Часов'
void CTestDlg::OnBnClickedButtonAboutclock()
{
// TODO: Add your control notification handler code here
m_MyClock.AboutBox();
}
Постройте обработчик для флажка Звуковой сигнал и заполните его такОбработчик для флажка 'Звуковой сигнал'
void CTestDlg::OnBnClickedCheckBeep()
{
// TODO: Add your control notification handler code here
// Получить текущее значение от экрана
UpdateData(TRUE);
// Проверить значение флажка и установить режим Beep
if(m_bCheckBeep == TRUE)
m_MyClock.BeepClock(1);
else
m_MyClock.BeepClock(0);
}
Постройте обработчик для флажка ' Ход часов ' и заполните его такОбработчик для флажка 'Ход часов'
void CTestDlg::OnBnClickedCheckStart()
{
// TODO: Add your control notification handler code here
// Получить текущее значение от экрана
UpdateData(TRUE);
// Проверить значение флажка и Остановить/Запустить часы
if(m_bCheckStart == TRUE)
m_MyClock.StartClock();
else
m_MyClock.StopClock();
}
Постройте обработчик для раскрывающегося списка выбора цвета, добавив в проект таблицу цветов и код. Для этого выделите элемент Обработчик для раскрывающегося списка
void CTestDlg::OnCbnSelchangeComboColor()
{
// TODO: Add your control notification handler code here
// Таблица цветов
COLORREF colors[8] = {
RGB( 0, 0, 0),
RGB( 0, 0, 255),
RGB( 0, 255, 0),
RGB( 0, 255, 255),
RGB(255, 0, 0),
RGB(255, 0, 255),
RGB(255, 255, 0),
RGB(255, 255, 255)
};
// Получить текущее значение от экрана
UpdateData(TRUE);
int index = m_ComboColor.GetCurSel();
if(m_iSwapColor == 0)
m_MyClock.SetForeColor(colors[index]);
else
m_MyClock.SetBackColor(colors[index]);
}
Для управления ползунком необходимо связать сообщение Windows WM_HSCROLL с методом OnHScroll() (сообщение WM_VSCROLL и метод OnVScroll() - для вертикальных ползунков.
Постройте обработчик для ползунка. Для этого выделите форму IDD_TEST_DIALOG
Перейдите в панель Properties и установите режим Messages
Найдите сообщение
Обработчик заполните следующим кодомОбработчик для ползунка
void CTestDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: Add your message handler code here and/or call default
int pos = m_SliderInterval.GetPos();
m_sShowInterval.Format("%d", pos);
UpdateData(FALSE);
m_MyClock.SetUpdateInterval((long)pos);
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
Оформите About тестового приложения
Постройте приложение и убедитесь в правильности его работыВнешний вид приложения должен выглядеть примерно так

Элемент управления ActiveX можно разрабатывать самим, затем любой программист в состоянии его использовать точно также, как стандартный элемент управления. Готовый элемент управления размещается на диалоговой панели и настраивается установкой значений свойств и обработчиков событий.
Поскольку расширением файла элемента управления ActiveX является ., то иногда элементы управления называют элементами .
Далее в этой работе мы создадим и протестируем свой собственный элемент управления ActiveX - MyClock., который будет выполнять задачу непрерывного вывода текущего времени. Далее мы присоединим готовый компонент в панель Toolbox оболочки, чтобы его можно было использовать в любом приложении наряду со стандартными компонентами.
Войдите в свой каталог выполнения лабораторных работ
Запустите оболочку Microsoft Visual Studio .NET 2003
Создайте новый проект и назовите его
Настройте мастер 

Установите в раскрывающемся списке Solution Configurations режим построения решения ReleaseПосле того, как с помощью мастера мы создали проект и каркасы файлов элемента управления MyClock, дальнейшая работа заключается в его настройке так, чтобы он вел себя нужным образом. Но прежде давайте построим и протестируем проект, чтобы убедиться, что даже каркасный код уже в определенной мере работоспособен.
Выполните компиляцию и компоновку проекта
Войдите в каталог проекта Release и убедитесь, что создан файл MyClock.ocx Чтобы протестировать элемент управления, нужно создать тестовое приложение, которое его использует, как это было с календарем в Lab17. Но на начальной стадии проще воспользоваться утилитой ActiveX Control Test Container, которая предоставляет такие возможности без построения тестового приложения.
Выполните команду главного меню оболочки Tools/ActiveX Control Test Container
В появившемся окне утилиты
В появившемся диалоговом окне
Щелкните по кнопке Элемент управления MyClock рисует в окне утилиты эллипс для начального контроля за способностью изменять размеры. Этот эллипс создан каркасным кодом оболочки в функции OnDraw(). Рамка с маркерами позволяет изменять размеры элемента управления, а сам элемент можно перетаскивать в окне утилиты. Можно поместить в окно утилиты несколько элементов управления.
Через кнопку Таким образом, с помощью утилиты ActiveX Control Test Container на данном этапе мы протестировали визуальный аспект элемента управления MyClock: способность размещаться в родительском окне-контейнере, изменять размеры и местоположение, размещаться в контейнере несколькими копиями.
Утилита позволяет протестировать свойства и события элемента управления, но на данном этапе нам тестировать нечего, поскольку свойства и события в MyClock нами еще не определены.
Закройте ( без сохранения изменений) утилиту ActiveX Control Test Container, чтобы завершить процесс предварительного тестированияВсе управляющие элементы, размещенные в панели Toolbox оболочки, представлены на ней кнопками с пиктограммой и надписью. Подготовим подходящий значок для нашего элемента управления.
В панели Resource View раскройте узел Bitmap и вызовите на редактирование ресурс IDB_MYCLOCK
Воспользуйтесь расположенными в верхней части оболочки кнопками редактора и замените значок по умолчанию

на более подходящий, например такой

Пока элемент управления выводит только пустой эллипс. Нам нужно, чтобы в нем отображалось текущее время в виде текста. Напишем соответствующий код.
В панели Solution Explorer вызовите на редактирование файл MyClockCtrl.cpp
Найдите в нем функцию OnDraw(). Сейчас она выглядит такКод функции OnDraw(), сгенерированный мастером
void CMyClockCtrl::OnDraw(
CDC* pdc,
const CRect rcBounds,
const CRect rcInvalid)
{
if (!pdc)
return;
// TODO: Replace the following code with your own drawing code.
pdc->FillRect(rcBounds,
CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
pdc->Ellipse(rcBounds);
}
Обратите внимание на два последних оператора (выделены красным). Первый оператор заливает прямоугольную область элемента управления белым цветом, второй рисует в ней эллипс пером, установленным по умолчанию.
Замените в функции OnDraw() последний оператор так, чтобы вместо эллипса для проверки внутри выводилась текстовая "затычка" ( заглушка )Измененный код функции OnDraw()
void CMyClockCtrl::OnDraw(
CDC* pdc,
const CRect rcBounds,
const CRect rcInvalid)
{
if (!pdc)
return;
// TODO: Replace the following code with your own drawing code.
// Залить элемент управления
pdc->FillRect(rcBounds,
CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
// pdc->Ellipse(rcBounds); // Построить эллипс
// Вывести текст
CString m_str = "Это мой первый ActiveX";
pdc->ExtTextOut(rcBounds.left,
rcBounds.top,
ETO_CLIPPED,
rcBounds,
m_str,
m_str.GetLength(),
NULL);
}
Функция вывода текста MFC -класса имеет такие аргументы
ETO_CLIPPED устанавливает, что текст будет усечен границами прямоугольной области.CString .NULL, то пробелы между символами заданы по умолчанию.Чтобы увидеть результаты внесенный изменений, выполните следующие действия:
Постройте проект и проверьте его в утилите ActiveX Control Test ContainerДолжен получиться такой результат

Заменим нашу текстовую заглушку на строку, содержащую текущую дату и время. Подобную информацию мы уже получали в Lab6, поэтому повторим эти шаги
Добавьте объявление двух переменных m_strDate и m_strTime в объявление класса CMyClockCtrl файла MyClockCtrl.h для хранения текущих даты и времени. Выполните это без применения мастера - вручнуюИзменение в файле MyClockCtrl.h
class CMyClockCtrl : public COleControl
{
............................................
private:
// Переменная-член текущей даты
CString m_strDate;
// Переменная-член текущего времени
CString m_strTime;
};
В панели
Вновь в панели
Заготовку функции GetCurDate() в файле MyClockCtrl.cpp наполните следующим кодомФункция GetCurDate() в файле MyClockCtrl.cpp
// Заполняет переменную-член m_strDate текущей датой
void CMyClockCtrl::GetCurDate(void)
{
// массив месяцев
char *month[] = {"января"
,"февраля"
,"марта"
,"апреля"
,"мая"
,"июня"
,"июля"
,"августа"
,"сентября"
,"октября"
,"ноября"
,"декабря"};
// массив дней недели
char *week[] = { "воскресенье"
,"понедельник"
,"вторник"
,"среда"
,"четверг"
,"пятница"
,"суббота" };
int m_day, m_month, m_week, m_year;
// Получить текущее время
CTime curTime = CTime::GetCurrentTime();
m_day = curTime.GetDay(); // Число месяца
m_month = curTime.GetMonth(); // 1 - Январь
m_week = curTime.GetDayOfWeek(); // 1 - Воскресенье
m_year = curTime.GetYear(); // Год
char tmp[5]; // Для размещения числа месяца, года и нуль-символа
itoa(m_day, tmp, 10);// Преобразуем целое в строку в десятичном формате
m_strDate = ""; // Заполнить пустой строкой
m_strDate += tmp;
m_strDate += " ";
m_strDate += month[m_month - 1];
m_strDate += " (";
m_strDate += week[m_week - 1];
m_strDate += ") - ";
itoa(m_year, tmp, 10); // Преобразуем число-год в строку-год
m_strDate += tmp;
m_strDate += " г.";
}
Заготовку функции GetCurTime() в файле MyClockCtrl.cpp наполните следующим кодомФункция GetCurTime() в файле MyClockCtrl.cpp
// Заполняет переменную m_strTime текущим временем
void CMyClockCtrl::GetCurTime(void)
{
// Получить текущее время
CTime curTime = CTime::GetCurrentTime();
// Сохранить текущее время в нужном формате
m_strTime = " "; // Два пробела
m_strTime += curTime.Format("%H:%M:%S");
}
Добавьте объявление суммарной переменной m_str в объявление класса CMyClockCtrl файла MyClockCtrl.h для хранения текущих даты и времени. Выполните это без применения мастера - вручнуюИзменение в файле MyClockCtrl.h
class CMyClockCtrl : public COleControl
{
............................................
// Переменная-член текущей даты
CString m_strDate;
// Переменная-член текущего времени
CString m_strTime;
// Заполняет переменную-член m_strDate текущей датой
void GetCurDate(void);
// Заполняет переменную m_strTime текущим временем
void GetCurTime(void);
CString m_str;
};
Отредактируйте функцию OnDraw() так, чтобы она выводила текущие дату и времяФункция OnDraw() в файле MyClockCtrl.cpp
void CMyClockCtrl::OnDraw(
CDC* pdc,
const CRect rcBounds,
const CRect rcInvalid)
{
if (!pdc)
return;
// TODO: Replace the following code with your own drawing code.
// Залить элемент управления
pdc->FillRect(rcBounds,
CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
// pdc->Ellipse(rcBounds); // Построить эллипс
// Получить и вывести текст текущей даты и времени
GetCurDate();
m_str = m_strDate;
GetCurTime();
m_str += m_strTime;
pdc->ExtTextOut(rcBounds.left,
rcBounds.top,
ETO_CLIPPED,
rcBounds,
m_str,
m_str.GetLength(),
NULL);
}
Постройте проект и проверьте его в утилите ActiveX Control Test ContainerДолжен получиться такой результат
Код вывода текстовой информации о текущей дате и времени обновляется только при разрушении окна. Поэтому нужно создать таймер с ежесекундным интервалом срабатывания и разместить код обновления вывода текста управляющего элемента в обработчик таймера. Поскольку дата в обозримый промежуток времени остается неизменной и нет смысла обновлять ее постоянно, ежесекундно вычисляя достаточно объемный код, поместим в обработчик только вызов функции обновления времени.
Чтобы однозначно определить таймер, ему нужно присвоить уникальный идентификатор. Для этого:
Выделите в панели ресурсов Resource View узел MyClock.rc, затем либо через меню Edit оболочки, либо через контекстное меню узла выполните опцию Resource Symbols (символы резурсов)
В диалоговом окне Resource Symbols щелкните на New (создать)
В диалоговом окне
Значение поля Value окна New Symbol можно изменить или оставить по умолчаниюТаймер необходимо установить сразу после создания элемента управления, так что нам нужно связать код, устанавливающий таймер, с событием WM_CREATE элемента управления.
В панели
Создайте обработчик OnCreate(), который заполните такОбработчик OnCreate() в файле MyClockCtrl.cpp
int CMyClockCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (COleControl::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
// Установить таймер
SetTimer(ID_CLOCK_TIMER, 1000, NULL);
return 0;
}
Введенный код создает и запускает таймер с 1000-миллисекундным периодом срабатывания. Начиная с этого момента, каждую секунду Windows будет посылать нашему элементу управления сообщение WM_TIMER, которое мы и будем перехватывать и обрабатывать, обновляя строку времени.
В панели
Найдите сообщение WM_TIMER, создайте для него обработчик, который заполните такОбработчик OnTimer() в файле MyClockCtrl.cpp
void CMyClockCtrl::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
// Переключить вызов на функцию OnDraw()
InvalidateControl();
COleControl::OnTimer(nIDEvent);
}
Предусмотрим остановку
В панели
Найдите сообщение WM_DESTROY, создайте для него обработчик, который заполните такОбработчик OnDestroy() в файле MyClockCtrl.cpp
void CMyClockCtrl::OnDestroy()
{
COleControl::OnDestroy();
// TODO: Add your message handler code here
// Остановить таймер
KillTimer(ID_CLOCK_TIMER);
}
Постройте проект и проверьте его в утилите ActiveX Control Test Container. Время должно меняться ежесекундно
Сохраните в своем каталоге сессию тестирования утилиты Любой элемент управления имеет некоторый неизменный ограниченный набор свойств, которые определяют его основные характеристики. Этот набор называется базовым. Базовые свойства любого элемента управления ActiveX приведены в таблице
| Базовые свойства элементов управления | |
|---|---|
| Свойство | Значение |
Apperance |
Внешний вид (например, 3-мерный или плоский) |
BackColor |
Цвет фона |
BorderStyle |
|
Caption |
Заголовок |
Enabled |
Состояние доступности |
Font |
Шрифт для выводимого текста |
ForeColor |
Цвет переднего плана |
hWnd |
Маркер окна |
ReadyState |
Состояние готовности |
Text |
Текст |
Для практики включим два базовых свойства в наш элемент управления: BackColor и ForeColor.
Откройте панель
Через контекстное меню элемента
В появившемся диалоговом окне Add Property Wizard включите радиокнопку Stock (Готовое, Базовое)
В раскрывающемся списке выберите свойство
Повторите те же действия для добавления в элемент управления свойства ForeColor
Убедитесь, что в панели
Убедитесь, что файл MyClock.idl получил две новых строки кодаНовые строки кода в файле MyClock.idl
// MyClock.idl : type library source for ActiveX Control project.
// This file will be processed by the MIDL compiler tool to
// produce the type library (MyClock.tlb) that will become a resource in
// MyClock.ocx.
#include
#include
[ uuid(30BE1A13-3CBE-4525-A8B6-D126D4CFD220), version(1.0),
helpfile("MyClock.hlp"),
helpstring("MyClock ActiveX Control module"),
control ]
library MyClockLib
{
importlib(STDOLE_TLB);
// Primary dispatch interface for CMyClockCtrl
[ uuid(15870940-495C-4FFD-8C1A-32B6C2C87C25),
helpstring("Dispatch interface for MyClock Control")]
dispinterface _DMyClock
{
properties:
[id(DISPID_BACKCOLOR), helpstring("property BackColor")] OLE_COLOR BackColor;
[id(DISPID_FORECOLOR), helpstring("property ForeColor")] OLE_COLOR ForeColor;
methods:
[id(DISPID_ABOUTBOX)] void AboutBox();
};
// Event dispatch interface for CMyClockCtrl
[ uuid(1E47AE17-F799-44FC-9C13-9E3DD23D5540),
helpstring("Event interface for MyClock Control") ]
dispinterface _DMyClockEvents
{
properties:
// Event interface has no properties
methods:
};
// Class information for CMyClockCtrl
[ uuid(21FD49D6-3D30-40EA-AF5A-043ABDAD8074),
helpstring("MyClock Control"), control ]
coclass MyClock
{
[default] dispinterface _DMyClock;
[default, source] dispinterface _DMyClockEvents;
};
};
Чтобы проверить, что элемент управления имеет сейчас свойства BackColor и ForeColor, выполните следующее
Постройте проект и запустите утилиту ActiveX Control Test Container
Откройте сохраненную нами ранее сессию под именем TestMyClock с размещенным для тестирования элементом управления MyClock
Выберите команду Container/Ambient Properties тестовой утилиты или нажмите кнопку с изображением лупы на панели инструментов этой утилиты
рис25Появится диалоговое окно изменения свойств, в котором можно проверить добавленные нами свойства, а также изменить значения этих и других свойств. Однако установка новых значений свойства не отражается на элементе управления, потому, что мы еще не написали код, который делает свойство функционирующим.
Напишем код, который делает свойства BackColor и ForeColor функциональными. Это позволит после задания новых значений свойствам сразу повлиять на элемент управления.
Откройте файл MyClockCtrl.cpp и найдите функцию OnDraw()
Отредактируйте ее, чтобы она приняла видФункция OnDraw() в файле MyClockCtrl.cpp
void CMyClockCtrl::OnDraw(
CDC* pdc,
const CRect rcBounds,
const CRect rcInvalid)
{
if (!pdc)
return;
// TODO: Replace the following code with your own drawing code.
// Задать цвет переднего плана (т.е. цвет текста)
// согласно значения свойства ForeColor
pdc->SetTextColor(TranslateColor(GetForeColor()));
// Установить режим прозрачного фона
pdc->SetBkMode(TRANSPARENT);
// Создать кисть на основе значения свойства BackColor
CBrush bkBrush(TranslateColor(GetBackColor()));
// Залить элемент управления, используя значение BackColor
pdc->FillRect(rcBounds, bkBrush);
// Получить и вывести текст текущей даты и времени
GetCurDate();
m_str = m_strDate;
GetCurTime();
m_str += m_strTime;
pdc->ExtTextOut(rcBounds.left,
rcBounds.top,
ETO_CLIPPED,
rcBounds,
m_str,
m_str.GetLength(),
NULL);
}
Установите начальные размеры элемента управления, добавив в конструктор класса CMyClockCtrl следующий кодДобавления в конструктор CMyClockCtrl в файле MyClockCtrl.cpp
CMyClockCtrl::CMyClockCtrl()
{
InitializeIIDs(IID_DMyClock, IID_DMyClockEvents);
// TODO: Initialize your control's instance data here.
// Установить начальные размеры
SetInitialSize(300, 15);
}
Базовые свойства предусмотрены оболочкой при создании каркаса. Но во многих случаях может понадибиться включение своих собственных свойств в элемент управления, со своим названием и кодом, который делает свойство функционирующим. Эти свойства называются специальными ( Custom ). Включим в элемент управления MyClock дополнительное свойство с именем UpdateInterval, которое будем использовать для хранения чисел, устанавливающих период обновления таймера.
Откройте панель
Через контекстное меню элемента
Появится мастер, в котором установите переключатель в положение
Нажмите кнопку Таким образом мы определили, что новое свойство будет иметь имя UpdateInterval и что это свойство будет использоваться для хранения чисел в переменной m_UpdateInterval типа long. В поле Notification function (OnUpdateIntervalChanged(), который будет срабатывать всякий раз, когда значение свойства UpdateInterval изменяется.
Необходимо инициализировать свойство UpdateInterval начальным значением. Для этого
Откройте файл MyClockCtrl.cpp и найдите функцию DoPropExchange()
Дополните код этой функции такДобавления кода в функцию DoPropExchange()
void CMyClockCtrl::DoPropExchange(CPropExchange* pPX)
{
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);
// TODO: Call PX_ functions for each persistent custom property.
// Инициализировать свойство UpdateInterval
// значением 1000
PX_Long(pPX, _T("UpdateInterval"), m_UpdateInterval, 1000);
}
Для инициализации свойства UpdateInterval в приведенном коде используется функция PX_Long(), поскольку свойство имеет тип long. Для инициализации свойств других типов можно использовать другие PX -функции, например, PX_Bool(), PX_Short(), PX_String().
Свойство UpdateInterval должно устанавливать частоту срабатывания OnCreate() класса CMyClockCtrl. Сейчас нам нужно модифицировать код элемента управления так, чтобы он свое состояние каждые X миллисекунд, где X - текущее значение свойства UpdateInterval.
Откройте файл MyClockCtrl.cpp и найдите функцию OnUpdateIntervalChanged(), которую создал мастер при добавлении нового свойства
Введите следующий код в функцию OnUpdateIntervalChanged()Добавления кода в функцию OnUpdateIntervalChanged()
void CMyClockCtrl::OnUpdateIntervalChanged(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your property handler code here
// Заново установить таймер с периодом,
// заданным значением свойства UpdateInterval
SetTimer(ID_CLOCK_TIMER, (UINT)m_UpdateInterval, NULL);
SetModifiedFlag();
}
Оператор UINT - это переопределенный тип unsigned int. Он обеспечивает явное приведение типа long свойства к ожидаемому функцией SetTimer () значению.
Поскольку у нас появилось свойство, где при создании элемента управления хранится значение периода OnCreate() также внесите следующие коррективыКорректирование кода в функции OnCreate()
int CMyClockCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (COleControl::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
// Установить таймер
SetTimer(ID_CLOCK_TIMER, (UINT)m_UpdateInterval, NULL);
return 0;
}
Напишем код, который защитит элемент управления от некорректного ввода значения в свойство UpdateInterval (например, отрицательного). Для этого скорректируем функцию OnUpdateIntervalChanged().
Откройте файл MyClockCtrl.cpp и найдите функцию OnUpdateIntervalChanged(), которую создал мастер при добавлении нового свойства и которую мы уже корректировали
Введите следующий код в функцию OnUpdateIntervalChanged()Повторное корректирование функции OnUpdateIntervalChanged()
void CMyClockCtrl::OnUpdateIntervalChanged(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your property handler code here
// Проверка того, что введенное пользователем
// значение свойства неотрицательно
if(m_UpdateInterval < 0){
MessageBox("Недопустимое значение!!!"
"\nУстанавливаю 1000!!!");
m_UpdateInterval = 1000;
}
// Заново установить таймер с периодом,
// заданным значением свойства UpdateInterval
SetTimer(ID_CLOCK_TIMER, (UINT)m_UpdateInterval, NULL);
SetModifiedFlag();
}
Использование утилиты ActiveX Control Test Container для тестирования и просмотра элемента управления на этапе проектирования - это только один из возможных способов, и мы им уже пользовались. Другим способом доступа к компоненту на этапе проектирование с целью просмотра и установки свойств являются страницы свойств, реализованные в виде закладок окна свойств.
Если мы, как разработчик компонента, предусмотрим страницы свойств в нашем проекте, то появится возможность на этапе проектирования этого компонента просматривать и изменять его свойства визуальными средствами. Каждая страница свойств должна обеспечить доступ к набору взаимосвязанных свойств разрабатываемого компонента. Например, можно просмотреть и выбрать свойства цвета на странице свойств Colors, а свойства шрифта - на странице свойств Fonts.
Для ознакомления с такой возможностью включим в элемент управления две закладки окна свойств: Цвет и Шрифт. Закладка Цвет позволит нам просматривать и устанавливать введенные ранее свойства BackColor и ForeColor. Закладку Шрифт введем для практики, но она будет пустой, поскольку свойство шрифта мы в компонент не вводили. Перед тем, как написать код, реализующий появление закладок, убедимся через утилиту ActiveX Control Test Container, что элемент управления MyClock не имеет закладок Цвета и Шрифта.
Выполните команду главного меню оболочки Tools/ActiveX Control Test Container
В появившемся окне утилиты
В появившемся диалоговом окне
Щелкните по кнопке OK и в клиентской области окна утилиты появится элемент управления MyClock
Выделите элемент управления (дайте ему фокус ввода) и через главное меню утилиты выполните команду То же самое можно сделать через кнопку Properties панели инструментов (при

Убедитесь, что в появившемся диалоговом окне свойств нет закладок Цвет и Шрифт
Страницы Цвет и Шрифт появятся тогда, когда мы введем в компонент соответствующий код. Закладка General (страница свойств General ) содержит сейчас следующий текст: "ЧТО ДЕЛАТЬ: Разместите в этом диалоге элементы управления свойствами компонента MyClock ".
Страница свойств General (закладка General ) должна содержать общие свойства, которые не относятся к другим страницам свойств. Позже мы настроим закладку General, а сейчас добавим к свойствам закладки Цвет и Шрифт.
Закройте утилиту без сохранения и вернитесь к проекту управляющего элемента.
Откройте файл Автоматически сгенерированная мастером проекта таблица свойств имеет вид
Прежний код таблицы свойств // Property pages // TODO: Add more property pages as needed. // Remember to increase the count! BEGIN_PROPPAGEIDS(CMyClockCtrl, 1) PROPPAGEID(CMyClockPropPage::guid) END_PROPPAGEIDS(CMyClockCtrl)
Модифицируйте таблицу Property pages следующим образомМодифицированный код таблицы свойств // Property pages // TODO: Add more property pages as needed. // Remember to increase the count! BEGIN_PROPPAGEIDS(CMyClockCtrl, 3) PROPPAGEID(CMyClockPropPage::guid) PROPPAGEID(CLSID_CColorPropPage) PROPPAGEID(CLSID_CFontPropPage) END_PROPPAGEIDS(CMyClockCtrl)
Начинает таблицу свойств макрос
BEGIN_PROPPAGEIDS(CMyClockCtrl, 3)
Первый параметр является именем класса элемента управления, второй определяет количество вкладок в окне свойств. По мере включения в компонент все новых страниц свойств этот параметр нужно увеличивать. Следующие три макроса определяют вкладки General и добавленные вкладки свойств Цвета и Шрифта. Последний макрос завершает таблицу свойств.
Чтобы убедиться, что закладки после модификации кода таблицы свойств появились в компоненте и позволят нам визуально его настраивать на этапе проектирования, снова испытайте компонент в утилите ActiveX Control Test Container. Для этого:
Постройте проект, выполнив команду Build/Build Solution
Выполните команду главного меню оболочки Tools/ActiveX Control Test Container
В появившемся окне утилиты
В появившемся диалоговом окне
Щелкните по кнопке OK и в клиентской области окна утилиты появится элемент управления MyClock
Выделите элемент управления (дайте ему фокус ввода) и через главное меню утилиты выполните команду То же самое можно сделать через кнопку Properties панели инструментов (при

Убедитесь, что в открывшемся диалоговом окне появились две дополнительные закладки: Цвета и ШрифтыЗакладка цвет позволяет в диалоговом режиме экспериментировать со свойствами компонента, определяющими цвет.

Завершите работу утилиты без сохраненияВкладка General пока не включает в себя никаких свойств. Настроим ее так, чтобы она позволяла просматривать и устанавливать свойство UpdateInterval, добавленное к компоненту нами ранее и управляющее частотой
Для настройки страницы свойств General нам нужно работать с диалоговой панелью IDD_PROPPAGE_MYCLOCK, автоматически созданной оболочкой проекта.
В панели
Вызовите на редактирование диалоговый ресурс IDD_PROPPAGE_MYCLOCK
Удалите статический текст из формы
Поместите на форму текстовую метку Static Text и дайте ей значение UpdateInterval:. Она будет служить маркером простого поля редактирования
Поместите на форму правее текстовой метки простое поле редактирования Edit Control и присвойте ему идентификатор IDC_UPDATE_INTERVAL
Выровняйте компоненты относительно формы, чтобы они смотрелись так
Ни в коем случае не меняйте размеры диалогового окна!!! Они автоматически генерируются оболочкой и должны быть равны 250x62 или 250x110. Любые другие размеры вкладки свойств General нестандартны и при их использовании могут возникнуть проблемы или, по крайней мере, предупреждения.
Теперь нам нужно связать введенную ранее переменную m_UpdateInterval, хранящую значение периода
Выберите в панели Class View проекта класс CMyClockPropPage
Через контекстное меню вызовите для этого класса мастер добавления переменной, который настройте так
Теперь модифицируем обмен данными с целью привязки переменной m_UpdateInterval поля редактирования вкладки General с переменной m_UpdateInterval элемента управления MyClock. Это нужно выполнить вручную, потому что средства в оболочке для такого действия нет.
Откройте файл исходного кода MyClockPropPage.cpp
Найдите реализацию функции DoDataExchange(). Модифицируйте ее путем добавления к функции DDX функции DDP , чтобы окончательный результат выглядел такМодифицированный код функции DoDataExchange() файла MyClockPropPage.cpp
void CMyClockPropPage::DoDataExchange(CDataExchange* pDX)
{
DDX_Text(pDX, IDC_UPDATE_INTERVAL, m_UpdateInterval);
DDP_Text(pDX, IDC_UPDATE_INTERVAL, m_UpdateInterval,
_T("UpdateInterval"));
DDP_PostProcessing(pDX);
}
Код организует двухступенчатый обмен данными: сначало между полем редактирования и ассоциированной с ним переменной, затем между этой переменной и переменной, хранящей значение свойства элемента управления. Обратите внимание, что DDP_PostProcessing(pDX) .
Проверьте работу вкладки General через тестовую утилиту, как мы это уже неоднократно проделывали, и убедитесь, что поле вкладки управляет периодом MyClockРезультат наших усилий внешне должен выглядеть так
Включение событий в элемент управления открывает для него канал приема сообщений от Windows о действиях пользователя, когда элемент получает фокус ввода. Различают базовые ( Stock - готовые) события и специальные ( Custom - пользовательские).
Базовые события ( Stock ), которые мы можем добавить в элемент управления - предопределены и приведены в таблице
| Базовые события элементов управления | ||||
|---|---|---|---|---|
Click |
DblClick |
Error |
KeyDown |
KeyPress |
KeyUp |
MouseDown |
MouseMove |
MouseUp |
ReadyStateChanged |
Имена базовых событий построены на основании того, что происходит. Если мы предусмотрим события в элементе управления, то будущий пользователь нашего элемента (программист) или мы сами можем в дальнейшем посадить на них обработчики. Добавление событий выполняет мастер Add Events Wizard.
Для практики включим в MyClock два события: Click и DblClick.
В панели Class View выберите CMyClockCtrl
Щелкните на нем правой кнопкой мыши и выберите из контекстного меню пункты Появится мастер добавления событий

Мастер имеет два режима добавления события: Stock (готовый, базовый) и Custom (пользовательский, прочий). В нижней части мастера расположена область для добавления параметров, которые можно передать из элемента управления в приложение-контейнер (то, что использует элемент управления и организует управление им) вместе с событием.
В раскрывающемся списке поля Event name установите событие Click и нажмите кнопку Finish
Такое же действие повторите для добавления события DblClickВ файле MyClockCtrl.cpp мастер добавит код в карту событий
Карта событий файла MyClockCtrl.cpp // Event map BEGIN_EVENT_MAP(CMyClockCtrl, COleControl) EVENT_STOCK_CLICK() EVENT_STOCK_DBLCLICK() END_EVENT_MAP()
В файле MyClock.idl мастер добавит код объявлений обработчиков событий
Объявление обработчиков событий файла MyClock.idl
....................................
// Event dispatch interface for CMyClockCtrl
[ uuid(1E47AE17-F799-44FC-9C13-9E3DD23D5540),
helpstring("Event interface for MyClock Control") ]
dispinterface _DMyClockEvents
{
properties:
// Event interface has no properties
methods:
[id(DISPID_CLICK)] void Click(void);
[id(DISPID_DBLCLICK)] void DblClick(void);
};
....................................
Мы закончили включение базовых событий Click и DblClick в элемент управления MyClock. Это означает, что другие лица, которые будут использовать наш элемент управления в своих программах, такие, например, как программисты-господа Панасюк, Сизых, Рублев или просто-программисты Жук, Фирстов, Вильгельм, Косенок, Цех, Даниленко, Непомнящий будут иметь возможность связать свой код не только с салом, но и с этими событиями.
Для того, чтобы убедиться, что элемент управления MyClock сейчас имеет события Click и DblClick, выполните следующее:
Постройте приложение
Выполните команду главного меню оболочки Tools/ActiveX Control Test Container
В появившемся окне утилиты
В появившемся диалоговом окне
Щелкните по кнопке OK и в клиентской области окна утилиты появится элемент управления MyClock
Убедитесь, что элемент управления выделен в окне утилиты
Выполните команду
В появившемся диалоговом окне Мы видим, что события Click и DblClick действительно добавлены в элемент управления.
Закройте диалоговое окно Control Logging Options
Пощелкайте мышью на элементе управленияМы видим, что в нижней части окна тестовая утилита регистрирует генерируемые элементом управления события Click и DblClick

Заметьте, что не имеет значения, какой кнопкой мыши мы щелкнули. При выполнении двойного щелчка утилита регистрирует сразу два события - Click и DblClick, поскольку первый щелчок в двойном щелчке является обычным щелчком.
Щелкните правой кнопкой мыши в нижней части окна утилиты по списку регистрации событий и через контекстное меню выполните команду Clear, чтобы очистить список
Закройте утилиту ActiveX Control Test Container без сохраненияСобытия, которые разрабатывает программист элемента управления (пользователь оболочки), называются пользовательскими ( Custom ) или специальными. Специальные события добавляют к элементу управления тогда, когда базовых событий не хватает.
На данном этапе мы включим в наш элемент управления MyClock специальное событие NewMinute (новая минута), а позже напишем для него обработчик.
В панели Class View выберите CMyClockCtrl
Щелкните на нем правой кнопкой мыши и выберите из контекстного меню пункты Появится мастер добавления событий
Заполните мастер, как показано на рисунке
Нажмите кнопку Finish и мастер добавит соответствующий код так, как это было при добавлении событий Click и DblClick
Карта событий файла MyClockCtrl.cpp
// Event map
BEGIN_EVENT_MAP(CMyClockCtrl, COleControl)
EVENT_STOCK_CLICK()
EVENT_STOCK_DBLCLICK()
EVENT_CUSTOM_ID("NewMinute", eventidNewMinute, NewMinute, VTS_NONE)
END_EVENT_MAP()
Объявление обработчиков событий файла MyClock.idl
....................................
// Event dispatch interface for CMyClockCtrl
[ uuid(1E47AE17-F799-44FC-9C13-9E3DD23D5540),
helpstring("Event interface for MyClock Control") ]
dispinterface _DMyClockEvents
{
properties:
// Event interface has no properties
methods:
[id(DISPID_CLICK)] void Click(void);
[id(DISPID_DBLCLICK)] void DblClick(void);
[id(1)] void NewMinute(void);
};
....................................
Кроме того, в класс CMyClockCtrl была добавлена функция генерации события
Функция NewMinute() генерации события в файле MyClockCtrl.h
class CMyClockCtrl : public COleControl
{
........................................................
void NewMinute(void)
{
FireEvent(eventidNewMinute, EVENT_PARAM(VTS_NONE));
}
};
Постройте проект и проверьте в тестовой утилите ActiveX Control Test Container наличие добавленного события, как мы это делали раньше
Результат будет таким

Как мы видим, в списке присутствуют все три события, которые мы добавляли в элемент управления.
Когда мы включаем в элемент управления базовое событие, такое, например, как Click или DblClick, то оно уже содержит встроенный код, который генерирует это событие и делать дополнительно ничего не надо, чтобы оно происходило. Например, событие Click происходит автоматически каждый раз, когда выполняется щелчок на элементе управления.
Когда включается в элемент управления специальное событие, то нужно написать код, который генерирует это событие в ответ на выполнение некоторых условий, для регистрации возникновения которых событие собственно и создается. Этот код должен распознать, что условия возникновения события выполнены, после чего код должен обратиться к функции FireEvent() данного события, которая и сгенерирует само событие.
Функция FireEvent() нашего события NewMinute вызывается одноименной функцией NewMinute(). Таким образом, чтобы событие NewMinute произошло, нужно составить код, который должен зарегистрировать выполнение условий генерации этого события и вызвать функцию FireEvent() генерации события через функцию NewMinute().
Определим, что событие NewMinute должно происходить всякий раз, когда начинается отсчет каждой новой минуты NewMinute, выполните следующее
В панели Solution Explorer оболочки откройте файл MyClockCtrl.cpp нашего проекта
В файле MyClockCtrl.cpp через верхний раскрывающийся список найдите функцию OnDraw()
В конец функции OnDraw() вставьте следующий кодДобавление генератора события NewMinute
void CMyClockCtrl::OnDraw(
CDC* pdc,
const CRect rcBounds,
const CRect rcInvalid)
{
if (!pdc)
return;
// TODO: Replace the following code with your own drawing code.
// Задать цвет переднего плана (т.е. цвет текста)
// согласно значения свойства ForeColor
pdc->SetTextColor(TranslateColor(GetForeColor()));
// Установить режим прозрачного фона
pdc->SetBkMode(TRANSPARENT);
// Создать кисть на основе значения свойства BackColor
CBrush bkBrush(TranslateColor(GetBackColor()));
// Залить элемент управления, используя значение BackColor
pdc->FillRect(rcBounds, bkBrush);
// Получить и вывести текст текущей даты и времени
GetCurDate();
m_str = m_strDate;
GetCurTime();
m_str += m_strTime;
pdc->ExtTextOut(rcBounds.left,
rcBounds.top,
ETO_CLIPPED,
rcBounds,
m_str,
m_str.GetLength(),
NULL);
// Получить текущее время и сгенерировать событие NewMinute
CTime curTime = CTime::GetCurrentTime();
if(curTime.Format("%S") == CString("00"))
NewMinute();
}
Постройте проект и проверьте в тестовой утилите ActiveX Control Test Container, что элемент управления действительно генерирует событие NewMinute всякий раз, когда обнуляется количество секундРезультат будет примерно таким

Помимо свойств и событий в элемент управления можно включать и методы, которые аналогичны функциям-членам классов C++. После того, как мы включим в элемент управления какой-либо метод, другие программисты, которые будут использовать наш элемент управления в своих программах, такие, например, как Панасюк, Сизых, Рублев и другие, сразу же заимеют возможность обращаться к этому методу как к своему кровному.
Предположим, что мы включили в элемент управления метод с условным именем MyMethod(). Предположим также, что некий студент-программист с условной фамилией Иванов включил впоследствии наш элемент управления MyClock в свою программу и назвал его экземпляр условно SaLo. После этого в своей программе студент-программист Иванов сможет написать следующий оператор
SaLo.MyMethod();
и тем самым применит метод к экземпляру SaLo элемента управления.
При создании заготовки проекта элемента управления оболочка автоматически генерирует код для метода AboutBox(). Когда программист, использующий наш элемент управления, вызывает в своей программе метод AboutBox(), появляется диалоговое окно About.
Перед тем, как включить в элемент управления свой собственный метод, проверим работу метода AboutBox(), который создал мастер приложения.
Постройте проект и поместите в тестовую утилиту ActiveX Control Test Container наш элемент управления MyClock
Обращая внимание, чтобы элемент управления был выделен, выполните пункт меню утилиты
В появившемся диалоговом окне На экране появится заготовка диалогового окна About

Метод существует и он работает!
Закройте тестовую утилиту и отредактируйте диалоговое окно About так, чтобы не стыдно было показать самой студентке Ельцовой - которая в этом деле добре разбирается (Ельцова, я восхищаюсь!)Ресурс About имеет имя IDD_ABOUTBOX_MYCLOCK в панели ресурсов Resource View. Мой About выглядит так
Базовые методы предопределены в оболочке VS.NET 2003. Их только два: DoClick() и Refresh().
Метод DoClick() имитирует щелчок на элементе управления, как будто бы пользователь щелкнул на нем. При выполнении метода Refresh() элемент управления перерисовывает сам себя, вызывая функцию OnDraw().
Для практики включим в элемент управления MyClock метод Refresh().
Откройте панель
Щелкните правой кнопкой мыши на узле _DMyClock или узле _DMyClockEvents (это безразлично).
Через контекстное меню Add/Add Method вызовите мастер добавления метода.
Раскройте список Method name: и выберите имя Refresh Сейчас окно мастера должно выглядеть так

Щелкните по кнопке Finish, чтобы добавить метод Refresh() в проект элемента управления
Вновь откройте панель Вспомним, что при выполнении метода Refresh() элемент управления MyClock должен перерисовывать сам себя, вызывая функцию OnDraw(). Сейчас же пока функция OnDraw() вызывается по сообщению WM_TIMER, генерируемому таймером. Если мы установим период OnDraw() будет обновлять окно элемента управления очень редко. Если в больших промежутках между Refresh(), который должен обновлять окно, то сможем убедиться, что он работает.
Испытания метода Refresh() проведем следующим образом
Постройте проект, чтобы обновить внесенные в MyClock изменения
Запустите утилиту тестирования ActiveX Control Test Container и поместите в нее наш элемент управления MyClock
При выделенном элементе управления щелкните по кнопке
В появившемся окне свойств выберите отредактированную нами ранее вкладку
Нажмите кнопку Применить и закройте окно свойств. Таймер элемента управления MyClock будет переведен на интервал срабатывания 55 секунд и можно продолжить испытание метода Refresh()
Нажмите кнопку OK или Отмена, чтобы закрыть окно свойств
При выделенном элементе управления выполните команду меню утилиты
В появившемся диалоговом окне
Сдвиньте окно Invoke Methods так, чтобы видеть элемент управления
Щелкая по кнопке Invoke убедитесь, что окно принудительно перерисовывается функцией OnDraw(), в тело которой мы ранее разместили код обновления времениДобавленный нами метод Refresh() работает!
Закройте окно тестовой утилиты без сохраненияПоскольку, как мы уже убедились, базовых методов всего два, а этого даже на сало не хватит, рассмотрим возможность добавления пользовательских ( Custom ) методов. Для примера добавим три пользовательских метода
StartClock() - для запуска часовBeepClock() - для запуска или остановки будильникаДля добавления метода StartClock() выполните следующее
Откройте панель Class View и раскройте узел MyClockLib, а затем узел интерфейса _DMyClock
Щелкните правой кнопкой мыши на узле _DMyClock и через контекстное меню Add/Add Method вызовите мастер добавления метода
Укажите в поле Method name имя добавляемого метода StartClock. Поле Internal name внутреннего имени заполнится автоматически на основе введенного внешнего имени
В поле Return type из раскрывающегося списка выбирете тип void
Щелкните по кнопке Finish
Для добавления метода StopClock() повторите те же шаги, которые выполнили для StartClock()Метод BeepClock() создадим с аргументом типа BYTE (unsigned char) , который будет принимать ненулевое (включить звонок) или нулевое (выключить звонок) значения.
Снова откройте панель Class View и раскройте узел MyClockLib, а затем узел интерфейса _DMyClock
Щелкните правой кнопкой мыши на узле _DMyClock и через контекстное меню Add/Add Method вызовите мастер добавления метода
Укажите в поле Method name имя добавляемого метода BeepClock Поле Internal name внутреннего имени заполнится автоматически на основе введенного внешнего имени.
В поле Return type из раскрывающегося списка выбирете тип void
В поле Parameter type из раскрывающегося списка выбирете значение BYTE, а в поле Parameter name введите beep Окно мастера должно выглядеть так

Щелкните по кнопке Add
Щелкните по кнопке Finish
Откройте панель
Перейдите в панель Solution Explorer и откройте в ней файл MyClockCtrl.cpp
Перейдите в конец файла MyClockCtrl.cpp и убедитесь, что мастер вместе с добавлением пользовательских методов создал и заготовки кода для их реализацииОбратите внимание, что заготовка для встроенного метода Refresh() отсутствует.
Для реализации методов StartClock() и StopClock() введем внутреннюю ( private ) логическую переменную flagClock типа bool в класс CMyClockCtrl. Инициализируем ее в конструкторе класса значением true, чтобы часы после создания объекта-экземпляра элемента управления MyClock сразу шли. В функцию OnDraw() введем условие обновления выходного текста в зависимости от значения переменной flagClock. В реализациях методов StartClock() и StopClock() будем устанавливать соответствующее значение флага.
Откройте файл MyClockCtrl.h и добавьте в конец объявления класса CMyClockCtrl код Добавление флага остановки часов в класс CMyClockCtrl
class CMyClockCtrl : public COleControl
{
.........................................................
private:
// Переменная-член текущей даты
CString m_strDate;
// Переменная-член текущего времени
CString m_strTime;
// Заполняет переменную-член m_strDate текущей датой
void GetCurDate(void);
// Заполняет переменную m_strTime текущим временем
void GetCurTime(void);
CString m_str;
public:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnDestroy();
protected:
void OnUpdateIntervalChanged(void);
LONG m_UpdateInterval;
void NewMinute(void)
{
FireEvent(eventidNewMinute, EVENT_PARAM(VTS_NONE));
}
void StartClock(void);
void StopClock(void);
void BeepClock(BYTE beep);
private:
bool flagClock;
};
Откройте файл MyClockCtrl.cpp и добавьте в конструктор класса CMyClockCtrl кодДобавление кода инициализации флага остановки часов в конструкторе класса CMyClockCtrl
CMyClockCtrl::CMyClockCtrl()
{
InitializeIIDs(IID_DMyClock, IID_DMyClockEvents);
// TODO: Initialize your control's instance data here.
// Установить начальные размеры
SetInitialSize(300, 15);
// Установить начальный ход часов
flagClock = true;
}
В функцию OnDraw() класса CMyClockCtrl добавьте условие перерисовки времениДобавление кода в функцию OnDraw() класса CMyClockCtrl
void CMyClockCtrl::OnDraw(
CDC* pdc,
const CRect rcBounds,
const CRect rcInvalid)
{
if (!pdc)
return;
// TODO: Replace the following code with your own drawing code.
// Задать цвет переднего плана (т.е. цвет текста)
// согласно значения свойства ForeColor
pdc->SetTextColor(TranslateColor(GetForeColor()));
// Установить режим прозрачного фона
pdc->SetBkMode(TRANSPARENT);
// Создать кисть на основе значения свойства BackColor
CBrush bkBrush(TranslateColor(GetBackColor()));
// Залить элемент управления, используя значение BackColor
pdc->FillRect(rcBounds, bkBrush);
// Получить и вывести текст текущей даты и времени
if(flagClock){ // Заморозить время
GetCurDate();
m_str = m_strDate;
GetCurTime();
m_str += m_strTime;
} // Разморозить время
pdc->ExtTextOut(rcBounds.left,
rcBounds.top,
ETO_CLIPPED,
rcBounds,
m_str,
m_str.GetLength(),
NULL);
// Получить текущее время и сгенерировать событие NewMinute
CTime curTime = CTime::GetCurrentTime();
if(curTime.Format("%S") == CString("00"))
NewMinute();
}
Отредактируйте реализации методов StartClock() и StopClock() в файле MyClockCtrl.cpp такРеализации методов StartClock() и StopClock()
void CMyClockCtrl::StartClock(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your dispatch handler code here
flagClock = true; // Запустить часы
}
void CMyClockCtrl::StopClock(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your dispatch handler code here
flagClock = false; // Остановить часы
}
Проверьте работу методов на тестовой утилите ActiveX Control Test Container
Теперь осталось реализовать добавленный пользовательский метод BeepClock(). Для простоты построим его таким же образом, что и реализацию предыдущих методов, только флаг введем с именем flagBeep. Функция просто будет устанавливать или сбрасывать флаг, а подачу звукового сигнала организуем в функции-обработчике NewMinute. Таким образом, при включенном методом BeepClock() флаге flagBeep в начале каждой минуты компьютер будет выдавать звуковой сигнал.
Откройте файл MyClockCtrl.h и добавьте в конец объявления класса CMyClockCtrl кодДобавление флага flagBeep в класс CMyClockCtrl
class CMyClockCtrl : public COleControl
{
.........................................................
private:
bool flagClock;
bool flagBeep;
};
Откройте файл MyClockCtrl.cpp и добавьте в конструктор класса CMyClockCtrl кодДобавление кода инициализации флага flagBeep в конструкторе класса CMyClockCtrl
CMyClockCtrl::CMyClockCtrl()
{
InitializeIIDs(IID_DMyClock, IID_DMyClockEvents);
// TODO: Initialize your control's instance data here.
// Установить начальные размеры
SetInitialSize(300, 15);
// Установить начальный ход часов
flagClock = true;
flagBeep = false;
}
В функцию NewMinute() файла MyClockCtrl.h добавьте условие подачи сигналаДобавление кода в функцию NewMinute() класса CMyClockCtrl
class CMyClockCtrl : public COleControl
{
...........................................
void NewMinute(void)
{
FireEvent(eventidNewMinute, EVENT_PARAM(VTS_NONE));
if(flagBeep)
MessageBeep((WORD) - 1);
}
void StartClock(void);
void StopClock(void);
void BeepClock(BYTE beep);
private:
bool flagClock;
bool flagBeep;
};
Отредактируйте реализации метода BeepClock() в файле MyClockCtrl.cpp такРеализации метода BeepClock()
void CMyClockCtrl::BeepClock(BYTE beep)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your dispatch handler code here
flagBeep = beep ? true : false;
}
Проверьте работу метода на тестовой утилите ActiveX Control Test ContainerПри создании заготовки проекта элемента управления оболочка автоматически присвоила ему имя MyClock Control. Изменим это имя на Часы.
Откройте тестовую утилиту и командой меню
Найдите элемент MyClock Control и снимите его с Unregister
Откройте ресурс String Table в панели Resource View оболочки проекта
Найдите идентификатор IDS_MYCLOCK и измените его значение с MyClock Control на новое значение Часы
Постройте проект и через тестовую утилиту вновь зарегистрируйте его в системе под новым название Часы. Проверьте через команду утилиты Теперь подключим наш элемент к оболочке VS.NET 2003.
Откройте ресурс диалогового окна IDD_PROPPAGE_MYCLOCK или IDD_ABOUTBOX_MYCLOCK, чтобы активизировать панель Toolbox оболочки
Раскройте одну из вкладок панели Toolbox, например, General (можно открыть и Dialog Editor, но для пробы General практически пустая)
Щелкните на ней правой кнопкой мыши и из контекстного меню выберите команду Add/Remove Items...
В появившемся диалоговом окне установите вкладку
После щелчка по кнопке Перейдите к разработке тестового приложения, где будет использован наш компонент Часы.
Для испытания нашего компонента "Часы" построим тестовое диалоговое приложение, в котором поместим элементы для управления всеми заложенными в компонент возможностями.
Перейдите в свой каталог и постройте в нем новый MFC Application - проект и именем Test
Настройте вкладки мастера проекта так


Используя клавишу
Смените стандартную иконку приложения на другую
Смените название приложения на Испытание элемента управления "Часы"
Установите режим построения проекта Solution Configurations в значение Release
Откройте диалоговую форму IDD_TEST_DIALOG и удалите с нее все компоненты, сгенерированные мастеромРазместите в верхней части диалоговой формы элемент управления "Часы", разработанный ранее, для чего:
Щелкните на форме правой кнопкой мыши и через контекстное меню выполните команду Insert ActiveX Control...
В открывшемся окне выберите элемент Часы из списка зарегистрированных в компьютере элементов (процедуру регистрации компонента в компьютере мы рассмотрели при разработке самого управляющего элемента, а также в
Измените идентификатор компонента Часы на IDC_MYCLOCKCTRL
Разместите на диалоговой форме стандартные элементы управления и задайте их свойства в соответствии с таблицей.| Элементы управления тестового приложения | |||
|---|---|---|---|
| Тип элемента управления | Имя свойства | Значение свойства | Пояснения |
Group Box |
ID |
IDC_STATIC |
Рамка для управления часами |
Caption |
Режимы | ||
|
ID |
IDC_CHECK_BEEP |
Включение/Выключение звука ActiveX |
Caption |
Звуковой сигнал | ||
|
ID |
IDC_CHECK_START |
Включение/Выключение хода часов на экране |
Caption |
Ход часов | ||
Group Box |
ID |
IDC_STATIC |
Рамка для элементов управления цветом |
Caption |
Цвет | ||
|
ID |
IDC_RADIO_FORE |
Регулируется цвет текста |
Caption |
Текст | ||
|
ID |
IDC_RADIO_BACK |
Регулируется цвет фона |
Caption |
Фон | ||
Left Text |
True |
||
|
ID |
IDC_COMBO_COLOR |
Список цветов |
Vertical Scrollbar |
True |
||
Sort |
False |
Не сортировать названия цвета | |
Group Box |
ID |
IDC_STATIC |
Рамка для управления интервалом |
Caption |
Интервал таймера | ||
Static Text |
ID |
IDC_SHOWINTERVAL |
Отображение интервала |
Caption |
1000 | ||
Align Text |
Right |
||
|
ID |
IDC_SLIDER_INTERVAL |
Регулирование интервала |
Orientation |
Horizontal |
Расположение по горизонтали | |
Button |
ID |
IDC_BUTTON_EXIT |
Завершить приложение |
Caption |
Выход | ||
Button |
ID |
IDC_BUTTON_ABOUTCLOCK |
Об авторах MyClock |
Caption |
About Часов |
||
Интерфейс тестового приложения должен выглядеть примерно так

Добавьте для элементов управления диалогового окна приложения ассоциированные переменные в соответствии с таблицей ниже
Для каждого элемента управления вызывайте мастер Add Variable через контекстное меню для этого элемента
Следите за тем, чтобы флажок
Control ID Access Variable t ype Variable name Category Comment IDC_MYCLOCKCTRLprivateCMyclockctrl m_MyClock Control IDC_CHECK_BEEP private BOOLm_bCheckBeepValue IDC_CHECK_START private BOOL m_bCheckStart Value IDC_RADIO_FORE privateint m_iSwapColor ValueIDC_COMBO_COLORprivateCComboBoxm_ComboColorControl IDC_SHOWINTERVALprivate CString m_sShowIntervalValue IDC_SLIDER_INTERVALprivate CSliderCtrl m_SliderIntervalControlИнициализацию ассоциированных переменных выполним в функции OnInitDialog() класса CTestDlg.
Откройте файл TestDlg.cpp тестового приложения
Найдите и дополните функцию OnInitDialog() следующим кодомДополнение функции OnInitDialog()
BOOL CTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
..........................................
// TODO: Add extra initialization here
// Переменные для элементов управления тестового приложения
m_bCheckBeep = false;
m_bCheckStart = true;
m_iSwapColor = 0;
m_ComboColor.AddString("Черный");
m_ComboColor.AddString("Синий");
m_ComboColor.AddString("Зеленый");
m_ComboColor.AddString("Бирюзовый");
m_ComboColor.AddString("Красный");
m_ComboColor.AddString("Сиреневый");
m_ComboColor.AddString("Желтый");
m_ComboColor.AddString("Белый");
m_ComboColor.SetCurSel(0);
long tmp = m_MyClock.GetUpdateInterval();
m_sShowInterval.Format("%ld", tmp);
m_SliderInterval.SetRangeMin(1000);
m_SliderInterval.SetRangeMax(5000);
UpdateData(FALSE); // Выдать значения на экран
return TRUE; // return TRUE unless you set the focus to a control
}
Постройте обработчик для кнопки Выход и заполните его такОбработчик для кнопки 'Выход'
void CTestDlg::OnBnClickedButtonExit()
{
// TODO: Add your control notification handler code here
OnOK();
}
Постройте обработчик для кнопки About Часов и заполните его такОбработчик для кнопки 'About Часов'
void CTestDlg::OnBnClickedButtonAboutclock()
{
// TODO: Add your control notification handler code here
m_MyClock.AboutBox();
}
Постройте обработчик для флажка Звуковой сигнал и заполните его такОбработчик для флажка 'Звуковой сигнал'
void CTestDlg::OnBnClickedCheckBeep()
{
// TODO: Add your control notification handler code here
// Получить текущее значение от экрана
UpdateData(TRUE);
// Проверить значение флажка и установить режим Beep
if(m_bCheckBeep == TRUE)
m_MyClock.BeepClock(1);
else
m_MyClock.BeepClock(0);
}
Постройте обработчик для флажка ' Ход часов ' и заполните его такОбработчик для флажка 'Ход часов'
void CTestDlg::OnBnClickedCheckStart()
{
// TODO: Add your control notification handler code here
// Получить текущее значение от экрана
UpdateData(TRUE);
// Проверить значение флажка и Остановить/Запустить часы
if(m_bCheckStart == TRUE)
m_MyClock.StartClock();
else
m_MyClock.StopClock();
}
Постройте обработчик для раскрывающегося списка выбора цвета, добавив в проект таблицу цветов и код. Для этого выделите элемент Обработчик для раскрывающегося списка
void CTestDlg::OnCbnSelchangeComboColor()
{
// TODO: Add your control notification handler code here
// Таблица цветов
COLORREF colors[8] = {
RGB( 0, 0, 0),
RGB( 0, 0, 255),
RGB( 0, 255, 0),
RGB( 0, 255, 255),
RGB(255, 0, 0),
RGB(255, 0, 255),
RGB(255, 255, 0),
RGB(255, 255, 255)
};
// Получить текущее значение от экрана
UpdateData(TRUE);
int index = m_ComboColor.GetCurSel();
if(m_iSwapColor == 0)
m_MyClock.SetForeColor(colors[index]);
else
m_MyClock.SetBackColor(colors[index]);
}
Для управления ползунком необходимо связать сообщение Windows WM_HSCROLL с методом OnHScroll() (сообщение WM_VSCROLL и метод OnVScroll() - для вертикальных ползунков.
Постройте обработчик для ползунка. Для этого выделите форму IDD_TEST_DIALOG
Перейдите в панель Properties и установите режим Messages
Найдите сообщение
Обработчик заполните следующим кодомОбработчик для ползунка
void CTestDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: Add your message handler code here and/or call default
int pos = m_SliderInterval.GetPos();
m_sShowInterval.Format("%d", pos);
UpdateData(FALSE);
m_MyClock.SetUpdateInterval((long)pos);
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
Оформите About тестового приложения
Постройте приложение и убедитесь в правильности его работыВнешний вид приложения должен выглядеть примерно так

Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.