Сообщений 3    Оценка 276 [+1/-0]         Оценить  
Система Orphus

Использование фреймов внутри диалоговых окон

Или “как создать сплиттер в диалоге ?”

Автор: Артамонов Дмитрий
Опубликовано: 26.01.2004
Исправлено: 10.12.2016
Версия текста: 1.0.1

Предисловие
Приступаем к работе
STATIC контрол
Создание фрейма
Перенаправление сообщений
Сплиттеры внутри диалога
Заключение

Демонстрационный проект

Предисловие

Представьте себе ситуацию – вы пишете MDI или SDI приложение. Уже готовы представления, выловлены все явные ошибки и всё работает. Но вам ставят задачу (начальник или, быть может, вы сами) реализовать ту же функциональность, но внутри диалогового окна. Как можно решить проблему ? Одним из вариантов решения будет создание диалогового дубля уже готового кода. Однако дублирование кода вносит множество неприятных проблем, главной из которой будет одновременная поддержка двух версий кода. Изложенный в этой статья метод позволяет задачу решить намного проще. Кроме того, этот приём позволяет без лишних усилий добавить сплиттеры внутрь диалогового окна.

Приступаем к работе

Итак, мы имеем некоторое представление. Оно является потомком MFC класса CView. Имеем диалоговое окно, в котором собираемся отображать это представление. Для этого нужно сделать следующее:

  1. Размещаем внутри диалога невидимый STATIC контрол. Он будет нам нужен, чтобы установить размер и положение области для отображение потомка CView. Без этого можно обойтись и выставить размер и положение “вручную”
  2. Добавляем в код диалога обработчики OnCreate, OnInitDialog, OnSize
  3. Перенаправляем некоторые сообщения внутрь CView c помощью PreTranslateMessage (например для того чтобы перехватывать нажатие клавиши отправлять его в CView )

STATIC контрол

Тут никаких сложностей. Пусть, для определённости, 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()
};
Реализация “CDialog1.cpp”
        #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]         Оценить