Это приложение демонстрирует некоторые из аспектов применения модальных окон свойств на примере разработки диалогового окна Find. Создаваемое диалоговое окно Find будет похоже на применяемое в других приложениях, за одним исключением: оно будет состоять из двух вкладок, в одной из которых будет задаваться запрос поиска (вкладка Find ), а результат поиска будет отражаться в другой вкладке (другой странице свойств - вкладке Result ).
Перейдите в свой каталог выполнения лабораторных работ
Создайте новый проект и назовите его
Вкладки мастера создания приложений заполните в соответствии с таблицей| Таблица настройки мастера создания заготовки приложения | |
|---|---|
| Свойство проекта | Значение |
Вкладка Application Type | |
Application Type |
Single document |
Document/View architecture support |
Yes |
Resource language: |
Английский (США) |
Project style: |
MFC standard |
Use of MFC: |
Use MFC in a |
Вкладка Advanced Features | |
| Все сбросить! | |
| Остальные вкладки | |
| Оставить все установки по умолчанию! | |
Вкладка должна искать книги по языку, автору и издателю. Она будет представлять собой диалоговую страницу свойств, содержащую:
Combo Box для выбора языка ( Language )Edit Control:Author )Publisher )Static Text для перечисленных элементов управленияButton для начала поискаСоздадим данный ресурс:
Чтобы не забыть, смените режим построения приложения с Debug на Release
В панели Resource View оболочки щелкните в любом месте правой кнопкой мыши и выполните команду контекстного меню Add Resource
В окне
В панели Resource View оболочки вызовите новый ресурс на редактирование и через панель Properties измените идентификатор ресурса на IDD_PROPPAGE_FIND
Измените значение свойства Caption на Find Book (это будет названием вкладки, а не заголовком диалогового окна)
Убедитесь в том, что свойстваStyle установлено в Child (дочерний)Border установлено в ThinDisabled установлен в True
Поместите на форму диалогового ресурса элемент управления Combo Box и отредактируйте такID значение IDC_COMBO_LANGUAGEType в значение Drop ListData заполните некоторыми фиктивными данными в виде строки названий языков программирования, разделенных точкой_с_запятой, например, Visual C++/MFC;Microsoft C#/.NET;JScript.NET
Поместите на форму диалогового ресурса элемент Edit Control и отредактируйте такID значение IDC_EDIT_AUTHOR Disabled = True
Поместите на форму диалогового ресурса элемент Edit Control и отредактируйте такID значение IDC_EDIT_PUBLISHER Disabled = True
Поместите на форму диалогового ресурса текстовые метки и отредактируйте их свойства такCaption = Language:Caption = Author:Caption = Publisher:Align Text = Right
Выровняйте метки по правой границе, пользуясь панелью инструментов оболочки Dialog Editor
Поместите на форму диалогового ресурса кнопку Button и отредактируйте ее свойства такID = IDC_BUTTON_FIND Caption = Find
Щелкните по кнопке Test Dialog панели инструментов Dialog Editor оболочкиРесурс страницы свойств IDD_PROPPAGE_FIND должен выглядеть так

Тест не имеет системной кнопки закрытия окна, поэтому для его закрытия воспользуйтесь комбинацией клавиш Alt+F4.
Вкладка должна отображать список найденных книг, а в заголовке окна вкладки должно отображаться количество найденных книг. Она будет представлять собой диалоговую страницу свойств, содержащую список с линейками прокрутки.
Создадим данный ресурс.
В панели Resource View оболочки щелкните в любом месте правой кнопкой мыши и выполните команду контекстного меню Add Resource
В окне
В панели Resource View оболочки вызовите новый ресурс на редактирование и через панель Properties измените идентификатор ресурса на IDD_PROPPAGE_RESULT
Измените значение свойства Caption на Results: %ld books found - это будет названием вкладки с отображением количества (спецификатор формата %ld - long decimal ) найденных книг
Убедитесь в том, что свойстваStyle установлено в Child (дочерний)Border установлено в ThinDisabled установлен в True
Поместите на форму диалогового ресурса элемент управления List Box и отредактируйте такID значение IDC_LIST_BOOKSHorizontal Scroll = TrueVertical Scrollbar = True
Поместите на форму диалогового ресурса кнопку Button и отредактируйте ее свойства такID = IDC_BUTTON_GOTOCaption = Go to
Щелкните по кнопке Test Dialog панели инструментов Dialog Editor оболочкиРесурс страницы свойств IDD_PROPPAGE_RESULT должен выглядеть так

Для закрытия теста воспользуйтесь комбинацией клавиш Alt+F4.
Создадим поддерживающие классы для разработанных диалоговых ресурсов и внесем в каждый из этих классов свои ассоциированные переменные для поддержки элементов управления.
Вызовите на редактирование ресурс IDD_PROPPAGE_FIND
Щелкните в любом месте на форме ресурса правой кнопкой мыши и через контекстное меню выполните команду Add Class
Заполните мастер так, как показано на рисунке
После нажатия кнопки Finish мастер создаст код класса поддержки страницы свойств FindДля создания ассоциированных переменных для элементов управления нужно щелкать правой кнопкой мыши на этих элементах, вызывать контекстное меню и выполнять команду Add Variable.
Создайте управляющую переменную для раскрывающегося списка, заполнив, мастер так
Создайте управляющую переменную для кнопки
Вызовите на редактирование ресурс IDD_PROPPAGE_RESULT
Щелкните в любом месте на форме ресурса правой кнопкой мыши и через контекстное меню выполните команду Add Class
Заполните мастер так, как показано на рисунке
После нажатия кнопки Finish мастер создаст код класса поддержки страницы свойств ResultДля создания ассоциированных переменных для элементов управления диалогового окна Result нужно щелкать правой кнопкой мыши на этих элементах, вызывать контекстное меню и выполнять команду Add Variable.

Создайте управляющую переменную для кнопки Создадим строковый ресурс для конструктора класса-контейнера страниц свойств.
Откройте панель Resource View и вызовите на редактирование ресурс String Table
Создайте новую строку с настройкамиID=IDS_FINDValue=129
Вызовите на редактирование ресурс меню через панель
Удалите все пункты из категории
Добавьте между категорией Добавьте в класс CModalDemoView обработчик события выбора пункта меню Find Book. Для этого:
В редакторе ресурса меню щелкните на пункте меню
Настройте выделения мастера создания заготовки обработчика так
Нажмите кнопку Add and Edit (Добавить и редактировать)
Мастер создаст в файле ModalDemoView.cpp заготовку функции-обработчика, которая должна выглядеть так
Заготовка обработчика пункта меню Find Book
void CModalDemoView::OnBooksFindbook()
{
// TODO: Add your command handler code here
}
Добавьте в начало файла ModalDemoView.cpp последними после других директив #include операторы подключения заголовочных файлов, описывающих классы страницПодключение заголовков к файлу ModalDemoView.cpp // ModalDemoView.cpp : implementation of the CModalDemoView class // #include "stdafx.h" #include "ModalDemo.h" #include "ModalDemoDoc.h" #include "ModalDemoView.h" #include ".\modaldemoview.h" #ifdef _DEBUG #define new DEBUG_NEW #endif #include "FindPage.h" #include "ResultPage.h" // CModalDemoView ................................................
Отредактируйте обработчик пункта меню Find Book так
Код обработчика OnBooksFindbook()
void CModalDemoView::OnBooksFindbook()
{
CPropertySheet sheet(IDS_FIND);
CFindPage pageFind;
CResultPage pageResult;
sheet.AddPage(pageFind);
sheet.AddPage(pageResult);
sheet.DoModal();
}
В коде обработчика вначале создается экземпляр класса CPropertySheet на стеке, в который конструктор передает заголовок окна свойств через строковый ресурс IDS_FIND, определенный нами ранее в String Table. Затем создаются экземпляры двух страниц свойств, которые добавляются к окну свойств. В завершение окно свойств выводится на экран в модальном режиме.
Постройте проект и убедитесь, что все работает в рамках выполненного
Выполним работу по настройке страниц свойств в соответствии с нашими пожеланиями.
При отображении
Предположим, что необходимо удалить кнопки Cancel (Отмена) и Apply (Применить). Обычно изменения параметров, внесенные в диалоговое окно, вступают в силу либо при нажатии кнопки Применить, либо после закрытия окна, если только при этом не была нажата кнопка Cancel (Отмена). В нашем случае окно свойств никаких параметров изменять не будет, поэтому эти две кнопки можно убрать. Но как их убрать, если для окна свойств в нашем приложении отсутствует ресурс диалогового окна свойств и все кнопки созданы с помощью MFC. Выход в том, чтобы скрыть ненужные кнопки.
Стандартные кнопки, создаваемые MFC, всегда имеют идентификаторы, показанные в таблице
| Идентификаторы стандартных кнопок | |
|---|---|
| Кнопка | Идентификатор |
OK |
IDOK |
Cancel |
IDCANCEL |
Help |
IDHELP |
Apply |
ID_APPLY_NOW |
В панели
Появится окно настройки
После щелчка по кнопке Open появится окно мастера добавления класса, которое заполните так

После щелчка по кнопке Finish создастся класс, производный от MFC -класса CPropertySheet и с именем CFindSheet. Этот класс расширяет возможности стандартного базового класса CPropertySheet и мы можем в этом расширении внести свои дополнения. Код нового класса мы будем использовать в файле ModalDemoView.cpp, поэтому подключим с помощью директивы #include описание класса к этому файлу.
Вынесите подключение заголовочного файла FindSheet.h в начало файла ModalDemoView.cpp после подключений заголовков файлов страниц свойствПодключение класса CFindSheet к файлу ModalDemoView.cpp // ModalDemoView.cpp : implementation of the CModalDemoView class // #include "stdafx.h" #include "ModalDemo.h" #include "ModalDemoDoc.h" #include "ModalDemoView.h" #include ".\modaldemoview.h" #ifdef _DEBUG #define new DEBUG_NEW #endif #include "FindPage.h" #include "ResultPage.h" #include "FindSheet.h" // CModalDemoView IMPLEMENT_DYNCREATE(CModalDemoView, CView) ..............................................
Измените обработчик пункта меню Find Book так, чтобы вместо экземпляра стандартного класса CPropertySheet создавался экземпляр созданного нами класса-расширения CFindSheetИзменение обработчика пункта меню
void CModalDemoView::OnBooksFindbook()
{
// CPropertySheet sheet(IDS_FIND);
CFindSheet sheet(IDS_FIND);
CFindPage pageFind;
CResultPage pageResult;
sheet.AddPage(pageFind);
sheet.AddPage(pageResult);
sheet.DoModal();
}
Теперь скроем ненужные кнопки IDCANCEL и ID_APPLY_NOW в окне свойств, переопределив функцию OnInitDialog() в классе CFindSheet.
Откройте панель Class View, найдите и выделите класс CFindSheet
Сразу после выделения откройте панель
Создайте переопределенную функцию, которую заполните следующим кодомПереопределенная функция OnInitDialog()
BOOL CFindSheet::OnInitDialog()
{
BOOL bResult = CPropertySheet::OnInitDialog();
// TODO: Add your specialized code here
int ids[] = { IDCANCEL,
ID_APPLY_NOW };
int len = sizeof(ids) / sizeof(ids[0]);
for(int i = 0; i < len; i++){
CWnd *pWnd = GetDlgItem(ids[i]);
ASSERT(pWnd);
if(pWnd)
pWnd->ShowWindow(FALSE);
}
return bResult;
}
Постройте проект и убедитесь, что двух кнопок больше нет, мы их скрыли перед появлением окна свойств на экране
Оставшаяся нескрытой кнопка OK расположена на том же самом месте, где она находилась и ранее до сокрытия двух других кнопок. Переместим оставшуюся кнопку в другое место. Для этого воспользуемся функцией-членом класса CWnd::MoveWindow(), предварительно рассчитав координаты нового положения кнопки.
Дополните переопределенную функцию CFindSheet::OnInitDialog() следующим кодомДополнение функции кодом перемещения кнопки
BOOL CFindSheet::OnInitDialog()
{
BOOL bResult = CPropertySheet::OnInitDialog();
// TODO: Add your specialized code here
int ids[] = { IDCANCEL,
ID_APPLY_NOW };
int len = sizeof(ids) / sizeof(ids[0]);
for(int i = 0; i < len; i++){
CWnd *pWnd = GetDlgItem(ids[i]);
ASSERT(pWnd);
if(pWnd)
pWnd->ShowWindow(FALSE);
}
CWnd *pBtnOk = GetDlgItem(IDOK);
ASSERT(pBtnOk);
CRect rectSheet;
GetWindowRect(rectSheet); // Размер окна свойств
CRect rectBtnOk;
pBtnOk->GetWindowRect(rectBtnOk); // Размер окна кнопки
// Выяснить расстояние между нижней частью кнопки
// и нижней частью окна свойств
int iBorder = rectSheet.bottom - rectBtnOk.bottom;
// Изменить размеры окна свойств
rectSheet.right += rectBtnOk.Width() + iBorder;
rectSheet.bottom = rectBtnOk.top;
MoveWindow(rectSheet);
// Найти первую страницу
CPropertyPage *pPage = GetPage(0);
ASSERT(pPage);
CRect rectPage;
pPage->GetWindowRect(rectPage); // Размер страницы свойств
// Сохранить ширину и высоту кнопки OK
int cxOk = rectBtnOk.Width();
int cyOk = rectBtnOk.Height();
// Переместить кнопку OK
rectBtnOk.top = rectPage.top;
rectBtnOk.bottom = rectPage.top + cyOk;
rectBtnOk.left = rectSheet.right - (cxOk + iBorder);
rectBtnOk.right = rectBtnOk.left + cxOk;
ScreenToClient(rectBtnOk);
pBtnOk->MoveWindow(rectBtnOk);
return bResult;
}
Постройте приложение и убедитесь, что кнопка переместилась на нужное место, а окно свойств изменило размерыРезультат должен выглядеть так
Надпись на кнопке должна описывать действия, осуществляемые при щелчке на ней. Увидев в диалоговом окне Find Book кнопку OK, пользователь вряд ли сможет догадаться, что случится, если он щелкнет по ней. Приведет ли щелчок на кнопке OK к закрытию диалогового окна или началу поиска - из названия не ясно. Заменим надпись на кнопке более содержательной.
Еще раз вспомним, что диалоговый ресурс, который можно править в редакторе диалоговых окон, у нас имеется только для страниц свойств. Мы же в данный момент учимся изменять структуру диалогового окна свойств, которое стандартно сгенерировано MFC -библиотекой и не имеет ресурса в приложении, который мы могли бы визуально отредактировать. Поэтому мы вынуждены добиваться необходимых визуальных результатов программно.
Добавьте в конец все той же функции OnInitDialog() класса CFindSheet следующий кодДополнение функции кодом изменения надписи на кнопке OK
BOOL CFindSheet::OnInitDialog()
{
...........................................
ScreenToClient(rectBtnOk);
pBtnOk->MoveWindow(rectBtnOk);
pBtnOk->SetWindowText(_T("Close"));
return bResult;
}
Запустите приложение и убедитесь в результате - теперь на кнопке другая надписьИногда может потребоваться отключить (сделать недоступными - Disabled ) некоторые вкладки окна свойств. Прямых способов отключения вкладок не существует, поэтому рассмотрим обходные пути решения этой задачи. Для этого нужно выполнить следующие действия:
TCN_SELCHANGING или TCN_SELCHANGE позволяющего выяснить индекс текущей вкладки
Добавьте в конец объявления класса CFindSheet в файле FindSheet.h следующий кодОбъявление переменной хранения индекса текущей вкладки
class CFindSheet : public CPropertySheet
{
.............................
protected:
int m_iLastActivePage;
};
В следующих двух таблицах приведены классы массивов, поддерживаемые MFC, и описание их общедоступных методов
Классы массивов MFC | |
|---|---|
| Класс | Описание |
CArray |
Шаблонный класс для создания пользовательских классов массивов |
CByteArray |
Массив с данными типа байт |
CDWordArray |
Массив с данными типа DWORD (двойное слово) |
CObArray |
Массив может содержать классы, производные от класса CObject |
CPtArray |
Массив указателей на любые объекты и типы данных |
CStringArray |
Массив объектов типа |
CUIntArray |
Массив данных типа UINT ( unsigned int ) |
CWordArray |
Массив данных типа WORD (слово) |
Методы классов массивов MFC | |
|---|---|
| Класс | Описание |
array[int index] |
Оператор ображения к значению элемента массива по его индексу. |
array.GetAt(int index) |
Возвращает элемент массива по его индексу. |
array.SetAt(int index, element) |
Записывает элемент массива по его индексу. Тип данных записываемого элемента должен соответствовать типу данных всего массива. Вызов функции не приводит к росту количества элементов массива. |
array.InsertAt(int index, element) |
Вставляет новый элемент перед элементом index. Размерность массива увеличивается на единицу. |
array.Add(element) |
Добавляет новый элемент в конец массива. Добавляемый элемент должен иметь тип данных всего массива. |
array.RemoveAt(int index, int count=1) |
Удаляет count элементов, начиная с позиции index. Если второй аргумент не указан, то удаляется один элемент. |
array.RemoveAll() |
Удаляет все элементы массива и присваивает свойству количества элементов массива значение ноль. |
В массивах классов CObArray и CPtArray элементы как объекты не удалаются. Из массивов удаляются только указатели на объекты. При использовании функций RemoveAt() и RemoveAll() необходимо сначала удалить сами объекты, а затем удалить из массива их указатели. |
|
array.GetSize() |
Возвращает количество элементов массива. |
array.GetCount() |
Возвращает количество элементов массива. |
array.GetUpperBound() |
Возвращает индекс последнего элемента массива. |
array.SetSize(int iNewSize, iGrowBy) |
Управляет размером массива. |
array.Copy(source) |
Копирует source в array |
array.Append(source) |
Присоединяет source в конец array |
В случае с массивами классов CObArray и CPtArray функции Copy() и Append() копируют в массив указатели, а не сами объекты. В результате, изменение значения объекта в одном из массивов приведет к изменению значения соответствующего объекта в другом массиве. Кроме того, при удалении элемента в одном из массивов указатель в другом массиве станет недействительным. |
|
Создадим стандартный массив MFC -класса CUIntArray, в котором будут содержаться индексы всех вкладок окна свойств, заблокированных в данный момент.
Добавьте в конец объявления класса CFindSheet в файле FindSheet.h следующий кодСоздание массива индексов отключенных вкладок
class CFindSheet : public CPropertySheet
{
.....................................................
protected:
int m_iLastActivePage;
CUIntArray m_arrDisabledPages;
};
Добавим в класс CFindSheet функцию-член, позволяющую отключать определенные вкладки. Подлежащие отключению вкладки указаны в передаваемом этой функции аргументе переменной длины ( ), представляющем собой список с завершающим m_arrDisabledPages, затем маркирует вкладку присоединением к ее названию текста "-Disabled".
Эту дополнительную работу с добавлением маркера "-Disabled" нужно выполнять потому, что не существует способа по настоящему сделать вкладку недоступной с помощью изменения ее цвета на серый. Поэтому Microsoft рекомендует просто добавлять к заголовку вкладки маркирующий текст.
Выделите в панели
Нажмите кнопку Add, затем кнопку Finish
Мастер добавит заготовку в конец файла FindSheet.cpp, а в описание класса добавит прототип функции.
Исправьте прототип и заголовок функции для возможности передачи аргументов переменной длины, чтобы они смотрелись такПравка в файле FindSheet.h
class CFindSheet : public CPropertySheet
{
...............................................
public:
// Отключение вкладок
void DisablePage(int iFirstPage, ...);
};
Правка в файле FindSheet.cpp
// Отключение вкладок
void CFindSheet::DisablePage(int iFirstPage, ...)
{
}
Наполните созданную функцию следующим кодомЗаполнение функции кодом
// Отключение вкладок
void CFindSheet::DisablePage(int iFirstPage, ...)
{
int iPage = iFirstPage;
va_list marker;
va_start (marker, iFirstPage);
int nArgs = 0;
while(iPage != -1){// Список заканчивается -1
// Добавить индекс страницы
// в массив отключенных страниц,
// если его там нет
int count = (int)m_arrDisabledPages.GetCount();
bool flag = false;
for(int i = 0; i < count; i++)
if(m_arrDisabledPages.GetAt(i) == (UINT)iPage){
flag = true;
break;
}
if(!flag)
m_arrDisabledPages.Add(iPage);
SetDisabledText(iPage); // Наша функция маркирует вкладку
// Получить индекс следующей страницы
iPage = va_arg(marker, UINT);
nArgs++; // счетчик вкладок
ASSERT(nArgs < 100); // Страж зацикливания
}
}
Создадим функцию SetDisabledText(), вызов которой мы предусмотрели в предыдущей функции.
Выделите в панели
Нажмите кнопку Add, затем кнопку Finish. Мастер добавит заготовку в конец файла FindSheet.cpp, а в описание класса добавит прототип функции
Перед заголовком реализации функции добавьте Маркировка вкладок
#define DISABLED_TEXT " - Disabled"
// Маркировка текста заголовков вкладок
void CFindSheet::SetDisabledText(int iPage)
{
CTabCtrl *pTab = GetTabControl();
ASSERT(pTab);
TC_ITEM ti;
char szText[100];
ti.mask = TCIF_TEXT;
ti.pszText = szText;
ti.cchTextMax = 100;
VERIFY(pTab->GetItem(iPage, ti));
strcat(szText, DISABLED_TEXT);
VERIFY(pTab->SetItem(iPage, ti));
}
Получив средства отображения состояния отключенных вкладок (доступна или не доступна), добавим функцию, которая фактически отключает возможность выбора пользователем заблокированной вкладки. Эту задачу выполнит виртуальная функция OnNotify() базового класса CPropertySheet, которую мы переопределим в нашем классе-наследнике CFindSheet.
Выделите в панели Class View класс CFindSheet и перейдите в панель Properties
Установите в панели
Наполните созданную функцию следующим кодом
Переопределение виртуальной функции OnNotify()
BOOL CFindSheet::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
NMHDR *pNmh = (NMHDR*)lParam;
ASSERT(pNmh);
if(pNmh->code == TCN_SELCHANGING)
m_iLastActivePage = GetActiveIndex();
else if(pNmh->code == TCN_SELCHANGE){
int iCurrPage = GetActiveIndex();
if(IsPageDisabled(iCurrPage))
PostMessage(PSM_SETCURSEL, m_iLastActivePage);
}
return CPropertySheet::OnNotify(wParam, lParam, pResult);
}
Далее мы добавим функцию IsPageDisabled(), проверяющую, числится ли выбираемая пользователем вкладка как недоступная.
Выделите в панели
Нажмите кнопку Add, затем кнопку FinishМастер добавит заготовку в конец файла FindSheet.cpp, а в описание класса добавит прототип функции.
Наполните созданную функцию следующим кодомПроверка доступности вкладок
// Определяет, числится ли выбираемая
// пользователем вкладка в недоступных
BOOL CFindSheet::IsPageDisabled(int iPage)
{
BOOL bFoundEntry = FALSE;
int iSize = (int)m_arrDisabledPages.GetSize();
int i = 0;
while(i < iSize !bFoundEntry)
if(m_arrDisabledPages.GetAt(i) == (UINT)iPage)
bFoundEntry = TRUE;
else
i++;
return bFoundEntry;
}
Пока ничего не было найдено, вкладка Result должна быть пользователю недоступной. Мы для этого создали соответствующий код. Теперь осталось проверить его работу.
Найдите функцию-член OnInitDialog() класса CFindSheet и добавьте в конец следующий кодСделать недоступной вкладку Result с индексом 1
BOOL CFindSheet::OnInitDialog()
{
..............................................
pBtnOk->SetWindowText(_T("Close"));
DisablePage(1, -1);
return bResult;
}
Постройте приложение и убедитесь, что в начальный момент вкладка Result недоступнаИмея возможность отключать вкладки окна свойств нужно дать возможность включать их. Для этого необходимо добавить функцию EnablePage(), восстанавливающую доступ к вкладке, и функцию SetEnabledText(), удаляющую из ее заголовка маркер " - Disabled".
В панели Class View выделите класс CFindSheet, поскольку добавлять функцию-член мы будем именно в этот класс
Через контекстное меню по щелчку правой кнопкой мыши на классе
Нажмите кнопку Add, затем кнопку Finish. Мастер добавит заготовку в конец файла FindSheet.cpp, а в описание класса добавит прототип функции
Наполните созданную функцию следующим кодомФункция включения доступа к вкладке
// Включение вкладки
void CFindSheet::EnablePage(int iPage)
{
BOOL bFoundEntry = FALSE;
int iSize = (int)m_arrDisabledPages.GetSize();
int i = 0;
while(i < iSize !bFoundEntry)
if(m_arrDisabledPages.GetAt(i) == (UINT)iPage)
bFoundEntry = TRUE;
else
i++;
if(bFoundEntry){
m_arrDisabledPages.RemoveAt(i);
SetEnabledText(iPage);
}
}
Снова в панели Class View выделите класс CFindSheet
Через контекстное меню по щелчку правой кнопкой мыши на классе
Нажмите кнопку Add, затем кнопку Finish
Мастер добавит заготовку в конец файла FindSheet.cpp, а в описание класса добавит прототип функции.
Наполните созданную функцию следующим кодомУдаление маркера " - Disabled" из заголовка вкладки
// Удаление маркера " - Disabled" из заголовка вкладки
void CFindSheet::SetEnabledText(int iPage)
{
CTabCtrl *pTab = GetTabControl();
ASSERT(pTab);
TC_ITEM ti;
char szText[100];
ti.mask = TCIF_TEXT;
ti.pszText = szText;
ti.cchTextMax = 100;
VERIFY(pTab->GetItem(iPage, ti));
// Функция strstr() осуществляет поиск в строке szText
// первого вхождения подстроки DISABLED_TEXT
char *pFound = strstr(szText, DISABLED_TEXT);
if(pFound){
*pFound = '\0'; // Устанавливаем метку конца заголовка
VERIFY(pTab->SetItem(iPage, ti));
}
}
Проверить работу кода включения доступности второй вкладки можно с помощью обработчика кнопки Find первой вкладки. В реальном приложении вкладка Result должна стать доступной лишь после того, как фактически выполненный поиск увенчается успехом. В нашем случае вызов кода разрешения и запрещения доступа к вкладке существенно упрощен.
Класс CFindSheet является родительским по отношению к классам вкладок CFindPage и CResultPage. Мы создадим обработчик кнопки Find первой вкладки, в котором получим указатель на объект родительского класса, а потом адресуемся к проверяемой функции EnablePage() родительского класса.
Откройте панель Resource View оболочки и вызовите на редактирование ресурс IDD_PROPPAGE_FIND
Выделите кнопку
Наполните созданную функцию следующим кодомОбработчик кнопки Find
void CFindPage::OnBnClickedButtonFind()
{
// TODO: Add your control notification handler code here
CFindSheet *pParentSheet = (CFindSheet*)GetParent();
ASSERT(pParentSheet->IsKindOf(RUNTIME_CLASS(CFindSheet)));
pParentSheet->EnablePage(1);// Вторая вкладка
}
В начало файла FindPage.cpp добавьте директиву #include, подключающую к этому файлу заголовочный файл с описанием класса CFindSheet, используемого в обработчикеПодключение заголовочного файла FindSheet.h к файлу с обработчиком кнопки FindPage.cpp
// FindPage.cpp : implementation file
//
#include "stdafx.h"
#include "ModalDemo.h"
#include "FindPage.h"
#include ".\findpage.h"
#include "FindSheet.h"
// CFindPage dialog
IMPLEMENT_DYNAMIC(CFindPage, CPropertyPage)
CFindPage::CFindPage()
: CPropertyPage(CFindPage::IDD)
{
}
CFindPage::~CFindPage()
{
}
..................................................
Постройте приложение и убедитесь, что вкладка Result становится доступной после щелчка по кнопке Find В зависимости от результатов поиска по команде из первой вкладке нужно изменить заголовок и доступность второй вкладки. Количество найденных книг для простоты свяжем с индексом элемента в раскрывающемся списке интерфейса первой вкладки. Выбранный индекс списка nHint при нажатии кнопки Find будет передаваться в функцию CFindSheet::SetResult(nHits), которая установит заголовок и доступность второй вкладки Result. Функция будет в большей части повторять код предыдущей функции CFindSheet::SetEnabledText(int iPage) .
В начало файла FindSheet.cpp добавьте два макроопределенияМакроопределения в начале файла FindSheet.cpp // FindSheet.cpp : implementation file // #include "stdafx.h" #include "ModalDemo.h" #include "FindSheet.h" #include ".\findsheet.h" #define RESULT_TAB_INDEX 1 #define RESULT_TAB_CAPTION "Result: %ld books found" ......................................................
Для класса
Нажмите кнопку Add, затем кнопку Finish
Мастер добавит заготовку в конец файла FindSheet.cpp, а в описание класса добавит прототип функции.
Наполните созданную функцию следующим кодомФункция SetResult()
// Изменение содержимого и доступности заголовка вкладки
void CFindSheet::SetResult(int nHits)
{
// Изменение текста заголовка вкладки
CTabCtrl *pTab = GetTabControl();
ASSERT(pTab);
TC_ITEM ti;
char szText[100];
ti.mask = TCIF_TEXT;
ti.pszText = szText;
ti.cchTextMax = 100;
VERIFY(pTab->GetItem(RESULT_TAB_INDEX, ti));
sprintf(szText, RESULT_TAB_CAPTION, nHits);
VERIFY(pTab->SetItem(RESULT_TAB_INDEX, ti));
// Управление доступностью вкладки
if(nHits <= 0)
DisablePage(RESULT_TAB_INDEX, -1);
else
EnablePage(RESULT_TAB_INDEX);
}
Перейдите в файл FindPage.cpp и найдите обработчик кнопки Find, который измените такИзменение обработчика кнопки Find
void CFindPage::OnBnClickedButtonFind()
{
// TODO: Add your control notification handler code here
CFindSheet *pParentSheet = (CFindSheet*)GetParent();
ASSERT(pParentSheet->IsKindOf(RUNTIME_CLASS(CFindSheet)));
// pParentSheet->EnablePage(1);// Вторая вкладка
// Демонстрация управления вкладкой Result по индексу списка
int iIndex = m_cboLanguages.GetCurSel();
pParentSheet->SetResult(iIndex);
}
Постройте приложение и убедитесь, что:Все работает, как мы задумали. Внешний вид приложения должен быть примерно таким

Это приложение демонстрирует некоторые из аспектов применения модальных окон свойств на примере разработки диалогового окна Find. Создаваемое диалоговое окно Find будет похоже на применяемое в других приложениях, за одним исключением: оно будет состоять из двух вкладок, в одной из которых будет задаваться запрос поиска (вкладка Find ), а результат поиска будет отражаться в другой вкладке (другой странице свойств - вкладке Result ).
Перейдите в свой каталог выполнения лабораторных работ
Создайте новый проект и назовите его
Вкладки мастера создания приложений заполните в соответствии с таблицей| Таблица настройки мастера создания заготовки приложения | |
|---|---|
| Свойство проекта | Значение |
Вкладка Application Type | |
Application Type |
Single document |
Document/View architecture support |
Yes |
Resource language: |
Английский (США) |
Project style: |
MFC standard |
Use of MFC: |
Use MFC in a |
Вкладка Advanced Features | |
| Все сбросить! | |
| Остальные вкладки | |
| Оставить все установки по умолчанию! | |
Вкладка должна искать книги по языку, автору и издателю. Она будет представлять собой диалоговую страницу свойств, содержащую:
Combo Box для выбора языка ( Language )Edit Control:Author )Publisher )Static Text для перечисленных элементов управленияButton для начала поискаСоздадим данный ресурс:
Чтобы не забыть, смените режим построения приложения с Debug на Release
В панели Resource View оболочки щелкните в любом месте правой кнопкой мыши и выполните команду контекстного меню Add Resource
В окне
В панели Resource View оболочки вызовите новый ресурс на редактирование и через панель Properties измените идентификатор ресурса на IDD_PROPPAGE_FIND
Измените значение свойства Caption на Find Book (это будет названием вкладки, а не заголовком диалогового окна)
Убедитесь в том, что свойстваStyle установлено в Child (дочерний)Border установлено в ThinDisabled установлен в True
Поместите на форму диалогового ресурса элемент управления Combo Box и отредактируйте такID значение IDC_COMBO_LANGUAGEType в значение Drop ListData заполните некоторыми фиктивными данными в виде строки названий языков программирования, разделенных точкой_с_запятой, например, Visual C++/MFC;Microsoft C#/.NET;JScript.NET
Поместите на форму диалогового ресурса элемент Edit Control и отредактируйте такID значение IDC_EDIT_AUTHOR Disabled = True
Поместите на форму диалогового ресурса элемент Edit Control и отредактируйте такID значение IDC_EDIT_PUBLISHER Disabled = True
Поместите на форму диалогового ресурса текстовые метки и отредактируйте их свойства такCaption = Language:Caption = Author:Caption = Publisher:Align Text = Right
Выровняйте метки по правой границе, пользуясь панелью инструментов оболочки Dialog Editor
Поместите на форму диалогового ресурса кнопку Button и отредактируйте ее свойства такID = IDC_BUTTON_FIND Caption = Find
Щелкните по кнопке Test Dialog панели инструментов Dialog Editor оболочкиРесурс страницы свойств IDD_PROPPAGE_FIND должен выглядеть так

Тест не имеет системной кнопки закрытия окна, поэтому для его закрытия воспользуйтесь комбинацией клавиш Alt+F4.
Вкладка должна отображать список найденных книг, а в заголовке окна вкладки должно отображаться количество найденных книг. Она будет представлять собой диалоговую страницу свойств, содержащую список с линейками прокрутки.
Создадим данный ресурс.
В панели Resource View оболочки щелкните в любом месте правой кнопкой мыши и выполните команду контекстного меню Add Resource
В окне
В панели Resource View оболочки вызовите новый ресурс на редактирование и через панель Properties измените идентификатор ресурса на IDD_PROPPAGE_RESULT
Измените значение свойства Caption на Results: %ld books found - это будет названием вкладки с отображением количества (спецификатор формата %ld - long decimal ) найденных книг
Убедитесь в том, что свойстваStyle установлено в Child (дочерний)Border установлено в ThinDisabled установлен в True
Поместите на форму диалогового ресурса элемент управления List Box и отредактируйте такID значение IDC_LIST_BOOKSHorizontal Scroll = TrueVertical Scrollbar = True
Поместите на форму диалогового ресурса кнопку Button и отредактируйте ее свойства такID = IDC_BUTTON_GOTOCaption = Go to
Щелкните по кнопке Test Dialog панели инструментов Dialog Editor оболочкиРесурс страницы свойств IDD_PROPPAGE_RESULT должен выглядеть так

Для закрытия теста воспользуйтесь комбинацией клавиш Alt+F4.
Создадим поддерживающие классы для разработанных диалоговых ресурсов и внесем в каждый из этих классов свои ассоциированные переменные для поддержки элементов управления.
Вызовите на редактирование ресурс IDD_PROPPAGE_FIND
Щелкните в любом месте на форме ресурса правой кнопкой мыши и через контекстное меню выполните команду Add Class
Заполните мастер так, как показано на рисунке
После нажатия кнопки Finish мастер создаст код класса поддержки страницы свойств FindДля создания ассоциированных переменных для элементов управления нужно щелкать правой кнопкой мыши на этих элементах, вызывать контекстное меню и выполнять команду Add Variable.
Создайте управляющую переменную для раскрывающегося списка, заполнив, мастер так
Создайте управляющую переменную для кнопки
Вызовите на редактирование ресурс IDD_PROPPAGE_RESULT
Щелкните в любом месте на форме ресурса правой кнопкой мыши и через контекстное меню выполните команду Add Class
Заполните мастер так, как показано на рисунке
После нажатия кнопки Finish мастер создаст код класса поддержки страницы свойств ResultДля создания ассоциированных переменных для элементов управления диалогового окна Result нужно щелкать правой кнопкой мыши на этих элементах, вызывать контекстное меню и выполнять команду Add Variable.

Создайте управляющую переменную для кнопки Создадим строковый ресурс для конструктора класса-контейнера страниц свойств.
Откройте панель Resource View и вызовите на редактирование ресурс String Table
Создайте новую строку с настройкамиID=IDS_FINDValue=129
Вызовите на редактирование ресурс меню через панель
Удалите все пункты из категории
Добавьте между категорией Добавьте в класс CModalDemoView обработчик события выбора пункта меню Find Book. Для этого:
В редакторе ресурса меню щелкните на пункте меню
Настройте выделения мастера создания заготовки обработчика так
Нажмите кнопку Add and Edit (Добавить и редактировать)
Мастер создаст в файле ModalDemoView.cpp заготовку функции-обработчика, которая должна выглядеть так
Заготовка обработчика пункта меню Find Book
void CModalDemoView::OnBooksFindbook()
{
// TODO: Add your command handler code here
}
Добавьте в начало файла ModalDemoView.cpp последними после других директив #include операторы подключения заголовочных файлов, описывающих классы страницПодключение заголовков к файлу ModalDemoView.cpp // ModalDemoView.cpp : implementation of the CModalDemoView class // #include "stdafx.h" #include "ModalDemo.h" #include "ModalDemoDoc.h" #include "ModalDemoView.h" #include ".\modaldemoview.h" #ifdef _DEBUG #define new DEBUG_NEW #endif #include "FindPage.h" #include "ResultPage.h" // CModalDemoView ................................................
Отредактируйте обработчик пункта меню Find Book так
Код обработчика OnBooksFindbook()
void CModalDemoView::OnBooksFindbook()
{
CPropertySheet sheet(IDS_FIND);
CFindPage pageFind;
CResultPage pageResult;
sheet.AddPage(pageFind);
sheet.AddPage(pageResult);
sheet.DoModal();
}
В коде обработчика вначале создается экземпляр класса CPropertySheet на стеке, в который конструктор передает заголовок окна свойств через строковый ресурс IDS_FIND, определенный нами ранее в String Table. Затем создаются экземпляры двух страниц свойств, которые добавляются к окну свойств. В завершение окно свойств выводится на экран в модальном режиме.
Постройте проект и убедитесь, что все работает в рамках выполненного
Выполним работу по настройке страниц свойств в соответствии с нашими пожеланиями.
При отображении
Предположим, что необходимо удалить кнопки Cancel (Отмена) и Apply (Применить). Обычно изменения параметров, внесенные в диалоговое окно, вступают в силу либо при нажатии кнопки Применить, либо после закрытия окна, если только при этом не была нажата кнопка Cancel (Отмена). В нашем случае окно свойств никаких параметров изменять не будет, поэтому эти две кнопки можно убрать. Но как их убрать, если для окна свойств в нашем приложении отсутствует ресурс диалогового окна свойств и все кнопки созданы с помощью MFC. Выход в том, чтобы скрыть ненужные кнопки.
Стандартные кнопки, создаваемые MFC, всегда имеют идентификаторы, показанные в таблице
| Идентификаторы стандартных кнопок | |
|---|---|
| Кнопка | Идентификатор |
OK |
IDOK |
Cancel |
IDCANCEL |
Help |
IDHELP |
Apply |
ID_APPLY_NOW |
В панели
Появится окно настройки
После щелчка по кнопке Open появится окно мастера добавления класса, которое заполните так

После щелчка по кнопке Finish создастся класс, производный от MFC -класса CPropertySheet и с именем CFindSheet. Этот класс расширяет возможности стандартного базового класса CPropertySheet и мы можем в этом расширении внести свои дополнения. Код нового класса мы будем использовать в файле ModalDemoView.cpp, поэтому подключим с помощью директивы #include описание класса к этому файлу.
Вынесите подключение заголовочного файла FindSheet.h в начало файла ModalDemoView.cpp после подключений заголовков файлов страниц свойствПодключение класса CFindSheet к файлу ModalDemoView.cpp // ModalDemoView.cpp : implementation of the CModalDemoView class // #include "stdafx.h" #include "ModalDemo.h" #include "ModalDemoDoc.h" #include "ModalDemoView.h" #include ".\modaldemoview.h" #ifdef _DEBUG #define new DEBUG_NEW #endif #include "FindPage.h" #include "ResultPage.h" #include "FindSheet.h" // CModalDemoView IMPLEMENT_DYNCREATE(CModalDemoView, CView) ..............................................
Измените обработчик пункта меню Find Book так, чтобы вместо экземпляра стандартного класса CPropertySheet создавался экземпляр созданного нами класса-расширения CFindSheetИзменение обработчика пункта меню
void CModalDemoView::OnBooksFindbook()
{
// CPropertySheet sheet(IDS_FIND);
CFindSheet sheet(IDS_FIND);
CFindPage pageFind;
CResultPage pageResult;
sheet.AddPage(pageFind);
sheet.AddPage(pageResult);
sheet.DoModal();
}
Теперь скроем ненужные кнопки IDCANCEL и ID_APPLY_NOW в окне свойств, переопределив функцию OnInitDialog() в классе CFindSheet.
Откройте панель Class View, найдите и выделите класс CFindSheet
Сразу после выделения откройте панель
Создайте переопределенную функцию, которую заполните следующим кодомПереопределенная функция OnInitDialog()
BOOL CFindSheet::OnInitDialog()
{
BOOL bResult = CPropertySheet::OnInitDialog();
// TODO: Add your specialized code here
int ids[] = { IDCANCEL,
ID_APPLY_NOW };
int len = sizeof(ids) / sizeof(ids[0]);
for(int i = 0; i < len; i++){
CWnd *pWnd = GetDlgItem(ids[i]);
ASSERT(pWnd);
if(pWnd)
pWnd->ShowWindow(FALSE);
}
return bResult;
}
Постройте проект и убедитесь, что двух кнопок больше нет, мы их скрыли перед появлением окна свойств на экране
Оставшаяся нескрытой кнопка OK расположена на том же самом месте, где она находилась и ранее до сокрытия двух других кнопок. Переместим оставшуюся кнопку в другое место. Для этого воспользуемся функцией-членом класса CWnd::MoveWindow(), предварительно рассчитав координаты нового положения кнопки.
Дополните переопределенную функцию CFindSheet::OnInitDialog() следующим кодомДополнение функции кодом перемещения кнопки
BOOL CFindSheet::OnInitDialog()
{
BOOL bResult = CPropertySheet::OnInitDialog();
// TODO: Add your specialized code here
int ids[] = { IDCANCEL,
ID_APPLY_NOW };
int len = sizeof(ids) / sizeof(ids[0]);
for(int i = 0; i < len; i++){
CWnd *pWnd = GetDlgItem(ids[i]);
ASSERT(pWnd);
if(pWnd)
pWnd->ShowWindow(FALSE);
}
CWnd *pBtnOk = GetDlgItem(IDOK);
ASSERT(pBtnOk);
CRect rectSheet;
GetWindowRect(rectSheet); // Размер окна свойств
CRect rectBtnOk;
pBtnOk->GetWindowRect(rectBtnOk); // Размер окна кнопки
// Выяснить расстояние между нижней частью кнопки
// и нижней частью окна свойств
int iBorder = rectSheet.bottom - rectBtnOk.bottom;
// Изменить размеры окна свойств
rectSheet.right += rectBtnOk.Width() + iBorder;
rectSheet.bottom = rectBtnOk.top;
MoveWindow(rectSheet);
// Найти первую страницу
CPropertyPage *pPage = GetPage(0);
ASSERT(pPage);
CRect rectPage;
pPage->GetWindowRect(rectPage); // Размер страницы свойств
// Сохранить ширину и высоту кнопки OK
int cxOk = rectBtnOk.Width();
int cyOk = rectBtnOk.Height();
// Переместить кнопку OK
rectBtnOk.top = rectPage.top;
rectBtnOk.bottom = rectPage.top + cyOk;
rectBtnOk.left = rectSheet.right - (cxOk + iBorder);
rectBtnOk.right = rectBtnOk.left + cxOk;
ScreenToClient(rectBtnOk);
pBtnOk->MoveWindow(rectBtnOk);
return bResult;
}
Постройте приложение и убедитесь, что кнопка переместилась на нужное место, а окно свойств изменило размерыРезультат должен выглядеть так
Надпись на кнопке должна описывать действия, осуществляемые при щелчке на ней. Увидев в диалоговом окне Find Book кнопку OK, пользователь вряд ли сможет догадаться, что случится, если он щелкнет по ней. Приведет ли щелчок на кнопке OK к закрытию диалогового окна или началу поиска - из названия не ясно. Заменим надпись на кнопке более содержательной.
Еще раз вспомним, что диалоговый ресурс, который можно править в редакторе диалоговых окон, у нас имеется только для страниц свойств. Мы же в данный момент учимся изменять структуру диалогового окна свойств, которое стандартно сгенерировано MFC -библиотекой и не имеет ресурса в приложении, который мы могли бы визуально отредактировать. Поэтому мы вынуждены добиваться необходимых визуальных результатов программно.
Добавьте в конец все той же функции OnInitDialog() класса CFindSheet следующий кодДополнение функции кодом изменения надписи на кнопке OK
BOOL CFindSheet::OnInitDialog()
{
...........................................
ScreenToClient(rectBtnOk);
pBtnOk->MoveWindow(rectBtnOk);
pBtnOk->SetWindowText(_T("Close"));
return bResult;
}
Запустите приложение и убедитесь в результате - теперь на кнопке другая надписьИногда может потребоваться отключить (сделать недоступными - Disabled ) некоторые вкладки окна свойств. Прямых способов отключения вкладок не существует, поэтому рассмотрим обходные пути решения этой задачи. Для этого нужно выполнить следующие действия:
TCN_SELCHANGING или TCN_SELCHANGE позволяющего выяснить индекс текущей вкладки
Добавьте в конец объявления класса CFindSheet в файле FindSheet.h следующий кодОбъявление переменной хранения индекса текущей вкладки
class CFindSheet : public CPropertySheet
{
.............................
protected:
int m_iLastActivePage;
};
В следующих двух таблицах приведены классы массивов, поддерживаемые MFC, и описание их общедоступных методов
Классы массивов MFC | |
|---|---|
| Класс | Описание |
CArray |
Шаблонный класс для создания пользовательских классов массивов |
CByteArray |
Массив с данными типа байт |
CDWordArray |
Массив с данными типа DWORD (двойное слово) |
CObArray |
Массив может содержать классы, производные от класса CObject |
CPtArray |
Массив указателей на любые объекты и типы данных |
CStringArray |
Массив объектов типа |
CUIntArray |
Массив данных типа UINT ( unsigned int ) |
CWordArray |
Массив данных типа WORD (слово) |
Методы классов массивов MFC | |
|---|---|
| Класс | Описание |
array[int index] |
Оператор ображения к значению элемента массива по его индексу. |
array.GetAt(int index) |
Возвращает элемент массива по его индексу. |
array.SetAt(int index, element) |
Записывает элемент массива по его индексу. Тип данных записываемого элемента должен соответствовать типу данных всего массива. Вызов функции не приводит к росту количества элементов массива. |
array.InsertAt(int index, element) |
Вставляет новый элемент перед элементом index. Размерность массива увеличивается на единицу. |
array.Add(element) |
Добавляет новый элемент в конец массива. Добавляемый элемент должен иметь тип данных всего массива. |
array.RemoveAt(int index, int count=1) |
Удаляет count элементов, начиная с позиции index. Если второй аргумент не указан, то удаляется один элемент. |
array.RemoveAll() |
Удаляет все элементы массива и присваивает свойству количества элементов массива значение ноль. |
В массивах классов CObArray и CPtArray элементы как объекты не удалаются. Из массивов удаляются только указатели на объекты. При использовании функций RemoveAt() и RemoveAll() необходимо сначала удалить сами объекты, а затем удалить из массива их указатели. |
|
array.GetSize() |
Возвращает количество элементов массива. |
array.GetCount() |
Возвращает количество элементов массива. |
array.GetUpperBound() |
Возвращает индекс последнего элемента массива. |
array.SetSize(int iNewSize, iGrowBy) |
Управляет размером массива. |
array.Copy(source) |
Копирует source в array |
array.Append(source) |
Присоединяет source в конец array |
В случае с массивами классов CObArray и CPtArray функции Copy() и Append() копируют в массив указатели, а не сами объекты. В результате, изменение значения объекта в одном из массивов приведет к изменению значения соответствующего объекта в другом массиве. Кроме того, при удалении элемента в одном из массивов указатель в другом массиве станет недействительным. |
|
Создадим стандартный массив MFC -класса CUIntArray, в котором будут содержаться индексы всех вкладок окна свойств, заблокированных в данный момент.
Добавьте в конец объявления класса CFindSheet в файле FindSheet.h следующий кодСоздание массива индексов отключенных вкладок
class CFindSheet : public CPropertySheet
{
.....................................................
protected:
int m_iLastActivePage;
CUIntArray m_arrDisabledPages;
};
Добавим в класс CFindSheet функцию-член, позволяющую отключать определенные вкладки. Подлежащие отключению вкладки указаны в передаваемом этой функции аргументе переменной длины ( ), представляющем собой список с завершающим m_arrDisabledPages, затем маркирует вкладку присоединением к ее названию текста "-Disabled".
Эту дополнительную работу с добавлением маркера "-Disabled" нужно выполнять потому, что не существует способа по настоящему сделать вкладку недоступной с помощью изменения ее цвета на серый. Поэтому Microsoft рекомендует просто добавлять к заголовку вкладки маркирующий текст.
Выделите в панели
Нажмите кнопку Add, затем кнопку Finish
Мастер добавит заготовку в конец файла FindSheet.cpp, а в описание класса добавит прототип функции.
Исправьте прототип и заголовок функции для возможности передачи аргументов переменной длины, чтобы они смотрелись такПравка в файле FindSheet.h
class CFindSheet : public CPropertySheet
{
...............................................
public:
// Отключение вкладок
void DisablePage(int iFirstPage, ...);
};
Правка в файле FindSheet.cpp
// Отключение вкладок
void CFindSheet::DisablePage(int iFirstPage, ...)
{
}
Наполните созданную функцию следующим кодомЗаполнение функции кодом
// Отключение вкладок
void CFindSheet::DisablePage(int iFirstPage, ...)
{
int iPage = iFirstPage;
va_list marker;
va_start (marker, iFirstPage);
int nArgs = 0;
while(iPage != -1){// Список заканчивается -1
// Добавить индекс страницы
// в массив отключенных страниц,
// если его там нет
int count = (int)m_arrDisabledPages.GetCount();
bool flag = false;
for(int i = 0; i < count; i++)
if(m_arrDisabledPages.GetAt(i) == (UINT)iPage){
flag = true;
break;
}
if(!flag)
m_arrDisabledPages.Add(iPage);
SetDisabledText(iPage); // Наша функция маркирует вкладку
// Получить индекс следующей страницы
iPage = va_arg(marker, UINT);
nArgs++; // счетчик вкладок
ASSERT(nArgs < 100); // Страж зацикливания
}
}
Создадим функцию SetDisabledText(), вызов которой мы предусмотрели в предыдущей функции.
Выделите в панели
Нажмите кнопку Add, затем кнопку Finish. Мастер добавит заготовку в конец файла FindSheet.cpp, а в описание класса добавит прототип функции
Перед заголовком реализации функции добавьте Маркировка вкладок
#define DISABLED_TEXT " - Disabled"
// Маркировка текста заголовков вкладок
void CFindSheet::SetDisabledText(int iPage)
{
CTabCtrl *pTab = GetTabControl();
ASSERT(pTab);
TC_ITEM ti;
char szText[100];
ti.mask = TCIF_TEXT;
ti.pszText = szText;
ti.cchTextMax = 100;
VERIFY(pTab->GetItem(iPage, ti));
strcat(szText, DISABLED_TEXT);
VERIFY(pTab->SetItem(iPage, ti));
}
Получив средства отображения состояния отключенных вкладок (доступна или не доступна), добавим функцию, которая фактически отключает возможность выбора пользователем заблокированной вкладки. Эту задачу выполнит виртуальная функция OnNotify() базового класса CPropertySheet, которую мы переопределим в нашем классе-наследнике CFindSheet.
Выделите в панели Class View класс CFindSheet и перейдите в панель Properties
Установите в панели
Наполните созданную функцию следующим кодом
Переопределение виртуальной функции OnNotify()
BOOL CFindSheet::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
NMHDR *pNmh = (NMHDR*)lParam;
ASSERT(pNmh);
if(pNmh->code == TCN_SELCHANGING)
m_iLastActivePage = GetActiveIndex();
else if(pNmh->code == TCN_SELCHANGE){
int iCurrPage = GetActiveIndex();
if(IsPageDisabled(iCurrPage))
PostMessage(PSM_SETCURSEL, m_iLastActivePage);
}
return CPropertySheet::OnNotify(wParam, lParam, pResult);
}
Далее мы добавим функцию IsPageDisabled(), проверяющую, числится ли выбираемая пользователем вкладка как недоступная.
Выделите в панели
Нажмите кнопку Add, затем кнопку FinishМастер добавит заготовку в конец файла FindSheet.cpp, а в описание класса добавит прототип функции.
Наполните созданную функцию следующим кодомПроверка доступности вкладок
// Определяет, числится ли выбираемая
// пользователем вкладка в недоступных
BOOL CFindSheet::IsPageDisabled(int iPage)
{
BOOL bFoundEntry = FALSE;
int iSize = (int)m_arrDisabledPages.GetSize();
int i = 0;
while(i < iSize !bFoundEntry)
if(m_arrDisabledPages.GetAt(i) == (UINT)iPage)
bFoundEntry = TRUE;
else
i++;
return bFoundEntry;
}
Пока ничего не было найдено, вкладка Result должна быть пользователю недоступной. Мы для этого создали соответствующий код. Теперь осталось проверить его работу.
Найдите функцию-член OnInitDialog() класса CFindSheet и добавьте в конец следующий кодСделать недоступной вкладку Result с индексом 1
BOOL CFindSheet::OnInitDialog()
{
..............................................
pBtnOk->SetWindowText(_T("Close"));
DisablePage(1, -1);
return bResult;
}
Постройте приложение и убедитесь, что в начальный момент вкладка Result недоступнаИмея возможность отключать вкладки окна свойств нужно дать возможность включать их. Для этого необходимо добавить функцию EnablePage(), восстанавливающую доступ к вкладке, и функцию SetEnabledText(), удаляющую из ее заголовка маркер " - Disabled".
В панели Class View выделите класс CFindSheet, поскольку добавлять функцию-член мы будем именно в этот класс
Через контекстное меню по щелчку правой кнопкой мыши на классе
Нажмите кнопку Add, затем кнопку Finish. Мастер добавит заготовку в конец файла FindSheet.cpp, а в описание класса добавит прототип функции
Наполните созданную функцию следующим кодомФункция включения доступа к вкладке
// Включение вкладки
void CFindSheet::EnablePage(int iPage)
{
BOOL bFoundEntry = FALSE;
int iSize = (int)m_arrDisabledPages.GetSize();
int i = 0;
while(i < iSize !bFoundEntry)
if(m_arrDisabledPages.GetAt(i) == (UINT)iPage)
bFoundEntry = TRUE;
else
i++;
if(bFoundEntry){
m_arrDisabledPages.RemoveAt(i);
SetEnabledText(iPage);
}
}
Снова в панели Class View выделите класс CFindSheet
Через контекстное меню по щелчку правой кнопкой мыши на классе
Нажмите кнопку Add, затем кнопку Finish
Мастер добавит заготовку в конец файла FindSheet.cpp, а в описание класса добавит прототип функции.
Наполните созданную функцию следующим кодомУдаление маркера " - Disabled" из заголовка вкладки
// Удаление маркера " - Disabled" из заголовка вкладки
void CFindSheet::SetEnabledText(int iPage)
{
CTabCtrl *pTab = GetTabControl();
ASSERT(pTab);
TC_ITEM ti;
char szText[100];
ti.mask = TCIF_TEXT;
ti.pszText = szText;
ti.cchTextMax = 100;
VERIFY(pTab->GetItem(iPage, ti));
// Функция strstr() осуществляет поиск в строке szText
// первого вхождения подстроки DISABLED_TEXT
char *pFound = strstr(szText, DISABLED_TEXT);
if(pFound){
*pFound = '\0'; // Устанавливаем метку конца заголовка
VERIFY(pTab->SetItem(iPage, ti));
}
}
Проверить работу кода включения доступности второй вкладки можно с помощью обработчика кнопки Find первой вкладки. В реальном приложении вкладка Result должна стать доступной лишь после того, как фактически выполненный поиск увенчается успехом. В нашем случае вызов кода разрешения и запрещения доступа к вкладке существенно упрощен.
Класс CFindSheet является родительским по отношению к классам вкладок CFindPage и CResultPage. Мы создадим обработчик кнопки Find первой вкладки, в котором получим указатель на объект родительского класса, а потом адресуемся к проверяемой функции EnablePage() родительского класса.
Откройте панель Resource View оболочки и вызовите на редактирование ресурс IDD_PROPPAGE_FIND
Выделите кнопку
Наполните созданную функцию следующим кодомОбработчик кнопки Find
void CFindPage::OnBnClickedButtonFind()
{
// TODO: Add your control notification handler code here
CFindSheet *pParentSheet = (CFindSheet*)GetParent();
ASSERT(pParentSheet->IsKindOf(RUNTIME_CLASS(CFindSheet)));
pParentSheet->EnablePage(1);// Вторая вкладка
}
В начало файла FindPage.cpp добавьте директиву #include, подключающую к этому файлу заголовочный файл с описанием класса CFindSheet, используемого в обработчикеПодключение заголовочного файла FindSheet.h к файлу с обработчиком кнопки FindPage.cpp
// FindPage.cpp : implementation file
//
#include "stdafx.h"
#include "ModalDemo.h"
#include "FindPage.h"
#include ".\findpage.h"
#include "FindSheet.h"
// CFindPage dialog
IMPLEMENT_DYNAMIC(CFindPage, CPropertyPage)
CFindPage::CFindPage()
: CPropertyPage(CFindPage::IDD)
{
}
CFindPage::~CFindPage()
{
}
..................................................
Постройте приложение и убедитесь, что вкладка Result становится доступной после щелчка по кнопке Find В зависимости от результатов поиска по команде из первой вкладке нужно изменить заголовок и доступность второй вкладки. Количество найденных книг для простоты свяжем с индексом элемента в раскрывающемся списке интерфейса первой вкладки. Выбранный индекс списка nHint при нажатии кнопки Find будет передаваться в функцию CFindSheet::SetResult(nHits), которая установит заголовок и доступность второй вкладки Result. Функция будет в большей части повторять код предыдущей функции CFindSheet::SetEnabledText(int iPage) .
В начало файла FindSheet.cpp добавьте два макроопределенияМакроопределения в начале файла FindSheet.cpp // FindSheet.cpp : implementation file // #include "stdafx.h" #include "ModalDemo.h" #include "FindSheet.h" #include ".\findsheet.h" #define RESULT_TAB_INDEX 1 #define RESULT_TAB_CAPTION "Result: %ld books found" ......................................................
Для класса
Нажмите кнопку Add, затем кнопку Finish
Мастер добавит заготовку в конец файла FindSheet.cpp, а в описание класса добавит прототип функции.
Наполните созданную функцию следующим кодомФункция SetResult()
// Изменение содержимого и доступности заголовка вкладки
void CFindSheet::SetResult(int nHits)
{
// Изменение текста заголовка вкладки
CTabCtrl *pTab = GetTabControl();
ASSERT(pTab);
TC_ITEM ti;
char szText[100];
ti.mask = TCIF_TEXT;
ti.pszText = szText;
ti.cchTextMax = 100;
VERIFY(pTab->GetItem(RESULT_TAB_INDEX, ti));
sprintf(szText, RESULT_TAB_CAPTION, nHits);
VERIFY(pTab->SetItem(RESULT_TAB_INDEX, ti));
// Управление доступностью вкладки
if(nHits <= 0)
DisablePage(RESULT_TAB_INDEX, -1);
else
EnablePage(RESULT_TAB_INDEX);
}
Перейдите в файл FindPage.cpp и найдите обработчик кнопки Find, который измените такИзменение обработчика кнопки Find
void CFindPage::OnBnClickedButtonFind()
{
// TODO: Add your control notification handler code here
CFindSheet *pParentSheet = (CFindSheet*)GetParent();
ASSERT(pParentSheet->IsKindOf(RUNTIME_CLASS(CFindSheet)));
// pParentSheet->EnablePage(1);// Вторая вкладка
// Демонстрация управления вкладкой Result по индексу списка
int iIndex = m_cboLanguages.GetCurSel();
pParentSheet->SetResult(iIndex);
}
Постройте приложение и убедитесь, что:Все работает, как мы задумали. Внешний вид приложения должен быть примерно таким

Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.