Практикум прикладного программирования на MFC и C++ в среде VS.NET

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

Разбить на страницы
Показывать лекцию целиком
Файлы к данной лабораторной работе, Вы можете скачать здесь.

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

Поскольку расширением файла элемента управления ActiveX является .OCX, то иногда элементы управления называют элементами OCX.

Далее в этой работе мы создадим и протестируем свой собственный элемент управления ActiveX - MyClock.OCX, который будет выполнять задачу непрерывного вывода текущего времени. Далее мы присоединим готовый компонент в панель Toolbox оболочки, чтобы его можно было использовать в любом приложении наряду со стандартными компонентами.

Создание проекта элемента управления MyClock.OCX

  • Войдите в свой каталог выполнения лабораторных работ
  • Запустите оболочку Microsoft Visual Studio .NET 2003
  • Создайте новый проект и назовите его
  • Настройте мастер
  • Установите в раскрывающемся списке Solution Configurations режим построения решения Release
  • Тестирование элемента управления MyClock

    После того, как с помощью мастера мы создали проект и каркасы файлов элемента управления 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, чтобы завершить процесс предварительного тестирования
  • Настройка значка инструмента MyClock

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

  • В панели Resource View раскройте узел Bitmap и вызовите на редактирование ресурс IDB_MYCLOCK
  • Воспользуйтесь расположенными в верхней части оболочки кнопками редактора Image Editor и замените значок по умолчанию

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

  • Отображение текста

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

  • В панели 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 -класса CDC имеет такие аргументы

  • Первые два параметра задают верхний левый угол начало вывода текста.
  • Третий параметр определяет, как будет выводиться текст. В нашем случае макрос 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. Время должно меняться ежесекундно
  • Сохраните в своем каталоге сессию тестирования утилиты
  • Включение свойств в элемент управления MyClock

    Включение базовых свойств в элемент управления MyClock

    Любой элемент управления имеет некоторый неизменный ограниченный набор свойств, которые определяют его основные характеристики. Этот набор называется базовым. Базовые свойства любого элемента управления 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);
    }
  • Включение специального свойства в элемент управления MyClock

    Базовые свойства предусмотрены оболочкой при создании каркаса. Но во многих случаях может понадибиться включение своих собственных свойств в элемент управления, со своим названием и кодом, который делает свойство функционирующим. Эти свойства называются специальными ( Custom ). Включим в элемент управления MyClock дополнительное свойство с именем UpdateInterval, которое будем использовать для хранения чисел, устанавливающих период обновления таймера.

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

  • Откройте панель
  • Через контекстное меню элемента
  • Появится мастер, в котором установите переключатель в положение
  • Нажмите кнопку
  • Таким образом мы определили, что новое свойство будет иметь имя UpdateInterval и что это свойство будет использоваться для хранения чисел в переменной m_UpdateInterval типа long. В поле Notification function (функция уведомления) установлено имя обработчика OnUpdateIntervalChanged(), который будет срабатывать всякий раз, когда значение свойства UpdateInterval изменяется.

    Инициализация свойства 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

    Свойство UpdateInterval должно устанавливать частоту срабатывания системного таймера. В настоящий момент это происходит каждые 1000 миллисекунд, поскольку мы установили ранее такой интервал в функции 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

    Напишем код, который защитит элемент управления от некорректного ввода значения в свойство 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();
    }
  • Использование таблицы свойств элемента управления MyClock для удобства тестирования

    Использование утилиты 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

    Вкладка 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
  • Результат наших усилий внешне должен выглядеть так

    Включение событий в элемент управления 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 наличие добавленного события, как мы это делали раньше

    Результат будет таким

  • Как мы видим, в списке присутствуют все три события, которые мы добавляли в элемент управления.

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

    Когда мы включаем в элемент управления базовое событие, такое, например, как 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 всякий раз, когда обнуляется количество секунд
  • Результат будет примерно таким

    Включение методов в элемент управления MyClock

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

    Предположим, что мы включили в элемент управления метод с условным именем MyMethod(). Предположим также, что некий студент-программист с условной фамилией Иванов включил впоследствии наш элемент управления MyClock в свою программу и назвал его экземпляр условно SaLo. После этого в своей программе студент-программист Иванов сможет написать следующий оператор

    SaLo.MyMethod();

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

    Встроенный метод AboutBox()

    При создании заготовки проекта элемента управления оболочка автоматически генерирует код для метода AboutBox(). Когда программист, использующий наш элемент управления, вызывает в своей программе метод AboutBox(), появляется диалоговое окно About.

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

  • Постройте проект и поместите в тестовую утилиту ActiveX Control Test Container наш элемент управления MyClock
  • Обращая внимание, чтобы элемент управления был выделен, выполните пункт меню утилиты
  • В появившемся диалоговом окне
  • На экране появится заготовка диалогового окна About

    Метод существует и он работает!

  • Закройте тестовую утилиту и отредактируйте диалоговое окно About так, чтобы не стыдно было показать самой студентке Ельцовой - которая в этом деле добре разбирается (Ельцова, я восхищаюсь!)
  • Ресурс About имеет имя IDD_ABOUTBOX_MYCLOCK в панели ресурсов Resource View. Мой About выглядит так

    Включение базового метода в элемент управления MyClock

    Базовые методы предопределены в оболочке VS.NET 2003. Их только два: DoClick() и Refresh().

    Метод DoClick() имитирует щелчок на элементе управления, как будто бы пользователь щелкнул на нем. При выполнении метода Refresh() элемент управления перерисовывает сам себя, вызывая функцию OnDraw().

    Для практики включим в элемент управления MyClock метод Refresh().

  • Откройте панель
  • Щелкните правой кнопкой мыши на узле _DMyClock или узле _DMyClockEvents (это безразлично).
  • Через контекстное меню Add/Add Method вызовите мастер добавления метода.
  • Раскройте список Method name: и выберите имя Refresh
  • Сейчас окно мастера должно выглядеть так

  • Щелкните по кнопке Finish, чтобы добавить метод Refresh() в проект элемента управления
  • Вновь откройте панель
  • Тестирование включенного базового метода 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() работает!

  • Закройте окно тестовой утилиты без сохранения
  • Включение специальных методов в элемент управления MyClock

    Поскольку, как мы уже убедились, базовых методов всего два, а этого даже на сало не хватит, рассмотрим возможность добавления пользовательских ( 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
  • Изменение системного имени компонента и включение его в панель Toolbox оболочки

    При создании заготовки проекта элемента управления оболочка автоматически присвоила ему имя 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 Режимы
    Check Box ID IDC_CHECK_BEEP Включение/Выключение звука ActiveX
    Caption Звуковой сигнал
    Check Box ID IDC_CHECK_START Включение/Выключение хода часов на экране
    Caption Ход часов
    Group Box ID IDC_STATIC Рамка для элементов управления цветом
    Caption Цвет
    Radio Button ID IDC_RADIO_FORE Регулируется цвет текста
    Caption Текст
    Radio Button ID IDC_RADIO_BACK Регулируется цвет фона
    Caption Фон
    Left Text True
    Combo Box 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
    Slider Control 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_MYCLOCKCTRL private CMyclockctrl m_MyClock Control Элемент ActiveX Часы IDC_CHECK_BEEP private BOOL m_bCheckBeep Value Включение/Выключение звука ActiveX IDC_CHECK_START private BOOL m_bCheckStart Value Включение/Выключение хода часов на экране IDC_RADIO_FORE private int m_iSwapColor Value Регулируется цвет текста/фона IDC_COMBO_COLOR private CComboBox m_ComboColor Control Для управления списком выбора цвета IDC_SHOWINTERVAL private CString m_sShowInterval Value Отображение интервала IDC_SLIDER_INTERVAL private CSliderCtrl m_SliderInterval Control Регулирование интервала
  • Инициализация ассоциированных переменных

    Инициализацию ассоциированных переменных выполним в функции 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 является .OCX, то иногда элементы управления называют элементами OCX.

    Далее в этой работе мы создадим и протестируем свой собственный элемент управления ActiveX - MyClock.OCX, который будет выполнять задачу непрерывного вывода текущего времени. Далее мы присоединим готовый компонент в панель Toolbox оболочки, чтобы его можно было использовать в любом приложении наряду со стандартными компонентами.

    Создание проекта элемента управления MyClock.OCX

  • Войдите в свой каталог выполнения лабораторных работ
  • Запустите оболочку Microsoft Visual Studio .NET 2003
  • Создайте новый проект и назовите его
  • Настройте мастер
  • Установите в раскрывающемся списке Solution Configurations режим построения решения Release
  • Тестирование элемента управления MyClock

    После того, как с помощью мастера мы создали проект и каркасы файлов элемента управления 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, чтобы завершить процесс предварительного тестирования
  • Настройка значка инструмента MyClock

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

  • В панели Resource View раскройте узел Bitmap и вызовите на редактирование ресурс IDB_MYCLOCK
  • Воспользуйтесь расположенными в верхней части оболочки кнопками редактора Image Editor и замените значок по умолчанию

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

  • Отображение текста

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

  • В панели 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 -класса CDC имеет такие аргументы

  • Первые два параметра задают верхний левый угол начало вывода текста.
  • Третий параметр определяет, как будет выводиться текст. В нашем случае макрос 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. Время должно меняться ежесекундно
  • Сохраните в своем каталоге сессию тестирования утилиты
  • Включение свойств в элемент управления MyClock

    Включение базовых свойств в элемент управления MyClock

    Любой элемент управления имеет некоторый неизменный ограниченный набор свойств, которые определяют его основные характеристики. Этот набор называется базовым. Базовые свойства любого элемента управления 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);
    }
  • Включение специального свойства в элемент управления MyClock

    Базовые свойства предусмотрены оболочкой при создании каркаса. Но во многих случаях может понадибиться включение своих собственных свойств в элемент управления, со своим названием и кодом, который делает свойство функционирующим. Эти свойства называются специальными ( Custom ). Включим в элемент управления MyClock дополнительное свойство с именем UpdateInterval, которое будем использовать для хранения чисел, устанавливающих период обновления таймера.

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

  • Откройте панель
  • Через контекстное меню элемента
  • Появится мастер, в котором установите переключатель в положение
  • Нажмите кнопку
  • Таким образом мы определили, что новое свойство будет иметь имя UpdateInterval и что это свойство будет использоваться для хранения чисел в переменной m_UpdateInterval типа long. В поле Notification function (функция уведомления) установлено имя обработчика OnUpdateIntervalChanged(), который будет срабатывать всякий раз, когда значение свойства UpdateInterval изменяется.

    Инициализация свойства 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

    Свойство UpdateInterval должно устанавливать частоту срабатывания системного таймера. В настоящий момент это происходит каждые 1000 миллисекунд, поскольку мы установили ранее такой интервал в функции 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

    Напишем код, который защитит элемент управления от некорректного ввода значения в свойство 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();
    }
  • Использование таблицы свойств элемента управления MyClock для удобства тестирования

    Использование утилиты 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

    Вкладка 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
  • Результат наших усилий внешне должен выглядеть так

    Включение событий в элемент управления 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 наличие добавленного события, как мы это делали раньше

    Результат будет таким

  • Как мы видим, в списке присутствуют все три события, которые мы добавляли в элемент управления.

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

    Когда мы включаем в элемент управления базовое событие, такое, например, как 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 всякий раз, когда обнуляется количество секунд
  • Результат будет примерно таким

    Включение методов в элемент управления MyClock

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

    Предположим, что мы включили в элемент управления метод с условным именем MyMethod(). Предположим также, что некий студент-программист с условной фамилией Иванов включил впоследствии наш элемент управления MyClock в свою программу и назвал его экземпляр условно SaLo. После этого в своей программе студент-программист Иванов сможет написать следующий оператор

    SaLo.MyMethod();

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

    Встроенный метод AboutBox()

    При создании заготовки проекта элемента управления оболочка автоматически генерирует код для метода AboutBox(). Когда программист, использующий наш элемент управления, вызывает в своей программе метод AboutBox(), появляется диалоговое окно About.

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

  • Постройте проект и поместите в тестовую утилиту ActiveX Control Test Container наш элемент управления MyClock
  • Обращая внимание, чтобы элемент управления был выделен, выполните пункт меню утилиты
  • В появившемся диалоговом окне
  • На экране появится заготовка диалогового окна About

    Метод существует и он работает!

  • Закройте тестовую утилиту и отредактируйте диалоговое окно About так, чтобы не стыдно было показать самой студентке Ельцовой - которая в этом деле добре разбирается (Ельцова, я восхищаюсь!)
  • Ресурс About имеет имя IDD_ABOUTBOX_MYCLOCK в панели ресурсов Resource View. Мой About выглядит так

    Включение базового метода в элемент управления MyClock

    Базовые методы предопределены в оболочке VS.NET 2003. Их только два: DoClick() и Refresh().

    Метод DoClick() имитирует щелчок на элементе управления, как будто бы пользователь щелкнул на нем. При выполнении метода Refresh() элемент управления перерисовывает сам себя, вызывая функцию OnDraw().

    Для практики включим в элемент управления MyClock метод Refresh().

  • Откройте панель
  • Щелкните правой кнопкой мыши на узле _DMyClock или узле _DMyClockEvents (это безразлично).
  • Через контекстное меню Add/Add Method вызовите мастер добавления метода.
  • Раскройте список Method name: и выберите имя Refresh
  • Сейчас окно мастера должно выглядеть так

  • Щелкните по кнопке Finish, чтобы добавить метод Refresh() в проект элемента управления
  • Вновь откройте панель
  • Тестирование включенного базового метода 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() работает!

  • Закройте окно тестовой утилиты без сохранения
  • Включение специальных методов в элемент управления MyClock

    Поскольку, как мы уже убедились, базовых методов всего два, а этого даже на сало не хватит, рассмотрим возможность добавления пользовательских ( 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
  • Изменение системного имени компонента и включение его в панель Toolbox оболочки

    При создании заготовки проекта элемента управления оболочка автоматически присвоила ему имя 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 Режимы
    Check Box ID IDC_CHECK_BEEP Включение/Выключение звука ActiveX
    Caption Звуковой сигнал
    Check Box ID IDC_CHECK_START Включение/Выключение хода часов на экране
    Caption Ход часов
    Group Box ID IDC_STATIC Рамка для элементов управления цветом
    Caption Цвет
    Radio Button ID IDC_RADIO_FORE Регулируется цвет текста
    Caption Текст
    Radio Button ID IDC_RADIO_BACK Регулируется цвет фона
    Caption Фон
    Left Text True
    Combo Box 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
    Slider Control 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_MYCLOCKCTRL private CMyclockctrl m_MyClock Control Элемент ActiveX Часы IDC_CHECK_BEEP private BOOL m_bCheckBeep Value Включение/Выключение звука ActiveX IDC_CHECK_START private BOOL m_bCheckStart Value Включение/Выключение хода часов на экране IDC_RADIO_FORE private int m_iSwapColor Value Регулируется цвет текста/фона IDC_COMBO_COLOR private CComboBox m_ComboColor Control Для управления списком выбора цвета IDC_SHOWINTERVAL private CString m_sShowInterval Value Отображение интервала IDC_SLIDER_INTERVAL private CSliderCtrl m_SliderInterval Control Регулирование интервала
  • Инициализация ассоциированных переменных

    Инициализацию ассоциированных переменных выполним в функции 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 тестового приложения
  • Постройте приложение и убедитесь в правильности его работы
  • Внешний вид приложения должен выглядеть примерно так

    Вернуться к учебному плану