Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

CSplitterWndEx: Indicating Splitter Window Focus, and Automatic Splitting

0.00/5 (No votes)
13 Jan 2000 1  
A tutorial that shows how to automatically split a view, and also how to indicate which view has the focus

Sample Image - AutomaticSplitter.jpg

Introduction

I'm developing a CAD/CAM application with MFC using the Document/View framework. In this application, the C<MyProject>View contains a 3D view with 3D objects. In the CAD/CAM industry, we need different points of views for the 3D Window, so I implemented an Automatic Split function, which tiles the 3D views in 4 panes: Front, Top, Left and Axonometric View.

Unfortunately, I cannot give a sample of the CAD/CAM Application (because it is using third party libraries), but you will find a simple MFC project showing the Automatic split capability, and the focus.

This tutorial explains how to set the focus on the current pane of a Splitted View and how to automatically split a View (without giving the position of the splitterbar).

Note: For the focus, I’m using code that I found somewhere on the Internet or in a book, but I cannot remember where. If you have written this code, contact me and I will give you credits for your work.

1. Creating the Project

You can use the MFC Application wizard to generate the code of your project:

  • Open Visual C++.
  • Select File | New | MFCAppWizard (EXE).
  • Give a Name (MyProj) and a path.
  • Keep the default options (using direct Finish).

Your project is done; you can compile it and run it.

If you run it, you can see in the Window menu, there is no split option.

2. Adding the Splitter Bars

To do this, you should use the Component Gallery:

  • Select Project | Add To Project | Components and Controls.
  • Go in Visual C++ Component and select SplitterBar.
  • Select Both / OK and Close.

Now, if you see the CChildFrame class, you have the OnCreateClient methods, which creates the SplitterWnd. Open your Resource View and in the Menu IDR_MYPROJTYPE, add under Window the entry &Split with the ID ID_WINDOW_SPLIT.

Build the executable, and test the "Split" function (it should work).

3. Creating a New Class which Inherits from CSplitterWnd

  • Select Insert | New Class
  • Class Type : MFC
  • Name: CSplitterWndEx
  • BaseClass: Generic CWnd
  • Edit the files SplitterWnd.h and SplitterWnd.cpp, and replace all the CWnd objects by CSplitterWnd objects
  • Edit ChildFrm.h, and insert #include "SplitterWndEx.h" in the top of the include file, and replace:
    CSplitterWnd m_wndSplitter;

    with:

    CSplitterWndEx m_wndSplitter;

Now, you have a new class to work with.

4. Setting the Focus

Add the following 2 public methods to CSplitterWndEx:

void OnDrawSplitter(CDC* pDC, ESplitType nType, const CRect& rectArg);
void RefreshSplitBars(void);

void CSplitterWndEx::OnDrawSplitter(CDC* pDC, 
    ESplitType nType, const CRect& rectArg)
{
    int x_ActivePane, y_ActivePane; 
    COLORREF hilightcolor = RGB(255,255,0);
    
    GetActivePane(x_ActivePane, y_ActivePane);
    if( ((GetRowCount()>1) ||(GetColumnCount()>1)) 
                        && (nType == splitBorder))
    {
        int pRow = 0;
        int pCol = 0;
        if(rectArg.top)
        {
            pRow = 1;
        }
        if(rectArg.left)
        {
            pCol = 1;
        }
        if((pCol == y_ActivePane) && (pRow == x_ActivePane)) 
        {
            if (pDC == NULL)
            {
                RedrawWindow(rectArg, NULL, RDW_INVALIDATE|RDW_NOCHILDREN);
                return;
            }
            ASSERT_VALID(pDC);
            CRect rect = rectArg;pDC->Draw3dRect(rect, 
                               hilightcolor, hilightcolor);
            int dx = -GetSystemMetrics(SM_CXBORDER);
            int dy = -GetSystemMetrics(SM_CYBORDER);
            rect.InflateRect(dx,dy);
            pDC->Draw3dRect(rect, hilightcolor, hilightcolor);
            return;
        }
    }
    CSplitterWnd::OnDrawSplitter(pDC,nType,rectArg);
}

This method draws an extra Rectangle around the ActivePane with the color hilightcolor.

Note: You can set 2 colors (see help for Draw3dRect).

void CSplitterWndEx::RefreshSplitBars(void)
{
    CRect rectInside;
    GetInsideRect(rectInside);
    DrawAllSplitBars(NULL, rectInside.right, rectInside.bottom);
}

This method calls the refresh for the splitbars.

You have all your methods to draw the focus window. You just need to call them:

  • Using the Class Wizard, add handlers for the messages WM_KILLFOCUS and WM_SETFOCUS to CMyProjView.
  • In both methods, write the following line:
    ((CChildFrame*)GetParentFrame())->m_wndSplitter.RefreshSplitBars();
  • In the CMyProjView.cpp, add the include: #include "ChildFrm.h"
  • Go to the CChildFrame include file, and change the access to:
    CSplitterWndEx m_wndSplitter;
    from protected to public.
  • Build the project, and run it.

Select Split in the Window menu. When you click a pane, a yellow border is drawn. It shows which pane has the focus.

Automatic Splitting

The Automatic-Splitting is the capability to split a view in 4 views, but without mouse event. It means that the CView is split into 4 views where newWidth = oldWidth / 2 and newHeight = oldHeight / 2.

First, add to the CSplitterWnd class 2 booleans:

Public:
    bool m_bSplittingDone;
protected :
    bool m_bIsAutomaticSplit;

In the constructor, initialize these members:

CSplitterWndEx::CSplitterWndEx()
{
    m_bIsAutomaticSplit = false;
    m_bSplittingDone = false;
}

To call the splitting function, you need to use CSplitterWnd::DoKeyboardSplit();.

We write 4 new methods to the class:

public:
    BOOL DoAutomaticSplit();          // new method
    BOOL DoKeyboardSplit();           // overload the CSplitterWnd::DoKeyboardSplit();
protected:
    void StartTracking(int ht);       // overload the CSplitterWnd::StartTracking
    void StopTracking(BOOL bAccept);  // overload the CSplitterWnd::StopTracking

void CSplitterWndEx::StartTracking(int ht)
{
    //save the current cursor ...
    HCURSOR theCurrentCursor = GetCursor();

    CSplitterWnd::StartTracking(ht); 

    if ( m_bIsAutomaticSplit )
    {
        //...and restore it immediately if in AutomaticSplit mode
        SetCursor(theCurrentCursor);
    }
}

void CSplitterWndEx::StopTracking(BOOL bAccept)
{
    CSplitterWnd::StopTracking(bAccept);

    //now the is splitting done
    m_bIsAutomaticSplit = false;
    m_bSplittingDone = true;
}

BOOL CSplitterWndEx::DoAutomaticSplit()
{
    //save the current mouse position
    POINT theInitialMousePosition;
    GetCursorPos(&theInitialMousePosition);

    //set the splitting done to false ( of course )
    m_bSplittingDone = false;

    //and automatic to true 
    m_bIsAutomaticSplit = true;

    //do the split
    BOOL RetVal = CSplitterWnd::DoKeyboardSplit();

    //restore immediately the old mouse position
    SetCursorPos(theInitialMousePosition.x,theInitialMousePosition.y);
    return RetVal;
}

BOOL CSplitterWndEx::DoKeyboardSplit()
{
    m_bSplittingDone = false;
    m_bIsAutomaticSplit = false;
    return CSplitterWnd::DoKeyboardSplit();
}

We also need to overload the OnMouseMove event:

void CSplitterWndEx::OnMouseMove(UINT nFlags, CPoint pt)
{
    if ( m_bIsAutomaticSplit )
    {
        //if AutomaticSplit mode : We are not able 
        //to choose the position of the splitters
        // -> Exiting to next step
        StopTracking(TRUE);
        return;
    }
    CSplitterWnd::OnMouseMove(nFlags,pt);
}

Note, you will need to call the DoAutomaticSplit method. You can call it in the CChildFrame class:

void CChildFrame::AutomaticSplit()
{
    // set the timer ( just a trick make an action once the splitting is done )
    KillTimer(1);  // Destroy the old timer
    SetTimer(1, 10 , NULL); // Creates a new timer (index = 1 ; time = 10ms)

    m_wndSplitter.DoAutomaticSplit();
}

void CChildFrame::OnTimer(UINT nIDEvent) 
{
    if ( m_wndSplitter.m_bSplittingDone ) // if the splitting is done
    {
        KillTimer(1); // kill the timer
        // ( just a trick make an action once the splitting is done ) 

        //loop on the row & columns
        int nbRow = m_wndSplitter.GetRowCount();
        int nbCol = m_wndSplitter.GetColumnCount(); 
        for ( int r = 0; r < nbRow ; r++ ) 
        { 
            for  ( int c=0  ; c <  nbCol ; c++ )
            {
                CMyProjView* theView = (CMyProjView*)m_wndSplitter.GetPane(r,c);
                if ( r==0 && c== 0)
                    //top-lefttheView->DoSomething(RGB(255,0,0));
                else if ( r==0 && c== 1)
                    //top-righttheView->DoSomething(RGB(0,255,0));
                else if ( r==1 && c== 0)
                    //bottom-lefttheView->DoSomething(RGB(0,0,255));
                else if ( r==1 && c== 1)
                  //bottom-righttheView->DoSomething(RGB(255,255,0));
            }
        }
    }
}

Here, I’m using a trick with a timer to do something when the splitting is done.

Now, you can call the CChildFrame::AutomaticSplit() method to split the CView.

For example, with the Class Wizard, you can create a new entry in the Window menu, and add the call to AutomaticSplit:

void CChildFrame::OnWindowAutomaticsplit() 
{
    AutomaticSplit();
}

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here