Rogue Wave banner
Previous fileTop of DocumentContentsIndex pageNext file
Objective Grid User's Guide
Rogue Wave web site:  Home Page  |  Main Documentation Page

19.2 Process

  1. Generate a default SDI doc/view or MDI doc/view based skeleton application with the MFC AppWizard.

  2. Include grid\gxall.h in stdafx.h.

  3. Add "grid\gxresrc.h" and "grid\gxres.rc" to the Resource Includes dialog.

  4. Add a call to the GXInit() function call in the InitInstance() function of the CWndApp-derived class of the application.

  5. Add grid-related data to your document class. Remember to initialize the m_pParam pointer to NULL in the constructor of the document class.

  6. Change the base class for your view class from CView to CGXGridView. The following steps are specific to adding dynamic splitter support.

  7. 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;
      …
      
  8. 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;
      }
      
  9. 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();
      }
      
  10. 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();
           }
      }
      
  11. 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;
      }
      
  12. Now, delegate the OnDraw() function to that of CGXGridView class.

      void CGridDSplitView::OnDraw(CDC* pDC)
      {
           CGXGridView::OnDraw(pDC);
      }
      
  13. 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.

  14. 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;
      }
      
  15. 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.



Previous fileTop of DocumentContentsNo linkNext file

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.