Глава 15.


Наборы свойств

 

Набор свойств представляет собой особый вид диалогового окна, применяемый для модификации свойств как самого приложения, так и каких-то внешних объектов. Набор свойств содержит три основные части:

Рис. 15.1. Окно Options оболочки Visual C++ 6.0

В качестве примера набора свойств можно привести блок диалога просмотра и изменения параметров оболочки Visual C++ 6.0 (рис. 15.1).

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

Наборы свойств могут применяться не только для отображения свойств объектов, но и для группирования сходных по назначению или по используемым параметрам действий. Например, приложение MS Word 97 таким образом группирует действия для поиска, замены и перехода (рис. 15.2).

Рис. 15.2. Окно "Найти и заменить" приложения MS Word 97

 

Создание набора свойств

Библиотека MFC предоставляет специальные классы для создания набора свойств и обеспечения работы с ним: CPropertySheet и СPropertySheetEx позволяют создать набор свойств, a CPropertyPage и CPropertyPageEx предназначены для создания включаемых в набор страниц свойств. Несколько особняком стоит класс CTabCtrl, используемый для создания вкладок и работы с ними. Дело 'В том, что в общем случае нет необходимости напрямую работать с вкладками в наборе свойств, достаточно лишь задать текст, который нужно поместить на них, а все остальное сделает библиотека. Однако возможность доступа к объектам данного класса все же имеется, и его функции применяются в основном для создания вкладок, отличающихся от создаваемых по умолчанию.

 Примечание 

Классы CPropertySheetEx и CPropertyPageEx доступны только в Visual C++ 6.0.

Проще всего создать набор свойств, воспользовавшись средством добавления в проект различных программных компонентов. Для этого выберите команду Insert\Component, а затем вставьте в проект набор свойств с требуемыми характеристиками (рис. 15.3).

Рис. 15.3. Окно выбора добавляемых в проект программных компонентов

При этом требуется проделать следующее:

Если впоследствии понадобится добавить новые страницы свойств, то следует воспользоваться механизмом, изложенным ниже.

 

Создание объекта "набор свойств"

Следующим шагом будет создание объекта класса СPropertySheet в тексте программы. Обычно такой объект создается в обработчике команды, осуществляющей вывод на экран набора свойств.

CPropertySheet propsheet;

CPropertySheet propsheet("Параметры", pParentWnd);

ИЛИ

CPropertySheet propsheet("Параметры ", GetParent());

Конструктор класса имеет несколько реализаций:

CPropertySheet::CPropertySheet()

 CPropertySheet::CPropertySheet (

UINT nIDCaption,

CWnd* pParentWnci = NULL,

UINT iSelectPage = 0) 

CPropertySheet::CPropertySheet (

LPCTSTR pszCaption,

CWnd* pParentWnd = NULL,

UINT iSelectPage =0)

Параметр nIDCaptbn — идентификатор заголовка для набора свойств; pParentWnd— указатель на родительское окно. Если его значение равно NULL, то родительским окном является главное окно приложения. Параметр iSelectPage— индекс страницы, выводимой первой при отображении набора свойств на экран. По умолчанию выводится первая страница набора свойств. Параметр pszCaption — указатель на строку, содержащую заголовок для набора свойств (не может быть равным NULL).

Если не нужно добавлять в набор свойств дополнительные элементы управления (например, окно предварительного просмотра) или выводить на экран немодальный набор свойств, то создавать класс, производный от класса CpropertySheet, вообще не требуется.

Примечание 

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

 

Настройка окна набора свойств

По умолчанию набор свойств выделяет окно для размещения страниц свойств с вкладками, для модальных наборов свойств в этом окне также размещаются кнопки OK, Cancel (Отмена) и Apply (Применить). Допускается добавлять и другие необходимые для работы элементы управления, например, дополнительное окно просмотра, в котором будут выводиться изображения, находящиеся в выбираемых пользователем файлах. Дополнительные элементы управления можно вводить в обработчике OnCreate, однако лучше это делать в функции OnlnitDialog, т. к. на момент вызова этой функции уже созданы окна страниц свойств, доступ к которым также может понадобиться.

Вставка дополнительных элементов управления в набор свойств обычно требует увеличения размеров его диалогового окна. Для этого после вызова функции базового класса OnCreate или OnlnitDialog необходимо вызвать функцию CWnd::GetWindowRect для получения значений размеров текущего окна набора свойств, задать требуемые размеры и вызвать функцию CWnd::MoveWindow или CWnd::SetWindowPos для изменения размеров окна, в котором выводится набор свойств.

 

Добавление страниц

Наиболее удобный способ обеспечения доступа к страницам свойств внутри набора свойств является встраивание объектов класса CPropertyPage в класс, производный от СPropertySheet. Такой подход отличается от разработки модальных наборов свойств, когда объект-владелец набора свойств создает объекты CPropertyPage, а затем передает их набору свойств, вызывая функцию CProperty'Sheet::AddPage.

На этом этапе следует сделать еще одно важное замечание: если нужно создать несколько объектов класса CPropertySheet, например, массив наборов свойств, то следует воспользоваться не конструктором СPropertySheet, а функцией Construct.

void CPropertySheet::Construct ( 

UINT nIDCaption, 

CWnd* pParentWnd = NULL, 

UINT iSelectPage =0)

ИЛИ

void CPropertySheet::Construct ( 

LPCTSTR pszCaption, 

CWnd* pParentWnd = NULL, 

UINT iSelectPage =0)

Параметры здесь те же, что и у конструктора класса CPropertySheet Данную функцию можно вызывать, если ни один из конструкторов с параметрами класса CPropertySheet еще не вызывался. В случае создания массива объектов CPropertySheet вызов функции Construct обязателен для каждого элемента массива.

Ниже приведен пример, иллюстрирующий применение этой функции:

int i;

CPropertySheet propsheetarray[3];

// Для одиночного набора свойств не требуется

// вызывать функцию Construct

CPropertySheet oneSheet;

UINT sheetsID[3] = (IDS_SHEET_1, IDS_SHEET_2, IDS_SHEET_3};

for (i = 0; i < 3; i++)

{

// Функция Construct вызывается для каждого

/./ создаваемого набора свойств

propsheetarray[i].Construct(sheetsID[i]}; 

}

Следующим важным шагом на пути создания набора свойств будет выполнение следующих операций для каждой добавляемой в набор свойств страницы:

Обычно функция, в которой создается объект CPropertySheet, на этом же шаге также создает и объекты класса CPropertyPage. Однако если создается производный от CPropertySheet класс, то можно встроить объекты класса CPropertyPage в объект класса CPropertySheet и вызвать из конструктора производного от CPropertySheet класса функцию CPropertySheet::AddPage для каждой страницы свойств. Функция AddPage добавляет страницу свойств в набор свойств, однако не создает окно для этой страницы. Следовательно, для добавления страницы можно не ждать создания окна набора свойств, а вызвать функцию AddPage непосредственно из конструктора.

// Первый способ

CPropertySheet propsheet;

CFirstPage pagel; // Класс, производный от CPropertyPage

CSecondPage page2; // Класс, производный от CPropertyPage

propsheet.AddPage(&pagel);

propsheet.AddPage(&page2);

 

// Второй способ

class CPropSheet : public CPropertySheet

{

public:

CPropSheet(CWnd* pParentWnd = NULL);

public:

CFirstPage m_Pagel; // Класс, производный от CPropertyPage

 CSecondPage m_Page2; // Класс, производный от CPropertyPage

...

};

CPropSheet::CPropSheet(CWnd* pWndParent)

: CPropertySheet(IDS_PROPSHT_CAPTION, pWndParent)

{

AddPage(&m_Pagel);

AddPage(&m_Page2); 

}

Для вывода на экран набора свойств необходимо вызвать одну из функций: CPropertySheet::DoModal или CPropertySheet::Create.

 

Создание модального окна свойств

Создание набора свойств в виде модального блока диалога осуществляется вызовом функции DoModai.

virtual int CPropertySheet::DoModal ()

Выводит на экран модальный набор свойств. Функция возвращает значения IDОК или IDCANCEL, а в случае неуспешного завершения — 0. Следует иметь в виду, что если набор свойств создан как мастер, то возвращаются другие значения - ID_WIZFINISH или IDCANCEL.

После завершения работы функции окна и все страницы, связанные с набором свойств, удаляются. Сами объекты остаются существовать. Как правило, данные от объектов CPropertyPage считываются после того, как функция DoModal вернула значение IDOK.

 

Создание немодального окна набора свойств

Обычно создаваемая страница свойств является модальной. Так же. как и модальный диалог, пользователь должен закрыть окно модального набора свойств прежде, чем он сможет работать с другими объектами данного приложения (окнами, меню и т. п.). Однако существуют случаи, когда необходимо создать немодальный набор свойств, который позволяет, не закрыв его, перейти к работе с другими объектами приложения. Немодальный набор свойств формируется вызовом функции CPropertySheet::Create.

BOOL CPropertySheet::Create

CWnd* pParentWnd = NULL, 

DWORD dwStyle = (DWORD) -1, 

DWORD dwExStyle =0)

Параметр pParentWnd указывает на родительское окно (если равен NULL, то родительским окном является главное окно приложения); dwStyle задает стили окна (по умолчанию- комбинация стилей WS_SYSMENU, WS_POPUP, WS_ CAPTION, DS_MODALFRAME, DS_CONTEXT_HELP и WS_VISIBLE); a dwExStyie-расширенные стили окна (по умолчанию — стиль WS_EX_DLGMODALFRAME).

Функцию СProperty Sheet::Create можно вызывать как из конструктора, так и после того, как объект набора свойств уже создан. Функция возвращает управление непосредственно после создания набора свойств. Для уничтожения набора свойств следует использовать функцию CWnd::.DestroyWindow.

При создании немодального набора свойств придется выполнить несколько дополнительных операций. Одной из них является обеспечение обмена данными между набором свойств и внешними объектами в то время, когда окно набора свойств открыто. Эта задача целиком совпадает с аналогичной задачей для обычных немодальных блоков диалога. В данном случае такой обмен проще реализовать, создав производный от CPropertySheet класс. Одним из методов обеспечения обмена является определение указателя из набора свойств на внешний объект. Далее следует определить функцию в производном от CPropertySheet классе, которая будет отслеживать изменения данных набора свойств всякий раз, когда фокус ввода передается от одного внешнего объекта к другому. Такая функция вызывается, естественно, не объектом набора свойств, а самим приложением и осуществляет сброс текущих данных каждой страницы для отображения свойств текущего внешнего объекта, для чего она должна иметь доступ к объектам CPropertyPage, определенным в классе CPropertySheet.

Примечание 

Если вы, как и я, воспользовались услугами Galery Components, то всю эту работу за вас проделает мастер, и полученные в последних разделах сведения понадобятся только в случае, когда вы захотите расширить возможности созданного набора свойств. 

 

Обмен данными

Кроме вывода на экран набора свойств, следует обеспечить также обмен данными между страницами свойств и объектом-владельцем набора свойств. Как и при работе со всеми блоками диалога, процесс обмена между набором свойств и приложением играет очень важную роль (что, впрочем, неудивительно, ведь набор свойств и служит для того, чтобы задавать значения или параметры, необходимые для работы приложения).

Обмен данными с набором свойств реально является обменом данными с его отдельными страницами. Процедура обмена при этом ничем не отличается от обмена в обычном блоке диалога в силу того, что объект класса CPropertyPage является просто специализированным объектом класса CDialog. При этом используются средства обмена данными диалога (Dialog Data Exchange, DDX), которые позволяют осуществлять обмен между элементами управления блока диалога и переменными объекта-владельца блока диалога.

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

 

Операция Apply

Следует остановиться еще на одной важной детали — обработке нажатия кнопки Apply (Применить). Набор свойств имеет одну важную способность, отсутствующую у обычных блоков диалога, а именно — позволяет пользователям изменять задаваемые на странице свойств параметры во внешнем объекте до того, как окно с набором свойств будет закрыто. Это осушествляется с помощью кнопки Apply, нажатие которой приводит к изменению у объекта всех параметров всех страниц (а не только текущей активной страницы) в наборе свойств. По умолчанию эта кнопка недоступна. Необходимо самостоятельно написать код программы, делающий ее доступной для использования в требуемый момент работы с набором свойств. Следует также написать код, реализующий выполнение необходимых действий при нажатии этой кнопки.

Если не требуется предоставлять пользователю описанную возможность, то в общем случае нет необходимости удалять саму кнопку. Ее можно просто оставить недоступной для того, чтобы сохранить общность с другими приложениями Windows. Чтобы объявить об изменении параметров в странице свойств и сделать кнопку Apply доступной, следует вызвать функцию CPropertyPage::SetModified со значением ее единственного параметра, равным TRUE. Если какая-либо из страниц свойств содержит изменения, которые еще не применены к объекту, то, даже если текущая страница таких свойств не содержит, кнопка Apply все равно будет доступна. Это значит, что достаточно вызвать функцию SetModified хотя бы для одной из страниц. Функцию SetModified следует вызывать всякий раз, когда пользователь внес какие-либо изменения в параметры. Одним из способов обнаружения этого действия является обработка уведомляющих сообщений от элементов управления страницы свойств, таких, например, как EN_CHANGE или BN_CLICKED.

При нажатии кнопки Apply набор свойств должен сообщить своему владельцу или какому-то другому внешнему объекту приложения о том, что необходимо модифицировать текущие параметры в наборе свойств. Одновременно кнопка становится недоступной, для чего нужно вызвать функцию SetModified со значением параметра, равным FALSE. Причем сделать это необходимо для каждой из страниц свойств, для которых функция SetModified вызывалась ранее со значением параметра, равным TRUE, иначе будет считаться, что операция по обновлению не была совершена. Обратите внимание, что только Windows хранит информацию о состоянии страницы. Так что при небольшом количестве страниц свойств можно смело вызвать функцию SetModified со значением параметра, равным FALSE, для всех страниц набора свойств, чтобы гарантировать результат.

Обработчик команды Apply описывается следующим кодом:

BEGIN_MESSAGE_MAP(CPropSheet, CPropertySheet)

//{{AFX_MSG_MAP(CPropPSheet)

ON_COMMAND(ID_APPLY_NOW, OnApplyNow)

//}}AFX_MSG_MAP 

END_MESSAGE_MAP() 

void CPropSheet::OnApplyNow()

{

Default!);

...

m_Pagel.SetModifled(FALSE); 

m_Page2.SetModifled(FALSE);

}

Запретить отображение кнопки Apply очень просто:

void [11]CMainFrame::OnProperties()

 {

// Создаем объект класса

CMyPropertySheet propSheet;

// Удаляем кнопку Apply

propSheet.m_psh.dwFlags |= PSH_NOAPPLYNOW;

// Выводим набор свойств на экран

propSheet.DoModal();

}

Далее мы приводим очень краткий обзор классов CPropertySheet и СProperty Page, используемых для создания набора свойств, а также новых — CPropertySheetEx и CPropertyPageEx, реализующих некоторые дополнительные возможности.

 

Операции над набором свойств

Несомненный интерес представляют функции, которые предназначены для выполнения операций над набором свойств.

Для добавления страниц в набор свойств служит функция, о которой уже не раз упоминалось выше:

void CPropertySheet::AddPage (CPropertyPage* pPage)

Параметр рРаде — указатель на добавляемую страницу (не может быть равным NULL). Функция добавляет страницу в набор свойств, располагая ее вкладку справа от последней из уже имеющихся. Страницы в наборе свойств будут располагаться в том же порядке, в каком" они добавлялись в него. Функция добавляет объект класса CPropertyPage в список страниц свойств класса CPropertySheet, но не создает окно для его размещения. Библиотека откладывает создание окна для страницы до того момента, пока пользователь не выберет его.

Если функция вызывается после вывода набора свойств на экран, то в строку вкладок будет добавлена вкладка новой страницы.

Чтобы удалить страницу из набора свойств, следует воспользоваться одним из вариантов следующей функции:

void CPropertySheet::RemovePage (CPropertyPage* pPage)

ИЛИ

void CPropertySheet::RemovePage (int nPage)

Параметр рРаде в первой версии — указатель на удаляемую страницу (не может быть равен NULL), а во второй — индекс страницы, которая должна быть удалена (должен лежать в пределах от 0 до значения, на единицу меньшего числа страниц в наборе свойств).

Функция удаляет страницу из набора свойств и уничтожает ассоциированное с ней окно. Сам объект CPropertyPage не удаляется до того момента, пока владелец окна CPropertySheet не закроет его.

В ходе работы с набором свойств может появиться необходимость определить какие-либо из его атрибутов или изменить их. Для этого в классе CPropertySheet реализованы следующие функции: GetActiveIndex, GetPage, GetPagelndex, GetPageCount, GetActive Page, SetActivePage, SetTitle.

Следует отдельно отметить функцию, позволяющую получить доступ к вкладкам набора свойств:

CTabCtrl* CPropertySheet::GetTabControl ()

Возвращает указатель на объект класса СТаЬСМ.

Вкладки входят в набор свойств неявно и в общем случае нет необходимости работать с ними отдельно, достаточно лишь при создании страницы свойств задать текст, который должен будет появиться на вкладке страницы — все остальное за вас сделает библиотека. Но может возникнуть ситуация, когда понадобится произвести некоторые дополнительные действия с вкладками набора свойств, например, поместить на вкладки битовые изображения. Тогда можно воспользоваться данной функцией, чтобы затем с помощью функций класса СТаЬСМ выполнить необходимые операции.

Для завершения работы набора свойств служит функция:

void CPropertySheet::EndDialog (int nEndID)

Параметр nEndID задает значение, которое возвращает как результат своей работы функция DoModal.

Функция EndDialog вызывается библиотекой, когда окно набора свойств закрывается нажатием кнопок OK, Cancel или иным способом — нажатием клавиши <Esc> или нажатием кнопки закрытия окна в правой части заголовка, а также в случае, если будет нажата кнопка Close (Закрыть). Самим эту функцию можно вызывать в том случае, когда происходит событие, требующее закрытия набора свойств.

 

Изменение параметров отображения

В заключение описания класса CPropertySheet приведем пример изменения стилей и параметров отображения набора свойств. На рис. 15.4 представлен набор свойств с установленными по умолчанию параметрами вывода.

Для изменения некоторых флагов (параметров вывода) следует изменить конструктор класса, базирующегося на классе CPropertySheet. Если набор свойств был добавлен в качестве программного компонента, то такой класс будет создан в обязательном порядке. В противном случае аналогичные действия следует провести сразу же после создания объекта в набор свойств, перед вызовом функции CPropertySheet::DoModal или CPropertySheet::Create.

Рис. 15.4. Набор свойств со значениями параметров вывода, заданными по умолчанию

Вот как можно, например, удалить кнопку Apply из окна набора свойств:

CMyPropertySheet::CMyPropertySheet(CWnd* pWndParent)

: PropertySheet(IDS_PROPSHT_CAPTION, pWndParent)

 {

// Удаляем кнопку Apply

m_psh.dwFlags ]= PSH_NQAPPLYNOW;

}

Как видите, для настройки набора свойств используется компонент класса m_psh:

PROPSHEETHEADER CPropertySheet:;m_psh

В структуре PROPSHEETHEADER хранятся основные параметры набора свойств. Ее можно использовать для изменения этих параметров уже после создания набора, но до того, как он будет выведен на экран вызовом функции DoModal.

typedef struct _PROPSHEETHEADER { 

DWORD dwSize;

DWORD dwFlags; 

HWND hwndParent; 

HINSTANCE hlnstance;

 union 

{

HICON hIcon;

LPCTSTR pszIcon;

};

LPCTSTR pszCaption; 

UINT nPages 

union {

UINT nStartPage

LPCTSTR pStartPage;

};

union {

LPCPROPSHEETPAGE ppsp;

HPROPSHEETPAGE FAR *phpage; 

};

PFNPROPSHEETCALLBACK pfnCallback; 

#if (_WIN32_IE >= 0x0400)

 union {

HBITMAP hbmWatermark;

LPCTSTR pszbmWatermark; };

HPALETTE hplWatermark; 

union {

HBITMAP hbmHeader;

LPCSTR pszbmHeader; 

};

#endif 

} PROPSHEETHEADER

Поле dwSize— размер структуры в байтах; dwFlags— массив флагов, указывающих, какие переменные данной структуры должны быть использованы, а какие проигнорированы. Может быть комбинацией следующих значений:

PSH_DEFAULT

 Использовать значения по умолчанию для всех переменных структуры

PSH_HASHELP 

Выводить кнопку Help (Справка). Эта кнопка будет доступна только в том случае, если флаг PSP_HASHELP также установлен и в структуре PROPSHEETPAGE для текущей страницы свойств. Если флаг PSP_HASHELP установлен для какой-либо из страниц в наборе свойств, то кнопка Help появится автоматически независимо от того, установлен или нет флаг PSH_HASHELP. Однако флаг PSH_HASHELP полезен тогда, когда ни одна из страниц не имеет его установленным, но потом могут быть добавлены страницы свойств, содержащие кнопку Help

PSH_HEADER 

Сообщает, что для заголовка используется специальный битовый массив, который задается в поле pszbmHeader, если установлен флаг PSH_USEHBMHEADER, или в поле hbmHeader, если этот флаг не установлен. Флаг PSH_HEADER игнорируется, если одновременно не установлен флаг PSH_ WIZARD97

PSH_MODELESS

 Сообщает функции ::PropertySheet, что набор свойств необходимо создать как немодальный блок диалога (по умолчанию создается модальный). Если установлен этот флаг, то функция ::PropertySheet немедленно после создания диалога возвращает дескриптор набора свойств. (При использовании класса CPropertySheet манипуляции флагом осуществляются функциями класса.)

PSH_NOAPPLYNOW 

Удаляет кнопку Apply

PSH_PROPSHEETPAGE 

Сообщает о необходимости использовать поле ppsp структуры и игнорировать phpage

PSH_PROPTITLE 

Добавляет слева строку "Properties for" (в русской версии Windows — "Свойства:") к строке, заданной в поле pszCaption

PSH_STRETCHWATERMARK

 Растягивает битовый массив, чтобы заполнить фон набора свойств вместо перекрытия "водяных знаков" и битового массива заголовка. Этот флаг игнорируется, если не установлены флаги PSH_WIZARD97 и PSH_WATERMARK или флаг PSH_HEADER

PSH_USECALLBACK 

Сообщает о необходимости вызывать функцию, заданную в поле pfnCallback, при инициализации набора свойств, определяемого данной структурой

PSH_USEHBMHEADER

Сообщает, что для задания битового массива заголовка используется поле hbmHeader. Если флаг не установлен, то информация находится в поле pszbmHeader. Этот флаг игнорируется, если не установлены флаги PSH_WIZARD97 и PSH_HEADER

PSH_USEHBMWATERMARK 

Сообщает, что для задания битового массива фона используется поле hbmWatemnark. Если флаг не установлен, то информация находится в поле pszbmWatermark. Этот флаг игнорируется, если не установлены флаги PSH_WIZARD97 и PSH_WATERMARK

PSH_USEHICON 

Сообщает о необходимости использовать пиктограмму, заданную дескриптором в поле hIcon, в заголовке набора свойств

PSH_USEHPLWATERMARK 

Для рисования битовых массивов фона и/или заголовка используется палитра, заданная в поле hplWateimark. Этот флаг игнорируется, если не установлены флаги PSH_WIZARD97 и PSH_WATERMARK, или флаг PSH_HEADER

PSH_USEICONID 

Сообщает о необходимости использовать значок, заданный именем в поле pszlcon, в заголовке набора свойств

PSH_USEPAGELANG 

Устанавливает, что язык для набора свойств берется из ресурсов первой страницы

PSH_USEPSTARTPAGE 

Сообщает о необходимости использования поля pStartPage и игнорирования поля nStartPage для определения начальной страницы набора свойств

PSH_WATERMARK 

Сообщает, что для фона используется битовый массив, который в зависимости от значения флага PSH_USEHBMWATERMARK берется либо из поля pszbmWatermark, либо из поля hbmWatermark. Этот флаг игнорируется, если не установлен флаг PSH_ WIZARD97

PSH_WIZARD 

Создается мастер PSH_WIZARD97 Создается набор свойств мастера с возможностью вывода в фоне и/или в заголовке битовых массивов

PSH_WIZARDCONTEXTHELP 

В набор свойств мастера добавлено системное меню. Этот флаг игнорируется, если не установлен флаг PSH_WIZARD или >, PSH_WIZARD97

PSH_WIZARDHASFINISH 

В окне мастера отображается кнопка Finish (Готово). Этот флаг игнорируется, если не установлен флаг PSH_WIZARD или PSH_ WIZARD97

PSH_RTLREADING

 Задает вывод заголовка набора свойств для чтения справа налево (для арабского языка или иврита)

 Примечание 

Все флаги, которые требуют наличия установленного флага PSH_ WIZARD97, не поддерживаются в классе CPropertySheet. Для работы с этим стилем в библиотеке реализован новый класс — CPropertySheetEx.

Поле hwndParent— дескриптор родительского окна; hlnstance— дескриптор объекта, чья пиктограмма или заголовок должны быть загружены (если заданы pszlcon или pszCaption, то данный параметр должен быть заполнен); hlcon— дескриптор значка, помещаемого в заголовке набора свойств (если dwFlags не содержит значения PSH_USEHICON, то данный параметр игнорируется). Поле pszlcon предназначено для задания значка, который должен быть загружен и помещен в заголовке набора свойств, может содержать идентификатор значка или указатель на строку, задающую ее имя (если в dwFlags не задано значение PSH_USEHICONID, то данный параметр будет проигнорирован). Поле pszCaption задает заголовок набора свЬйств, может содержать идентификатор строкового ресурса или указатель на строку, содержащую заголовок. Если в dwFlags задано значение PSH_PROPTITLE, то к заголовку будет добавлена слева строка

"Properties for" (в русской версии Windows— "Свойства:"). Поле nPages— число элементов в массиве phpage. Поле nStartPage задает индекс, а поле pStertPage — имя страницы, которая появится на экране первой при создании набора свойств (параметр должен содержать идентификатор строкового ресурса или указатель на строку, содержащую имя). Поле ppsp— указатель на массив структур PROPSHEETPAGE, определяющих страницы набора свойств. Если в dwFlags не задано значение PSH_PROPSHEETPAGE, то данный параметр будет проигнорирован. Поле phpage — указатель на массив, содержащий дескрипторы страниц набора свойств. Каждый дескриптор должен быть создан предшествующим вызовом функции ::CreatePropertySheetPage (если в dwFlags не задано значение PSH_PROPSHEETPAGE, то данный параметр будет проигнорирован). Поле pfnCallback— указатель на функцию приложения, которая вызывается при инициализации набора свойств (если в dwFlags не задано значение PSH_ USECALLBACK, то данный параметр будет проигнорирован); hbmWatermark и pszbmWatermark — дескриптор или указатель на текстовую строку ресурса битового массива, используемого для фона набора свойств (что именно, задается флагом PSH_USEHBMWATERMARK); hplWatermark — дескриптор палитры, используемой для вывода битовых массивов (если в dwFlags не задано значение PSH_USEHBMHEADER, то данный параметр будет проигнорирован); hbmHeader и pszbmHeader— дескриптор или указатель на текстовую строку ресурса битового массива, используемого для заголовка набора свойств (что именно, задается флагом PSH_USEHBMHEADER).

Для изменения способа отображения вкладок следует использовать функцию OnlnitDialog.

BOOL CMyPropertySheet::OnlnitDialog()

{

BOOL bResult = CPropertySheet::OnlnitDialog();

 CWnd::ModifyStyle(GetTabControl()->m_hWnd,

TCS_MULTILINE, TCS_SINGLELINE, 0); 

return bResult; 

}

Как видите, в данном случае не обойтись без создания своего собственного класса. Однако есть более правильный способ (введенный в последней версии библиотеки MFC), при котором изменение стиля вывода вкладок осуществляется кодом библиотеки.

void CPropertySheet::EnableStackedTabs(BOOL bStacked)

Позволяет изменить стиль вывода вкладок набора свойств. Если параметр bStacked равен FALSE, то вкладки располагаются в несколько рядов, а если TRUE, то в одну строку с возможностью их прокрутки.

Эту функцию следует вызывать при создании набора свойств, но до вывода его на экран.

BOOL CMyPropertySheet::OnlnitDialog()

{

// Устанавливаем стиль вывода вкладок в одну линию

EnableStackedTabs(TRUE);

BOOL bResult = CPropertySheet::OnInitDialog(); 

return bResult; 

}

Результат использования функции EnableStackedTabs приведен на рис. 15.5.

Рис. 15.5. Набор свойств после изменения некоторых значений параметров вывода

В последней версии библиотеки MFC добавлен новый класс набора свойств CPropertySheetEx, который предоставляет поддержку для расширенной структуры PROPSHEETHEADER, введенной в Windows 98 и Windows NT Эта структура была описана раньше, и вы, конечно же, обратили внимание на дополнительные поля и соответствующие флаги для поддержки битового массива для отображения фона.

Вторым классом, используемым для создания включаемых в набор свойств страниц, является класс СPropertyPage.

 

Настройка страниц набора свойств

Выше мы говорили, что объекты класса CPropertyPage представляют собой отдельные страницы, входящие в набор свойств. Прежде всего должен быть создан сам объект CPropertySheet, а затем необходимо создать объекты класса С PropertyPage для каждой вставляемой в набор свойств страницы и добавить созданные страницы в набор свойств функцией СPropertySheet::AddPage Теперь можно вывести набор свойств на экран с помощью функций LPropertySheet::DoModal - для модального или CPropertySheet::Create - для немодального набора свойств.

Как и в случае класса CPropertySheet, для знакомства с возможными параметрами страницы свойств сначала рассмотрим переменную, в которой хранятся основные параметры страницы:

PROPSHEETPAGE CPropertyPage::m_psp

Эту структуру можно использовать для изменения параметров страницы уже после ее создания, но до того, как она выведена на экран

 

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

Класс CPropertyPage имеет три версии конструктора:

CPropertyPage::CPropertyPage (),

CPropertyPage::CPropertyPage (

UINT nIDTemplate,

UINT nIDCaption = 0) 

И

CPropertyPage::CPropertyPage (

LPCTSTR IpszTemplateName,

UINT nIDCaption = 0)

Параметр nIDTemplate — идентификатор шаблона создаваемой страницы; nIDCaption — идентификатор строки, которая будет помещена на вкладку данной страницы (если параметр задан равным 0, то имя будет взято из шаблона диалога для данной страницы); IpszTemplateName— указатель на строку, содержащую имя шаблона диалога для данной страницы (не может быть равным NULL).

В случае, если создается массив страниц свойств, вместо конструктора используется функция Construct.

 

Изменение состояния

Выше уже описывалась обработка нажатия кнопки Apply (Применить) в наборе свойств. Для этого предназначена следующая пара функций:

void CPropertyPage::SetModifled (BOOL bChanged = TRUE)

Делает доступной или недоступной кнопку Apply набора свойств в зависимости от того, должны ли быть применимы параметры, задаваемые в странице свойств, к внешнему объекту. Параметр bChanged может иметь значение TRUE, если с момента последнего применения измененных параметров страницы свойств пользователь внес новые изменения, и FALSE, если значения, заданные в странице свойств, уже применены к внешнему объекту или они должны быть проигнорированы.

Кнопка Apply станет доступной, если вызвана функция SetModified со значением параметра, равным TRUE. При значении FALSE кнопка станет недоступна только в том случае, если все другие страницы набора свойств не содержат измененных и не модифицированных у объекта параметров.

 

Переопределяемые функции

Если страница выбрана пользователем и становится активной (получает фокус ввода), то библиотека вызывает функцию OnSetActive:

virtual BOOL CPropertyPage::OnSetActive ()

Реализация по умолчанию создает окно для страницы, если оно еще не было создано, и делает его активным.

Данную функцию нужно переопределить, если требуется выполнить какие-либо операции во время активизации страницы. Если данная функция переопределяется, то сначала нужно вызвать из нее реализацию функции по умолчанию, а затем выполнять требуемые действия.

Если текущая страница набора свойств становится неактивной, библиотека вызывает функцию:

virtual BOOL CPropertyPage::OnKillActive ()

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

После успешного завершения функции OnKillActive библиотека вызывает функцию:

virtual void CPropertyPage::OnOK ()

Вызывается при нажатии кнопок OK, Apply и Close сразу после вызова функции OnKillActive. Данную функцию нужно переопределить, если необходимо выполнить какие-либо действия над текущей страницей перед тем, как будет удален весь набор свойств. Реализация по умолчанию помечает данную страницу как не имеющую изменений, это означает, что данные были успешно обновлены функцией OnKillActive.

При нажатии кнопок ОК или Apply вызывается функция:

virtual BOOL CPropertyPage::ОnАрр1у ()

Принимаются все изменения для всех страниц набора свойств, набор свойств теряет фокус ввода, и функция возвращает значение TRUE. Чтобы данная функция могла быть вызвана, необходимо прежде вызвать функцию SetModified со значением параметра, равным TRUE, которая делает доступной кнопку Apply, если пользователь произвел изменения параметров в странице свойств. Реализация по умолчанию вызывает функцию ОпОК.

Переопределив данную функцию, можно дать возможность приложению выполнить требуемые действия при нажатии пользователем кнопки Apply. Переопределенная функция должна возвращать значение TRUE (если изменения принимаются) или FALSE (если они не принимаются).

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

При нажатии кнопки Cancel библиотека вызывает следующую группу функций:

virtual void CPropertyPage::OnCancel ()

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

Как я уже неоднократно упоминал, в библиотеку MFC добавлены несколько новых классов, в число которых входит и CPropertyPageEx, расширяющий возможности работы со страницами свойств.

Этот новый класс библиотеки MFC предоставляет поддержку для расширенной структуры PROPSHEETPAGE, введенной в Windows 98 и Windows NT и содержащей дополнительные флаги и компоненты для работы с широкой областью заголовка, приспособленной для размещения как самого заголовка, так и подзаголовка.

 

Мастера

Мастер (Wizard) представляет собой набор свойств с последовательным набором страниц, которые пользователь может переключать (перемещение при этом идет на одну страницу вперед или назад). Мастера широко применяются в программах, например, для установки нового оборудования или создания факсимильного сообщения. Набор свойств в режиме мастера не имеет вкладок в верхней части страниц, и на экран в текущий момент времени выводится только одна страница свойств. В отличие от обычного набора свойств, мастер не имеет кнопок ОК и Apply (Применить), а содержит кнопки Back (Назад), Next (Далее) (рис. 15.6) или Finish (Готово), Cancel (Отмена) и Help (Справка).

Рис. 15.6. Типичное окно мастера

 

Создание мастеров

Для создания мастера нужно следовать шагам создания обычного набора свойств, но прежде, чем вызывать функцию CPropertySheet::DoModal, следует вызвать функцию SetWizardMode:

void CPropertySheet::SetWizardMode ()

Переводит создаваемый набор свойств в режим мастера. В этом случае при завершении работы пользователя с мастером функция CPropertySheet::DoModal вернет значение ID_WIZFINISH — если пользователь закрыл мастер, нажав кнопку Finish, или IDCANCEL — если была нажата кнопка Cancel. Функция устанавливает флаг PSF_WIZARD.

Чтобы сделать доступными требуемые кнопки мастера, нужно вызвать функцию:

void CPropertySheet::SetWizardButtons (DWORD dwFlags)

Параметр dwFlags может содержать комбинацию следующих значений, задающих кнопки, которые должны .быть доступны:

PSWIZB_BACK                     Кнопка Back

PSWIZB_NEXT                     Кнопка Next

PSWIZB_FINISH                                     Кнопка Finish

PSWIZB_DISABLEDFINISH           Кнопка Finish недоступна

Данную функцию можно вызвать только после того, как было открыто диалоговое окно мастера. Ее невозможно вызвать раньше функции CPropertySheet::DoModal. Обычно ее вызывают из функции CPropertyPage::OnSetActive.

Чтобы сделать доступной кнопку Finish или скрыть кнопки Back и Next в тот момент, когда пользователь, работая с мастером, достигнет последней страницы, нужно вызвать функцию SetFinishText.

void CPropertySheet::SetFinishText (LPCTSTR IpszText)

Помещает на кнопке Finish текст, заданный параметром IpszText, и скрывает кнопки Back и Next, когда пользователь завершает действия на последней странице мастера.

 

Переопределяемые функции

Класс CPropertyPage содержит следующие переопределяемые функции, используемые при создании мастера:

virtual LRESULT CPropertyPage::OnWizardBack() 

Вызывается, если нажата кнопка Back.

virtual LRESULT CPropertyPage::OnWizardNext()

Вызывается, если нажата кнопка Next. Обе функции возвращают 0, если должен быть осуществлен переход соответственно на одну страницу назад или вперед, или —1, что означает невозможность перехода к другой странице.

Если необходим переход более чем на одну страницу, то возвращается соответствующий идентификатор диалогового окна. Другим способом перехода к нужной странице является возможность задания в качестве текущей той, которая находится перед или после нужной, при помощи функции CPropertySheetr::SetActivePage.

virtual BOOL CPropertyPage::OnWizardFinish ()

Вызывается библиотекой, когда пользователь нажал кнопку Finish. Если функция возвращает значение TRUE, то набор свойств не уничтожается. Данную функцию можно переопределить, если необходимо выполнить какие-либо действия при нажатии пользователем кнопки Finish. При этом функция должна вернуть значение TRUE для того, чтобы набор свойств не был уничтожен.

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