Управление с помощью объекта класса-оболочки

Для управления внедренным элементом ActiveX надо ввести в существующий диалоговый класс CTestGLDlg объект (переменную типа) класса-оболочки. Этот шаг тоже автоматизирован в Studio.Net, так как введение объекта влечет сразу несколько строк изменения кода.

  1. Поставьте фокус на окно внедренного элемента IDC_OPENGL в форме диалога и вызовите контекстное меню.
  2. В меню выберите команду Variable, которая запустит мастер Add Member Variable Wizard.
  3. Установите флажок Control Variable и задайте в полях диалоговой страницы мастера следующие значения: Access — public, Variable type — COpenGL, Variable name — * m_Ctrl, Control ID - IDC_OPENGL
  4. Обратите внимание на то, что в.поле Control type уже выбран тип элемента OCX, и нажмите кнопку Finish.

Результатом работы мастера являются следующие строки программы:

  • объявление переменной COpenGL m_ctrl; в файле заголовков TestGLDlg.h;
  • вызов функции DDX_Control(pDX, IDC_OPENGL, m_ctrl), связывающей элемент управления в окне диалога с переменной m_ctrl. Этот вызов вы найдете в теле функции CTestGLDlg::DoDataExchange;
  • Для обеспечения видимости вставьте в начало файла TestGLDlg.h директиву:

    #include "opengl.h"

    В конец файла Stdafx.h вставьте директивы подключения заголовков библиотеки OpenGL:

    # include <gl/gl.h>

    // Будем пользоваться OpenGL

    #include <gl/glu.h>

    Теперь следует поместить в окно диалога элементы управления. Здесь мы не будем пользоваться страницами свойств элемента, созданными нами в рамках предыдущего проекта. Вместо этого мы покажем, как можно управлять внедренным элементом ActiveX с помощью объекта m_ctrl. Перейдите в окно диалогового редактора и придайте окну диалога IDD_TESTGL_DIALOG.

    Идентификаторы для элементов управления можно задать так, как показано в табл. 9.2.

    Таблица 9.2. Идентификаторы элементов управления

    Элемент

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

    Диалог

    IDD_TESTGL_DIALOG

    Кнопка Data File

    IDCJILENAME

    Кнопка Back Color

    IDC.BKCLR

    Переключатель Quads

    IDC_QUADS

    Переключатель Strips

    IDC_STRIPS

    Выпадающий список Fill Mode

    IDC_FILL

    Ползунок Light (X)

    IDC_XPOS

    Кнопка Close

    IDOK

    Для кнопки Quads установите свойство Group в положение True, а для кнопки Strips — в False. Обе они должны иметь свойство Auto в состоянии True. Важно еще то, что числовые значения их идентификаторов должны следовать по порядку. Для кнопки Data File установите свойство DefaultButton. Для выпадающего списка снимите свойство Sort (сделайте его False) и слегка растяните вниз его окно в открытом состоянии, для этого сначала нажмите кнопку раскрывания. Для ползунка вы можете установить свойство Point в положение Top/Left. Обратите внимание на тот факт, что в режиме дизайна вы можете открыть с помощью правой кнопки мыши диалог со страницами свойств для элемента IDC_OPENGL, одну из которых мы создавали в предыдущем проекте. Теперь с помощью Studio.Net введите в диалоговый класс обработчики следующих событий:

  • OnClickedFilename — нажата кнопка IDC_FILENAME,
  • OnCiickedBkcir — нажата кнопка IDC_BKCLR,
  • OnSelchangeFill — изменился выбор в списке IDC_FILL,
  • OnClickedQuads — нажата кнопка IDC_QUADS,
  • OnHScroll — изменилась позиция ползунка IDC_XPOS,
  • OnClickedStrips — нажата кнопка IDC_STRIPS.
  • Ниже мы приведем тела этих функций, а сейчас отметим, что все они пользуются услугами класса-оболочки для прямого вызова методов СОМ-сервера. Однако, как вы могли заключить из рассмотрения кодов класса COpenGL, на самом деле вызов будет происходить с помощью интерфейса IDispatch, а точнее его метода Invoke. Функция cwnd: : invokeHelper, вызов которой вы видите во всех методах COpenGL, преобразует параметры к типу VARIANTARG, а затем вызывает функцию Invoke. Если происходит отказ, то Invoke выбрасывает исключение.

    В диалоговом классе мы попутно произвели упрощения, которые связаны с удалением ненужных функций OnPaint и OnQueryDragicon. Эти изменения обсуждались при разработке приложения Look. Во избежание недоразумений, которые могут возникнуть в связи с многочисленным ручным редактированием, приведем коды как декларации, так и реализации класса CTestGLDlg:

    //=== Декларация диалогового класса (Файл TestGLDlg.h)

    #include "opengl.h"

    #pragma once

    class CTestGLDlg : public CDialog

    {

    public:

    CTestGLDlg(CWnd* p = NULL);

    enum

    {

    IDD = IDD_TESTGL_DIALOG

    };

    //======= Объект класса-оболочки

    COpenGL m_Ctrl;

    //======= Запоминаем способ изображения

    BOOL m_bQuads;

    //======= Реакции на регуляторы в окне диалога

    void OnSelchangeFill(void);

    void OnClickedFilename(void);

    afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);

    void OnCiickedBkcir(void);

    void OnClickedQuads(void);

    void OnClickedStrips(void);

    protected:

    virtual

    void DoDataExchange(CDataExchange* pDX) ;

    virtual BOOL OnlnitDialog();

    afx_msg void OnSysCommand(UINT nID, LPARAM IParam);

    DECLARE_MESSAGE_MAP()

    };

    В файл реализации методов класса мы кроме функций обработки сообщений от элементов управления вставили код начальной установки этих элементов. Для этой цели нам опять понадобилась связь с сервером, которую обеспечивает объект m_ctrl класса-оболочки. Характерным моментом является то, что обрабатываем событие WM_HSCROLL, которое поступает окну диалога, вместо того чтобы обработать уведомляющее событие NM_RELEASEDCAPTURE, которое идет от элемента типа Slider Control. Такая тактика позволяет реагировать на управление ползунком клавишами, а не только мышью:

    #include "stdafx.h"

    #include "TestGL.h"

    #include "TestGLDlg.h"

    #ifdef _DEBUG

    #define new DEBUG_NEW

    #undef THIS_FILE

    static char THIS_FILE[] = _FILE_;

    #endif

    //====== Пустое тело конструктора

    CTestGLDlg::CTestGLDlg(CWnd* p) : CDialog(CTestGLDlg::IDD, p){}

    void CTestGLDlg::DoDataExchange(CDataExchange* pDX) {

    //====== Связывание переменной с элементом

    DDX_Control(pDX, IDCJDPENGL, m_Ctrl);

    CDialog::DoDataExchange(pDX);

    }

    //====== Здесь мы убрали ON_WM_PAINT и т. д.

    BEGIN_MESSAGE_MAP(CTestGLDlg, CDialog) ON_WM_SYSCOMMAND()

    //

    }

    }

    AFX_MSG_MAP

    ON_CBN_SELCHANGE(IDC_FILL, OnSelchangeFill)

    ON_BN_CLICKED(IDC_FILENAME, OnClickedFilename)

    ON_WM_HSCROLL()

    ON_BN_CLICKED(IDC_BKCLR, OnClickedBkclr)

    ON_BN_CLICKED(IDC_QUADS, OnClickedQuads)

    ON_BN_CLICKED(IDC_STRIPS, OnClickedStrips)

    END_MESSAGE_MAP()

    //===== CTestGLDlg message handlers

    BOOL CTestGLDlg::OnInitDialog()

    {

    //====== Добываем адрес меню управления окном

    CMenu* pSysMenu = GetSystemMenu(FALSE);

    if (pSysMenu)

    {

    //====== Добавляем команду About

    pSysMenu->AppendMenu(MF_SEPARATOR);

    pSysMenu->AppendMenu(MF_STRING,

    IDM_ABOUTBOX,"About...");

    }

    //====== Загружаем стандартный значок

    HICON hMylcon = ::LoadIcon(0,(char*)IDI_WINLOGO);

    Setlcon(hMylcon, TRUE); // Set big icon Setlcon(hMylcon, FALSE);

    // Set small icon

    CDialog::OnInitDialog();

    //====== Начальная установка элементов

    CComboBox *pBox = (CComboBox*)GetDlgltem(IDC_FILL);

    pBox->AddString("Points"); pBox->AddString("Lines");

    pBox->AddString("Fill"); pBox->SetCurSel (2);

    //==== Выясняем состояние режима изображения полигонов

    m_Ctrl.GetQuad(&m_bQuads);

    WPARAM w = m_bQuads ? BST_CHECKED : BST_UNCHECKED;

    //===== Устанавливаем состояние переключателя

    GetDlgltem(IDC_QUADS)->SendMessage(BM_SETCHECK, w, 0);

    w = m_bQuads ? BST_UNCHECKED : BST_CHECKED;

    GetDlgltem(IDC_STRIPS)->SendMessage(BM_SETCHECK, w, 0);

    return TRUE;

    }

    void CTestGLDlg::OnSysCommand(UINT nID, LPARAM iParam)

    {

    if ((nID S OxFFFO) == IDM_ABOUTBOX)

    {

    CDialog(IDD_ABOUTBOX).DoModal();

    }

    else

    {

    CDialog::OnSysCommand(nID, IParam);

    }

    }

    //====== Выбор из списка типа Combo-box

    void CTestGLDlg::OnSelchangeFill(void) "'*

    {

    DWORD sel = ((CComboBox*)GetDlgltem(IDC_FILL))->GetCurSel();

    sel = sel==0 ? GL_POINT : sel==l ? GL_LINE

    : GL_FILL;

    m_Ctrl.SetFillMode(sel);

    }

    //==== Нажатие на кнопку запуска файлового диалога

    void CTestGLDlg::OnClickedFilename(void)

    {

    m_Ctrl.ReadData();

    }

    //====== Реакция на сдвиг ползунка

    void CTestGLDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)

    {

    //====== Выясняем текущую позицию, которая не во

    //====== всех случаях отражена в параметре nPos

    nPos = ((CSliderCtrl*)GetDlgItem(IDC_XPOS))->GetPos() ;

    m_Ctrl.SetLightParam (0, nPos);

    }

    //====== Запускаем стандартный диалог

    void CTestGLDlg::OnClickedBkclr(void)

    {

    DWORD clr = m_Ctrl.GetFillColor() ;

    CColorDialog dig (clr);

    dig.m_cc.Flags |= CC_FULLOPEN;

    if (dlg.DoModal()==IDOK)

    {

    m_Ctrl.SetFillColor(dlg.m_cc.rgbResult);

    }

    }

    //====== Запоминаем текущее состояние и

    //====== вызываем метод сервера

    void CTestGLDlg::OnClickedQuads(void)

    {

    m_Ctrl.SetQuad(m_bQuads = TRUE);

    }

    void CTestGLDlg::OnClickedStrips(void)

    {

    m_Ctrl.SetQuad(m_bQuads = FALSE);

    }

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

    Подведем итог. В этом уроке мы научились:

  • вносить функциональность окна OpenGL в окно, управляемое ATL-классом CWindow;
  • добавлять с помощью Studio.Net новые методы в интерфейс, представляемый ко-классом;
  • учитывать особенности обработки сообщений Windows в рамках ATL;
  • управлять контекстом передачи OpenGL, связанным с окном внедренного СОМ-объекта;
  • создавать приложение-контейнер на базе MFC и пользоваться услугами класса-оболочки для управления СОМ-объектом.
  •