CSplitterWnd in a Dialog based Application

来源:互联网 发布:厦门java培训班 编辑:程序博客网 时间:2024/05/21 09:24

This article was contributed by Gerd Klevesaat

Download Example Project

Had this problem before. I guess there are some more interested in a solution.

It seems CSplitterWnd is designed to be used in document/view-based applications only.
But by overriding some virtual methods in a derived class, you can make splitter windows based on CSplitterWnd be used in dialog based application, ActiveX-Controls using MFC:

All virtual methods that call GetParentFrame() in its implementation have to be overridden.
I have done this by using existing code except
- that I replaced the call to GetParentFrame() by a call to GetParent().
- all references or pointers to CFrameWnd were changed to references or pointers to CWnd.

I derived a class CxSplitterWnd from the class CSplitterWnd and proceeded as stated above.
Then I used this class in a dialog based application in the same way as any other CWnd derived class.
For example:

class CSampleDialog : public CDialog{... CxSplitterWnd m_wndSplitter; .... }BOOL CSampleDlg::OnInitDialog(){ ...// TODO: Add extra initialization herem_wndSplitter.CreateStatic(this, 1, 2);m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CSampleView), CSize(50,0), NULL);m_wndSplitter.CreateView(0,1,RUNTIME_CLASS(CSampleView), CSize(0,0), NULL); CRect rect = ...;m_wndSplitter.MoveWindow(&rect);... }

The sample attached is a dialog based application and demonstrates the use of CxSplitterWnd. It does
nothing useful.

This is the new class declaration:

// SplitWnd.h : implementation file// class CxSplitterWnd : public CSplitterWnd{// Constructionpublic:CxSplitterWnd() {};virtual ~CxSplitterWnd() {}; // Operationspublic: // Overrides// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(CxSplitterWnd)//}}AFX_VIRTUAL // Implementationpublic: // These are the methods to be overriddenvirtual void StartTracking(int ht); virtual CWnd* GetActivePane(int* pRow = NULL, int* pCol = NULL);virtual void SetActivePane( int row, int col, CWnd* pWnd = NULL ); virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);virtual BOOL OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult );virtual BOOL OnWndMsg( UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult ); // Generated message map functionsprotected://{{AFX_MSG(CxSplitterWnd)// NOTE - the ClassWizard will add and remove member functions here.//}}AFX_MSGDECLARE_MESSAGE_MAP()};
And here the implementation file:
// SplitWnd.cpp : implementation file// #include "stdafx.h"#include "SplitWnd.h" #ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endif // HitTest return values (values and spacing between values is important)// Had to adopt this because it has module scope enum HitTestValue{noHit = 0,vSplitterBox = 1,hSplitterBox = 2,bothSplitterBox = 3, // just for keyboardvSplitterBar1 = 101,vSplitterBar15 = 115,hSplitterBar1 = 201,hSplitterBar15 = 215,splitterIntersection1 = 301,splitterIntersection225 = 525}; /////////////////////////////////////////////////////////////////////////////// CxSplitterWnd BEGIN_MESSAGE_MAP(CxSplitterWnd, CSplitterWnd)//{{AFX_MSG_MAP(CxSplitterWnd)// NOTE - the ClassWizard will add and remove mapping macros here.//}}AFX_MSG_MAPEND_MESSAGE_MAP() CWnd* CxSplitterWnd::GetActivePane(int* pRow, int* pCol){ASSERT_VALID(this); CWnd* pView = GetFocus();// make sure the pane is a child pane of the splitterif (pView != NULL && !IsChildPane(pView, pRow, pCol))pView = NULL; return pView;} void CxSplitterWnd::SetActivePane( int row, int col, CWnd* pWnd){// set the focus to the paneCWnd* pPane = pWnd == NULL ? GetPane(row, col) : pWnd;pPane->SetFocus();} void CxSplitterWnd::StartTracking(int ht){ASSERT_VALID(this);if (ht == noHit)return; // GetHitRect will restrict 'm_rectLimit' as appropriateGetInsideRect(m_rectLimit); if (ht >= splitterIntersection1 && ht <= splitterIntersection225){// split two directions (two tracking rectangles)int row = (ht - splitterIntersection1) / 15;int col = (ht - splitterIntersection1) % 15; GetHitRect(row + vSplitterBar1, m_rectTracker);int yTrackOffset = m_ptTrackOffset.y;m_bTracking2 = TRUE;GetHitRect(col + hSplitterBar1, m_rectTracker2);m_ptTrackOffset.y = yTrackOffset;}else if (ht == bothSplitterBox){// hit on splitter boxes (for keyboard)GetHitRect(vSplitterBox, m_rectTracker);int yTrackOffset = m_ptTrackOffset.y;m_bTracking2 = TRUE;GetHitRect(hSplitterBox, m_rectTracker2);m_ptTrackOffset.y = yTrackOffset; // center itm_rectTracker.OffsetRect(0, m_rectLimit.Height()/2);m_rectTracker2.OffsetRect(m_rectLimit.Width()/2, 0);}else{// only hit one barGetHitRect(ht, m_rectTracker);} // steal focus and captureSetCapture();SetFocus(); // make sure no updates are pendingRedrawWindow(NULL, NULL, RDW_ALLCHILDREN | RDW_UPDATENOW); // set tracking state and appropriate cursorm_bTracking = TRUE;OnInvertTracker(m_rectTracker);if (m_bTracking2)OnInvertTracker(m_rectTracker2);m_htTrack = ht;SetSplitCursor(ht);} /////////////////////////////////////////////////////////////////////////////// CSplitterWnd command routing BOOL CxSplitterWnd::OnCommand(WPARAM wParam, LPARAM lParam){if (CWnd::OnCommand(wParam, lParam))return TRUE; // route commands to the splitter to the parent frame windowreturn GetParent()->SendMessage(WM_COMMAND, wParam, lParam);} BOOL CxSplitterWnd::OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult ){if (CWnd::OnNotify(wParam, lParam, pResult))return TRUE; // route commands to the splitter to the parent frame window*pResult = GetParent()->SendMessage(WM_NOTIFY, wParam, lParam);return TRUE;} BOOL CxSplitterWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult){ // The code line below is necessary if using CxSplitterWnd in a regular dll// AFX_MANAGE_STATE(AfxGetStaticModuleState()); return CWnd::OnWndMsg(message, wParam, lParam, pResult);}

Posted: 11 May 1998