Иногда для приложения требуется большей гибкости в использовании 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; }
|