Сообщений 0    Оценка 95        Оценить  
Система Orphus

Страницы свойств COM-объектов

Автор: Удалов В.А.
Опубликовано: 24.01.2004
Исправлено: 10.12.2016
Версия текста: 1.0

Введение
Страница свойств в объектной модели Windows
Отображение Property Pages с использованием функции OleCreatePropertyFrame
Отображение Property Pages с использованием стандартного элемента управления Property Sheet
Реализация
Заключение

Демонстрационный проект

Введение

В системах семейства Windows широко распространен пользовательский интерфейс на базе так называемых страниц свойств (Property Pages). Страницы свойств в виде закладок отображаются в специальном диалоговом окне, который называется "Панель свойств" (Property Sheet). Такие диалоги используются в Windows довольно часто от настроек шрифта и цвета, до управления безопасностью и параметрами системы. Property Page может использоваться не только как отдельный элемент управления. В объектной модели Windows она предоставляет собой графический интерфейс для просмотра и редактирования свойств некоторого COM-объекта. Об этой возможности использования страниц свойств пойдет речь в данной статье.

Страница свойств в объектной модели Windows

В понятиях COM Property Page не связана жестко с каким-либо объектом, более того, она сама является COM-объектом, со своим собственным идентификатором класса (CLSID). Такая независимость дает возможность разработчику использовать повторно одну и ту же страницу свойств. Property Page отличает от других объектов реализованный в ней интерфейс IPropertyPage, через методы которого можно управлять окном диалога страницы. Объект предоставляет свои страницы свойств через интерфейс ISpecifyPropertyPages. Единственный метод этого интерфейса - GetPages, он возвращает указатель на массив из CLSID страниц свойств.

Отображение Property Pages с использованием функции OleCreatePropertyFrame

Самый простой способ показать страницы свойств некоторого объекта - использовать функцию API OleCreatePropertyFrame. Функция создает модальное диалоговое окно в виде Property Sheet, выход из функции происходит с закрытием этого окна.

STDAPI OleCreatePropertyFrame(
				HWND hwndOwner,
				UINT x,
				UINT y,
				LPCOLESTR lpszCaption,
				ULONG cObjects,
				LPUNKNOWN FAR* lplpUnk,
				ULONG cPages,
				LPCLSID lpPageClsID,
				LCID lcid,
				DWORD dwReserved,
				LPVOID lpvReserved);

Два последних параметра зарезервированы и их будущее назначение не известно.

Для примера приведем код, который показывает все страницы свойств объекта pUnk.

HRESULT ShowPP(IUnknown* pUnk, HWND hwndOwner)
{
	if (!pUnk)
		return E_INVALIDARG;

	_bstr_t bstrObjectName;
	bstrObjectName = "Object";

	//запрашиваем интерфейс ISpecifyPropertyPages
	CComQIPtr<ISpecifyPropertyPages> spSpec(pUnk);
	if (spSpec==NULL)
		return E_FAIL;

	//получаем массив идентификаторов классов страниц свойств
	CAUUID cauuid = {0};
	HRESULT hr = spSpec->GetPages(&cauuid);
	if (FAILED(hr))
		return hr;

	LCID lcid = ::GetSystemDefaultLCID();
	
	if (cauuid.cElems != 0)
	{
		hr = OleCreatePropertyFrame(
				hwndOwner
				0,
				0,
				bstrObjectName.copy(),
				1,
				&pUnk,
				cauuid.cElems,
				cauuid.pElems,
				lcid,
				0,
				NULL
				);
		CoTaskMemFree(cauuid.pElems);
	}
	return hr;
}

Использование OleCreatePropertyFrame имеет существенные минусы. Нет возможности задать координаты окна диалога по собственному усмотрению, оно всегда отображается в каскадном порядке после окна родителя (если оно задано), нельзя получить HWND диалога и идентификатор кнопки, которую нажал пользователь. Кроме этого функция всегда создает модальный диалог и его нельзя разместить внутри другого окна. Все эти недостатки можно устранить, используя для создания страниц свойств элемент управления Property Sheet.

Отображение Property Pages с использованием стандартного элемента управления Property Sheet

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


Рисунок 1.Алгоритм отображения страниц свойств

Через интерфейс ISpecifyPropertyPages у объекта запрашиваются CLSID его станиц свойств и передаются вместе с указателем на IUnknown объекта функции OleCreatePropertyFrame. После этого функция создает объекты страниц свойств и размещает их на закладках в модальном диалоге. Для инициализации и показа страницы OleCreatePropertyFrame вызывает у нее методы интерфейса IPropertyPage:

Рассмотрим подробней метод SetPageSite. Для осуществления обратной связи между страницей свойств и создавшим ее объектом (site object) используется интерфейс IPropertyPageSite, который реализует OleCreatePropertyFrame и передает указатель на него странице свойств. Этот интерфейс имеет 4 метода:

Если пользователь нажимает Ok или Apply, OleCreatePropertyFrame для каждой страницы свойств вызывает метод Apply интерфейса IPropertyPage, а перед уничтожением окна диалога - методы Deactivate и SetObject с параметром NULL, который говорит странице свойств, что надо "отпустить" site object.

Чтобы отобразить страницы свойств некоторого объекта с использованием Property Sheet необходимо реализовать тот же алгоритм, что и OleCreatePropertyFrame. Для этого нужно создать окно-фрейм (определяется структурой PROPSHEETHEADER) и добавить в него страницы свойств (структура PROPSHEETPAGE), после чего разместить на этих страницах страницы свойств самого объекта. Основные принципы создания элементов управления на базе Property Sheet подробно изложены в MSDN. Страница свойств, созданная на основе структуры PROPSHEETPAGE, представляет собой посредника между фреймом и действительной страницей свойств объекта. Она получает сообщения от фрейма и передает их странице свойств через интерфейс IPropertyPage.

Реализация

В демонстрационном примере содержатся 3 проекта. Основной из них - COM-объект реализующий оба способа отображения страниц свойств: метод ShowPropertyPageOLE использует функцию OleCreatePropertyFrame, ShowPropertyPageSheet - создает страницы свойств внутри родительского окна, используя возможности Property Sheet. Остальные два проекта - это объект со страницами свойств и тестовое диалоговое приложение.

Для отображения страниц свойств на базе Property Sheet в проекте используются два класса: CMyPropertySheet и CMyPropertyPage. Классам соответствуют структуры PROPSHEETHEADER и PROPSHEETPAGE соответственно, которые инициализируются в конструкторах. Основной метод CMyPropertySheet - метод Create. Он создает массив экземпляров класса CMyPropertyPage, заполняет структуру PROPSHEETHEADER, после чего вызывает API-функцию PropertySheet, которая создает окно диалога и размещает на нем закладки страниц свойств.

      for (UINT i = 0; i < m_pageCLSIDs.cElems; i++)
	{		
           CMyPropertyPage* pPage = new CMyPropertyPage(m_pageCLSIDs.pElems[i], m_pObject, pPPSite);

 		//добавляем экземпляры классов и дескрипторы страниц свойств 
 		//соответствующие массивы
		m_pages.push_back(pPage);
		m_pageHeaders.Add(pPage->m_hPP);
	}

	if (m_pages.size() > 0)
	{
		m_psh.dwFlags	    = PSH_NOAPPLYNOW | PSH_USECALLBACK | PSH_MODELESS;
		m_psh.hwndParent	= hwndParent;
		m_psh.phpage		= (HPROPSHEETPAGE*)m_pageHeaders.GetData();
		m_psh.nPages		= m_pageHeaders.GetSize();
		m_psh.pfnCallback	= CMyPropertySheet::PropSheetCallback;

		//подготавливаем класс к созданию окна
		_Module.AddCreateWndData(&m_thunk.cd, this);
		
		HWND hWnd = (HWND)::PropertySheet(&m_psh);
 	}

Получив сообщение WM_INITDIALOG, экземпляр класса CMyPropertyPage вызывает методы Activate и Show у соответствующей ему страницы свойств:

LRESULT CMyPropertyPage::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	HRESULT hRes;
	_ASSERT(m_pPP);
	if(m_pPP==NULL)
		return 0;
	RECT rc;
	GetWindowRect(&rc);
	ScreenToClient(&rc);
	hRes = m_pPP->Activate( m_hWnd, &rc , TRUE );
	if( FAILED( hRes ) )
		return E_FAIL;
	hRes = m_pPP->Show( SW_SHOW );
	if( FAILED( hRes ) )
		return E_FAIL;
	return S_OK;
}


При уничтожении окна экземпляром класса CMyPropertyPage вызываются методы SetPageSite с параметром NULL и Deactivate.

В тестовом проекте кроме диалогового приложения реализован COM-объект, поддерживающий интерфейс IPropertyPageSite. Это позволяет тестовому приложению установить связь со страницами свойств.

Заключение

OleCreatePropertyFrame своей функциональностью сильно уступает использованию Property Sheets, но ее вполне бывает достаточно, когда требуется просто показать диалог страниц свойств. Реализация на базе Property Sheet немного сложна, но в итоге можно получить довольно гибкое и удобное средство манипулирования страницами свойств, при этом под контролем разработчика оказываются практически все процессы, связанные с созданием и отображением диалогов.


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 0    Оценка 95        Оценить