Generate a default SDI doc/view or MDI doc/view based skeleton application with the MFC AppWizard.
Include grid\gxall.h in stdafx.h.
Add "grid\gxresrc.h" and "grid\gxres.rc" to the Resource Includes dialog.
Add a call to the GXInit() function call in the InitInstance() function of the CWndApp-derived class of the application.
BOOL CGridDSplitApp::InitInstance() { AfxEnableControlContainer(); GXInit(); … |
Add grid-related data to your document class. Remember to initialize the m_pParam pointer to NULL in the constructor of the document class.
class CGridDSplitDoc : public CDocument { public: CGXGridParam* GetParam() {return m_pParam;}; void SetParam(CGXGridParam* pParam) {m_pParam = pParam;}; protected: CGXGridParam* m_pParam; … |
Change the base class for your view class from CView to CGXGridView. The following steps are specific to adding dynamic splitter support.
Add a member variable of CGXDTabWnd and CGXDSplitWnd to the CMainFrame class.
… protected: // control bar embedded members CStatusBar m_wndStatusBar; CToolBar m_wndToolBar; CGXDSplitWnd m_wndSplitter; CGXDTabWnd m_wndTab; … |
Override the OnCreateClient() virtual function of the CMainFrame class for SDI application, or override the CChildFrame class for MDI applications and implement the function as shown below. In addition, include the header file for our view class, GridDSplitView.h, at the top of the mainfrm.cpp file. After you add the include, add a forward declaration of the CGridDSplitDoc class to the top of CGridDSplitView class.
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { // Specify the run-time class for the first sheet. // Don't forget to register the document template // in InitInstance! pContext->m_pNewViewClass=RUNTIME_CLASS(CGridDSplitView); // creates the tab window VERIFY(m_wndTab.Create(this, _T("What's New?"), pContext)); // each view should associated with the same document. m_wndSplitter.m_pOwnerWnd = &m_wndTab; pContext->m_pNewViewClass=UNTIME_CLASS(CGridDSplitView); m_wndSplitter.Create(&m_wndTab, 2, 2, // TODO: adjust the number of rows, columns CSize(10, 10), // TODO: adjust the minimum pane size pContext); m_wndSplitter.ShowWindow(SW_HIDE); m_wndTab.AttachWnd(&m_wndSplitter, "Splitter Tab"); return TRUE; } |
Now, you need to add some code to the view class to support the dynamic splitter window. Override the OnInitialUpdate() virtual function in the view class.
void CGridDSplitView::OnInitialUpdate() { if(GetDocument()->GetParam() == NULL) SetParam(new CGXGridParam(), TRUE); else SetParam(GetDocument()->GetParam(), FALSE); DoInitialUpdate(); GetDocument()->SetParam(GetParam()); GetParam()->EnableUndo(FALSE); SetRowCount(100); SetColCount(20); SetValueRange(CGXRange(1,1), _T("Dynamic!")); EnableHints(); //GetParam()->SetSmartResize(FALSE); GetParam()->EnableUndo(TRUE); UpdateScrollbars(); } |
Implement the DoInitialUpdate() function used in OnInitialUpdate(). The implementation of this function is almost an exact copy of the OnInitialUpdate() method in the CGXGridView class, which is a class in the Objective Grid library. Notice that in the preceding implementation, we used some protected member variables of the CGXGridCore class. To give your view class access these variables, you need to make your view class a friend of CGXGridCore.
Let's add an empty derived class_core to the top of the DoInitialUpdate() function.
class _core: public CGXGridCore { friend class CGridDSplitView; }; void CGridDSplitView::DoInitialUpdate() { SetGridWnd(this, GetDocument(), TRUE); CWnd* pWnd = DXGetParentSplitter(this, TRUE); if(pWnd && pWnd->IsKindOf(RUNTIME_CLASS(CGXDSplitWnd))) m_pSplitterWnd = (CSplitterWnd*) pWnd; else m_pSplitterWnd = NULL; // Commment: I have move the following lines before // CGXGridCore::OnGridInitialUpdate() so that scrollbars // can be initialized correctly. if (m_pSplitterWnd || GetSharedScrollbarParentWnd()) SetScrollBarMode(SB_BOTH, gxnShared | gxnEnhanced); else SetScrollBarMode(SB_BOTH, gxnAutomatic | gxnEnhanced); OnGridInitialUpdate(); CGXView::OnInitialUpdate(); CGXGridParam* pParam = GetParam(); // check print device if (GetPrintDevice() == NULL) { // is print device in parameter object? if (pParam->GetPrintDevice() == NULL) pParam->SetPrintDevice(new CGXPrintDevice); SetPrintDevice(pParam->GetPrintDevice(), FALSE); } // if this is a new pane in a dynamic splitter window // I will initialize top row or left column if (m_pSplitterWnd != NULL) { ASSERT(m_pSplitterWnd-> IsKindOf(RUNTIME_CLASS(CGXDSplitWnd))); VERIFY(m_pSplitterWnd->IsChildPane(this, m_nSplitRow, m_nSplitCol)); ASSERT(m_nSplitRow < 2); ASSERT(m_nSplitCol < 2); if (m_nSplitRow > 0 || m_nSplitCol > 0) { // copy settings from other pane CGXGridView *pView; pView = (CGXGridView *) m_pSplitterWnd->GetPane(0, 0); ASSERT(pView != NULL); if (pView->IsKindOf(RUNTIME_CLASS(CGXGridView))) { if (m_nSplitRow > 0) { pView = (CGXGridView *) m_pSplitterWnd->GetPane(0, m_nSplitCol); ASSERT(pView != NULL); if (pView->IsKindOf(RUNTIME_CLASS(CGXGridView))) { m_nLeftCol = pView->m_nLeftCol; m_nTopRow = pView->m_nTopRow; m_bDisplayHeaderRow = FALSE; // disable smart redrawing of WM_SIZE message ((_core*)pView)->m_cxOld = ( (_core*)pView)->m_cyOld = 0; } } if (m_nSplitCol > 0) { pView = (CGXGridView *) m_pSplitterWnd->GetPane(m_nSplitRow, 0); ASSERT(pView != NULL); if (pView->IsKindOf(RUNTIME_CLASS(CGXGridView))) { m_nTopRow = pView->m_nTopRow; if (m_nSplitRow == 0) m_nLeftCol = pView->m_nLeftCol; m_bDisplayHeaderCol = FALSE; // disable smart redrawing of WM_SIZE message ((_core*)pView)->m_cxOld = ( (_core*)pView)->m_cyOld = 0; } } } } m_pSplitterWnd = NULL; UpdateScrollbars(); } } |
Implement the DXGetParentSplitter() function that is used in the DoInitialUpdate() function. This function is implemented as static.
CWnd* CGridDSplitView::DXGetParentSplitter(const CWnd *pWnd, BOOL bAnyState) { CWnd* pSplitter = pWnd->GetParent(); if (!pSplitter->IsKindOf(RUNTIME_CLASS(CGXDSplitWnd)) && !pSplitter->IsKindOf(RUNTIME_CLASS(CGXDTabWnd))) return NULL; // not a splitter if (!bAnyState) { // ignore splitters in minimized (iconic) windows while ((pWnd = pWnd->GetParent()) != NULL) if (pWnd->IsIconic()) return NULL; } return pSplitter; } |
Now, delegate the OnDraw() function to that of CGXGridView class.
void CGridDSplitView::OnDraw(CDC* pDC) { CGXGridView::OnDraw(pDC); } |
If you compile and run the application now, you will have a grid application with dynamic splitter support. If you choose the Splitter tab, you will see our dynamic splitter window at work. Two sets of scrollbars will be displayed. To hide one set of the scroll bars, you need to make a small change to your code.
Override the GetScrollBarCtrl() function of the CGXGridView class.
CScrollBar* CGridDSplitView::GetScrollBarCtrl(int nBar) const { #define _AfxGetDlgCtrlID(hWnd) ((UINT)(WORD)::GetDlgCtrlID(hWnd)) ASSERT(nBar == SB_HORZ || nBar == SB_VERT); if (GetStyle() & ((nBar == SB_HORZ) ? WS_HSCROLL : WS_VSCROLL)) { // it has a regular windows style scrollbar // (no control) return NULL; } CWnd* pParent = DXGetParentSplitter(this, TRUE); if (pParent == NULL) return NULL; // no splitter UINT nID = _AfxGetDlgCtrlID(m_hWnd); if (nID < AFX_IDW_PANE_FIRST || nID > AFX_IDW_PANE_LAST) return NULL; // not a standard pane ID // appropriate PANE id - look for sibling (splitter, or // just frame) UINT nIDScroll; if (nBar == SB_HORZ) nIDScroll = AFX_IDW_HSCROLL_FIRST + (nID - AFX_IDW_PANE_FIRST) % 16; else nIDScroll = AFX_IDW_VSCROLL_FIRST + (nID - AFX_IDW_PANE_FIRST) / 16; // return shared scroll bars that are immediate // children of splitter // return (CScrollBar*)pParent->GetDlgItem(nIDScroll); if(GetParent()->IsKindOf(RUNTIME_CLASS(CGXDTabWnd))) { // appropriate PANE id - look for sibling // (splitter, or just frame) UINT nIDScroll; if (nBar == SB_HORZ) nIDScroll = AFX_IDW_HSCROLL_FIRST; else nIDScroll = AFX_IDW_VSCROLL_FIRST; return (CScrollBar*) pParent->GetDlgItem(nIDScroll); } CScrollBar* pBar = (CScrollBar*) pParent->GetDlgItem(nIDScroll); // check one parent up if(pBar == NULL) pBar = (CScrollBar*) pParent->GetParent()->GetDlgItem(nIDScroll); return pBar; } |
Now, the dynamic splitter window should only have one set of scrollbars. You can use mouse to drag the splitter box to split the window in the Splitter tab window.
Copyright © Rogue Wave Software, Inc. All Rights Reserved.
The Rogue Wave name and logo, and Stingray, are registered trademarks of Rogue Wave Software. All other trademarks are the property of their respective owners.
Provide feedback to Rogue Wave about its documentation.