Создание 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();
}
- Статическая callback функция окошка свойств, XmnPropSheetCallback():
- Теперь мы имеем 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;
}
- В класс CMyPropertySheet добавьте следующие переменные: