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 here m_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 { // Construction public: CxSplitterWnd() {}; virtual ~CxSplitterWnd() {}; // Operations public: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CxSplitterWnd) //}}AFX_VIRTUAL // Implementation public: // These are the methods to be overridden virtual 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 functions protected: //{{AFX_MSG(CxSplitterWnd) // NOTE - the ClassWizard will add and remove member functions here. //}}AFX_MSG DECLARE_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_FILE static 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 keyboard vSplitterBar1 = 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_MAP END_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 splitter if (pView != NULL && !IsChildPane(pView, pRow, pCol)) pView = NULL; return pView; } void CxSplitterWnd::SetActivePane( int row, int col, CWnd* pWnd) { // set the focus to the pane CWnd* 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 appropriate GetInsideRect(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 it m_rectTracker.OffsetRect(0, m_rectLimit.Height()/2); m_rectTracker2.OffsetRect(m_rectLimit.Width()/2, 0); } else { // only hit one bar GetHitRect(ht, m_rectTracker); } // steal focus and capture SetCapture(); SetFocus(); // make sure no updates are pending RedrawWindow(NULL, NULL, RDW_ALLCHILDREN | RDW_UPDATENOW); // set tracking state and appropriate cursor m_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 window return 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