Учебник по Visual C++ .Net


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


Перед тем как мы начнем работать с окном СОМ-объекта, вводя в него реакции на управляющие воздействия, покажем, как добавить страницу свойств (property page) в уже существующий блок страниц объекта, который активизируется с помощью контекстного меню. Страница свойств является отдельным элементом управления, называемым Property Page, интерфейсы которого должны быть реализованы в рамках отдельного ко-класса. Такая структура позволяет нескольким ко-классам одновременно пользоваться страницами свойств, размещенными в общем СОМ DLL-сервере. Новый класс для поддержки страницы свойств помещается в сервер с помощью той же процедуры, которую мы использовали при вставке класса COpenGL, но при этом следует выбрать другой тип элемента управления. Вновь воспользуемся услугами мастера Studio.Net ATL Add Class.

  • Установите фокус на элемент ATLGL в дереве Solution Explorer и в контекстном меню выберите команду Add > Add Class, при этом важно, чтобы фокус стоял на имени проекта ATLGL

  • В окне диалога Add Class выберите категорию ATL, шаблон ATL Property Page и нажмите кнопку Open.

  • В окне мастера ATL Property Page выберите вкладку Names и в поле Short Name введите PropDlg.

  • Перейдите на вкладку Attributes и просмотрите допустимые установки, ничего в них не меняя.

  • Перейдите на вкладку Strings и в поле Title введите имя страницы Light, которое будет обозначено на вкладке (page tab). В поле Doc String введите строку Graphics Properties.

  • Нажмите кнопку Finish.

    Просмотрите результаты. Прежде всего убедитесь, что в проекте появился новый класс CPropDlg, который поддерживает функциональность страницы свойств и окна диалога. Однако, запустив сервер и вызвав из контекстного меню его свойства, вы не увидите новой страницы. Там будут только те две страницы, которые были и до момента, как вы подключили поддержку страницы свойств. Для того чтобы новая страница действительно попала в блок страниц элемента, надо ввести новый элемент в карту свойств разрабатываемого элемента COpenGL. Откройте файл OpenGL.h и найдите в нем карту свойств. Она начинается строкой:


    BEGIN_PROP_MAP(COpenGL)

    Введите в нее новый элемент:

    PROP_ENTRY("Свет", 1, CLSID_PropDlg)

    который привязывает (binds) новую страницу к существующему блоку страниц свойств. Как видите, страница создается и связывается с объектом COpenGL по правилам СОМ, то есть с помощью уникального идентификатора ко-класса CLSlD_PropDlg. Единица определяет индекс DISPID (dispatch identifier) — 32-битный идентификатор, который используется упоминавшейся выше функцией invoke для идентификации методов, свойств и аргументов. Карта свойств теперь должна выглядеть следующим образом:



    BEGIN_PROP_MAP(COpenGL)

    PROP_DATA_ENTRY("_cx", m_sizeExtent.ex, VT_UI4)

    PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)

    PROP_ENTRY("FillColor", DISPID_FILLCOLOR, CLSID_StockColorPage)

    PROP_ENTRY("CBeT", 1, CLSID_PropDlg) END_PROP_MAP()

    Здесь важно уяснить, что каждая строка типа PROP_ENTRY соответствует какой-то функциональности, скрытой в каркасе сервера. Например, стандартное свойство Fill Color реализовано с помощью одной переменной m_clrFillColor и пары функций FillColor, упоминания о которых вы видели в IDL-файле. Тела этих функций остались за кулисами. То же справедливо относительно страницы свойств.

    Важным моментом является появление нового ко-класса в составе библиотеки типов, генерируемой DLL-сервером. В коде, приведенном ниже, отметьте появление строк, связанных с ко-классом PropDlg и, конечно, не обращайте внимание на идентификаторы CLSID, которые могут не совпадать даже с предыдущей версией в этой книге, так как в процессе разработки сервера мне приходится неоднократно повторять заново процесс создания ко-классов:

    Каждый раз при этом идентификаторы CLSID обновляются, и ваш реестр распухает еще больше. Хорошим правилом для запоминания в этом случае является следующее. Убирайте регистрацию всего сервера каждый раз, когда вы целиком убираете какой-либо неудачный ко-класс. Это, как мы отмечали, делается с помощью команды Start > Run > regsvr32 -u "C:\My Projects\ATLGL\ Debug\ATLGL.dll.". Перед тем как нажать кнопку ОК, внимательно проверьте правильность файлового пути к вашему серверу.



    library ATLGLLib

    {

    importlib("stdole32.tlb");

    importlib("stdole2.tlb") ;

    [

    uuid(6DEBB446-C43A-4AB5-BEEl-110510C7AC89)

    helpstring("_IOpenGLEvents Interface")

    ]

    dispinterface _IOpenGLEvents

    {

    properties:

    methods:

    };

    [

    uuid(5B3EF182-CD91-426F-9309-2E4869C353DB),

    helpstringC'OpenGL Class")

    ]

    coclass COpenGL

    {

    [default] interface IQpenGL;

    [default, source] dispinterface _IOpenGLEvents;

    };

    //====== Новые элементы в библиотеке типов сервера

    [

    uuid(3AE16CD6-4558-460F-8A7E-5AB83D40DE9A),

    helpstring("_IGraphPropEvents Interface")

    ]

    dispinterface _IGraphPropEvents

    {

    properties:

    methods:

    };

    [

    uuid(lAOC756A-DA17-4630-91BO-72722950B8F7) ,

    helpstring("GraphProp Class")

    ]

    coclass PropDlg

    {

    interface lUnknown;

    [default, source] dispinterface _IGraphPropEvents;

    };

    Убедитесь, что в составе проекта появились новые файлы (PropDlg. h, PropDlg. cpp и PropDlg. rgs). Откройте первый файл описаний и отметьте, что класс CPropDlg происходит от четырех родителей (классов ATL и одного интерфейса). Два из них (ccomObjectRootEx и CGomCoClass) мы уже встречали ранее, а два других (iPropertyPagelmpl и CDialoglmpl), как нетрудно догадаться, поддерживают функциональность диалоговой вкладки (страницы), размещаемой в блоке страниц (property sheet), и самого диалога, то есть механизм обмена данными. Оба родителя являются шаблонами, которые уже настроены на наш конкретный класс CPropDlg. Конструктор класса:

    CPropDlg()

    {

    m_dwTitleID = IDSJTITLEPropDlg;

    m_dwHelpFileID = IDS_HELPFILEPropDlg;

    m_dwDocStringID = IDS_DOCSTRINGPropDlg;

    }

    устанавливает унаследованные переменные m_dwTitleio и идентификаторы строковых ресурсов в те значения, которые им присвоил мастер Studio.Net. Сами строки вы можете увидеть в ресурсах, если откроете узел дерева String Table. В классе изначально присутствует реакция на кнопку Apply, которая, как вы знаете, всегда сопровождает блок диалоговых вкладок (property sheet):



    //====== Реакция на нажатие кнопки Apply

    STDMETHOD(Apply)(void)

    {

    ATLTRACE(_T("CPropDlg::Apply\n"));

    for (UINT i = 0; i < m_nObjects; i++)

    {

    // Do something interesting here

    // ICircCtl* pCirc;

    //m_ppUnk[i]->QueryInterface(IID_ICircCtl, (void**)SpCirc)

    // pCirc->put_Caption(CComBSTR("smth special"));

    // pCirc->Release();

    }

    m_bDirty = FALSE;

    return S__OK;

    }

    В комментарий мастер поместил подсказку, которая дает намек о том, как следует пользоваться новым классом. Как вы видите, общение между двумя классами нашего сервера (copenGL и CPropDlg) должно происходить по правилам СОМ, то есть с помощью указателя на интерфейс. Этот факт производит впечатление излишней усложненности. Если оба класса расположены в рамках одной DLL, они могли бы общаться друг с другом с помощью прямого указателя, несмотря на то, что сама DLL загружается в пространство чужого процесса.

    Имя ICircCtl, которое присутствует в подсказке, не имеет отношения к нашему проекту. Оно связано с учебным примером по созданию элементов управления с помощью библиотеки ATL. Вы можете увидеть этот пример в MSDN (Visual C++ Tutorials > Creating the Circle Control).

    Переменная m_bDirty используется каркасом в качестве флага доступности кнопки Apply. Если m_bDirt у == FALSE; то кнопка недоступна. Она тотчас же должна стать доступной, если пользователь страницы диалога свойств введет изменения в органы управления на лице диалога. Конечно, этим состоянием управляет разработчик, то есть мы с вами.


    Содержание раздела