Сообщений 3 Оценка 276 [+1/-0] Оценить |
Представьте себе ситуацию – вы пишете MDI или SDI приложение. Уже готовы представления, выловлены все явные ошибки и всё работает. Но вам ставят задачу (начальник или, быть может, вы сами) реализовать ту же функциональность, но внутри диалогового окна. Как можно решить проблему ? Одним из вариантов решения будет создание диалогового дубля уже готового кода. Однако дублирование кода вносит множество неприятных проблем, главной из которой будет одновременная поддержка двух версий кода. Изложенный в этой статья метод позволяет задачу решить намного проще. Кроме того, этот приём позволяет без лишних усилий добавить сплиттеры внутрь диалогового окна.
Итак, мы имеем некоторое представление. Оно является потомком MFC класса CView. Имеем диалоговое окно, в котором собираемся отображать это представление. Для этого нужно сделать следующее:
Тут никаких сложностей. Пусть, для определённости, ID этого статика будет IDC_DLGFRAME.
Фрейм (CFrameWnd, место в котором мы будем размещать нужный нам CView) создаётся в 2 этапа. На первом этапе происходит конструирование фрейма внутри обработчика OnCreate нашего диалога. На втором мы показываем наш фрейм (InitialUpdateFrame) и подгоняем его размеры и положение.
... protected: CFrameWnd *m_pFrame; CDocument *m_pDocument; // если нам нужен доступ к документу из представления,// можно использовать одну из Ваших реализаций документа// Generated message map functions//{{AFX_MSG(CDialog1) ... afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); virtual BOOL OnInitDialog(); afx_msg void OnSize(UINT nType, int cx, int cy); ... //}}AFX_MSG DECLARE_MESSAGE_MAP() }; |
#include “stdafx.h” #include “Dialog1.h” #include “MyView.h” // представление, которое мы хотим разместить в диалоге ... BEGIN_MESSAGE_MAP(CDialog1, CDialog) //{{AFX_MSG_MAP(CDialog1) ON_WM_CREATE() ON_WM_SIZE() //}}AFX_MSG_MAP END_MESSAGE_MAP() ... int CDialog1::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CDialog::OnCreate(lpCreateStruct) == -1) return -1; // Если нет необходимости в использовании документов в созданном фрейме,// не создавайте его, а в CCreateContext поместите NULL m_pDocument = new(CDocument); // Инициализируем контекст представления CCreateContext ccc; ccc.m_pNewViewClass = RUNTIME_CLASS(CMyView); ccc.m_pCurrentDoc = m_pDocument; ccc.m_pNewDocTemplate = NULL; ccc.m_pLastView = NULL; ccc.m_pCurrentFrame = NULL; // Т.к. CFrameWnd нуждается в оконном классе, создадим его.// Код взят из примера в MSDN к функции “AfxRegisterWndClass” CString strMyClass = AfxRegisterWndClass(CS_VREDRAW | CS_HREDRAW, ::LoadCursor(NULL, IDC_ARROW), (HBRUSH) ::GetStockObject(WHITE_BRUSH), ::LoadIcon(NULL, IDI_APPLICATION)); // Создаём фрейм, указывая в качестве родительского окна наш диалог (this) m_pFrame = new CFrameWnd; m_pFrame->Create(strMyClass,"", WS_CHILD, CRect(0,0,1,1), this, NULL, 0, &ccc ); m_pFrame->ShowWindow(SW_SHOW); m_pFrame->MoveWindow(0,0,600,600); return 0; } BOOL CDialog1::OnInitDialog() { CDialog::OnInitDialog(); m_pFrame->InitialUpdateFrame( m_pDocument, TRUE ); // здесь мы уже можем обратиться к созданному представлению: m_pFrame->GetActiveView(); SendMessage( WM_SIZE ); return TRUE; } void CDialog1::OnSize(UINT nType, int cx, int cy) { CDialog::OnSize(nType, cx, cy); // Получаем координаты нашего статика (IDC_DLGFRAME)// для того чтобы спозиционировать по нему созданный фрейм CRect cRect; CWnd *pWnd; if ( pWnd = GetDlgItem(IDC_DLGFRAME) ) { pWnd->GetWindowRect(&cRect); ScreenToClient(&cRect); m_pFrame->MoveWindow(&cRect); m_pFrame->ShowWindow(SW_SHOW); } } |
Иногда нужно обрабатывать в классе представления некоторые события, например нажатие клавиш или что-нибудь ещё. Это реализуется через перенаправление сообщений в CDialog::PreTranslateMessage. Для примера, отправим в наш CMyView все клавиши, за исключением ENTER & ESC (чтобы диалог продолжил работать так, как ожидает пользователь)
... //{{AFX_VIRTUAL(CStandartFrameDialog)public: virtual BOOL PreTranslateMessage(MSG* pMsg); ... //}}AFX_VIRTUAL |
... BOOL CStandartFrameDialog::PreTranslateMessage(MSG* pMsg) { if ( WM_KEYDOWN == pMsg->message ) { if ( ((int)pMsg->wParam == VK_RETURN) || ((int)pMsg->wParam == VK_ESCAPE) ) return CDialog::PreTranslateMessage(pMsg); } return m_pFrame->PreTranslateMessage( pMsg ); } |
Собственно, после того как был создан фрейм, добавление внутрь сплиттеров не представляет никакой сложности. Код для этого пишем в том же CDialog::OnCreate:
... CSplitterWnd m_wndSplitter; |
int CDialog1::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_wndSplitter.CreateStatic( m_pFrame, 1, 2); m_wndSplitter2.CreateStatic( &m_wndSplitter, 2, 1, WS_CHILD|WS_VISIBLE,m_wndSplitter.IdFromRowCol(0,1)); m_wndSplitter.CreateView( 0, 0, RUNTIME_CLASS( CMyView ), CSize(200,0), &ccc ); m_wndSplitter2.CreateView( 0, 0, RUNTIME_CLASS( CMyView ), CSize(0,200), &ccc ); m_wndSplitter2.CreateView( 1, 0, RUNTIME_CLASS( CMyView ), CSize(0,200), &ccc ); return 0; } |
Окно демонстрационного приложения, созданного с применением данной технологии приведено на рисунке:
Хотелось бы отметить, что автор этого метода не я, пример реализации был выкопан где-то в интернете. Надеюсь, что описанный приём окажется полезным для Вас и сэкономит время и силы, потраченные на разработку..
Сообщений 3 Оценка 276 [+1/-0] Оценить |