Создание MFC Property Sheet изменяемого размера
Иногда для приложения требуется большей
гибкости в использовании property sheet, чем может
предоставить Microsoft Foundation Classes (MFC). В данной статье,
демонстрируются шаги, по созданию класса property
sheet, который так же содержит полоску меню.
Приведённые ниже инструкции, показывают, как
добавить новый класс property sheet в существующий
проект MFC и настроить окошко, обработав запросы
на изменение размера, а так же добавив меню. В
галвной роли будет выступать класс, CMyPropertySheet,
который можно использовать для воплощения
нашего замысла. Этот класс мог бы стать достойной
заменой для MFC-шного CPropertySheet так как
обеспечивает дополнительную функциональность,
описанную выше.
- В проекте, основанном на MFC, добавление нового
property sheet осуществляется при помощи галереи
компонентов. Для этого в меню Project кликните на
Add to project, а затем на Component and Controls. В
появившемся диалоговом окошке сделайте двойной
щелчёк на папку "Components", а затем выберите Property
Sheet.
- Для того, чтобы сделать рамку изменяемого
размера, необходимо переопределить DoModal() и
создать callback функцию, которая установит
соответствующие стили для окошка свойств (property
sheet). Поэтому, необходимо добавить следующие две
функции (как в заголовочном файле, так и в файле
исходника для класса CMyPropertySheet.):
- Статическая callback функция окошка свойств, XmnPropSheetCallback():
// Эта функция должна быть STATIC.
//Callback позволяет нам установить стили окна поумолчанию
// для property sheet
int CALLBACK CMyPropertySheet::XmnPropSheetCallback(HWND hWnd,
UINT message, LPARAM lParam)
{
extern int CALLBACK AfxPropSheetCallback(HWND, UINT message, LPARAM lParam);
// XMN: Вызывает MFC-шный callback
int nRes = AfxPropSheetCallback(hWnd, message, lParam);
switch (message)
{
case PSCB_PRECREATE:
// Устанавливаем наши собственные стили окна
((LPDLGTEMPLATE)lParam)->style |= (DS_3DLOOK | DS_SETFONT
| WS_THICKFRAME | WS_SYSMENU | WS_POPUP | WS_VISIBLE | WS_CAPTION);
break;
}
return nRes;
}
- Переопределение DoModal():
// Переопределение DoModal() позволяет нам перехватить наш callback
// при создании окошка свойств
int CMyPropertySheet::DoModal()
{
// Ловушка в коде создания property sheet
AFX_OLDPROPSHEETHEADER* psh = GetPropSheetHeader();
psh->dwFlags |= PSH_USECALLBACK;
psh->pfnCallback = XmnPropSheetCallback;
return CPropertySheet::DoModal();
}
- Теперь мы имеем property sheet, размеры которого можно
изменять при помощи мышки. Чтобы добавить меню,
Вам необходимо переопределить OnInitDialog(). В
меню View выберите Class Wizard, затем в
диалоговом окне Class Wizard выберите проект и
класс для property sheet. Далее в выпадающем списке Messages
выберите пункт OnInitDialog. Приведённый ниже код,
показывает, как должно выглядеть
переопределение OnInitDialog():
BOOL CMyPropertySheet::OnInitDialog()
{
BOOL bResult = CPropertySheet::OnInitDialog();
// Добавляем новое меню
CMenu *pMenu = new CMenu; pMenu->LoadMenu(IDR_PS_MENU);
SetMenu(pMenu);
// Корректируем размер propsheet под новое меню
CRect r; GetWindowRect(&r);
r.bottom += GetSystemMetrics(SM_CYMENU);
MoveWindow(r);
return bResult;
}
- Теперь нам необходимо обеспечить политику
изменения размеров. Наиболее правильный способ
сделать это, заключается в том, чтобы изменить
размеры внедрённого контрола tab в соответствии с
запросом на изменение размеров и перемещая
кнопки, присутствующие на окне. Для этого
проделаем следующее:
- В класс CMyPropertySheet добавьте следующие
переменные:
protected:
BOOL m_bNeedInit;
CRect m_rCrt;
int m_nMinCX;
int m_nMinCY;
- Инициализируйте эти переменные и установите
m_bNeedInit в TRUE в конструкторе CMyPropertySheet:
CMyPropertySheet::CMyPropertySheet(CWnd* pWndParent)
: CPropertySheet(IDS_PROPSHT_CAPTION, pWndParent)
, m_bNeedInit(TRUE)
, m_nMinCX(0)
, m_nMinCY(0)
{
AddPage(&m_Page1);
AddPage(&m_Page2);
}
- Добавьте следующие строки в конец OnInitDialog():
BOOL CMyPropertySheet::OnInitDialog()
{
// ...
// Инициализируем m_nMinCX/Y
m_nMinCX = r.Width();
m_nMinCY = r.Height();
// After this point we allow resize code to kick in
m_bNeedInit = FALSE;
GetClientRect(&m_rCrt);
return bResult;
}
- Делаем обработчики для события WM_SIZE. Для этого в
меню View кликаем Class Wizard, а затем в
диалоговом окошке Class Wizard выбираем проект и
класс окна свойств (property sheet). Далее, в выпадающем
списке Messages выбираем пункт WM_SIZE. Ниже
показана реализаци обработчика сообщения:
// Обрабатываем событие WM_SIZE путём изменения размера контрола tab
// и перемещая все кнопки на property sheet.
void CMyPropertySheet::OnSize(UINT nType, int cx, int cy)
{
CRect r1;
CPropertySheet::OnSize(nType, cx, cy);
if (m_bNeedInit)
return;
CTabCtrl *pTab = GetTabControl();
ASSERT(NULL != pTab && IsWindow(pTab->m_hWnd));
int dx = cx - m_rCrt.Width();
int dy = cy - m_rCrt.Height();
GetClientRect(&m_rCrt);
HDWP hDWP = ::BeginDeferWindowPos(5);
pTab->GetClientRect(&r1);
r1.right += dx; r1.bottom += dy;
::DeferWindowPos(hDWP, pTab->m_hWnd, NULL,
0, 0, r1.Width(), r1.Height(),
SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
// Перемещаем все кнопки с нижних правых сторон
for (CWnd *pChild = GetWindow(GW_CHILD);
pChild != NULL;
pChild = pChild->GetWindow(GW_HWNDNEXT))
{
if (pChild->SendMessage(WM_GETDLGCODE) & DLGC_BUTTON)
{
pChild->GetWindowRect(&r1); ScreenToClient(&r1);
r1.top += dy; r1.bottom += dy; r1.left+= dx; r1.right += dx;
::DeferWindowPos(hDWP, pChild->m_hWnd, NULL,
r1.left, r1.top, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
}
// Ресайзим всё остальное...
else
{
pChild->GetClientRect(&r1);
r1.right += dx; r1.bottom += dy;
::DeferWindowPos(hDWP, pChild->m_hWnd, NULL, 0, 0,
r1.Width(), r1.Height(),
SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
}
}
::EndDeferWindowPos(hDWP);
}
- В заключении, Вам необходимо создать обработчик
сообщения WM_GETMINMAXINFO. Для этого в меню View
кликаем Class Wizard, а затем в диалоговом окошке Class
Wizard выбираем проект и класс окна свойств (property
sheet). Далее, в выпадающем списке Messages выбираем
пункт WM_GETMINMAXINFO. Ниже приведена реализация
обработчика WM_GETMINMAXINFO:
void CMyPropertySheet::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
CPropertySheet::OnGetMinMaxInfo(lpMMI);
lpMMI->ptMinTrackSize.x = m_nMinCX;
lpMMI->ptMinTrackSize.y = m_nMinCY;
}
ССЫЛКИ
Для получения дополнительной информации, можно
посмотреть следующие статьи из Microsoft Knowledge Base:
Q143291 Как изменить размер CPropertyPages
во время выполнения
Q146916 Как создать
немодальный CPropertySheet со стандартными
кнопками
|