Click here to Skip to main content
Click here to Skip to main content

splitter control for dialog

, 23 May 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
a splitter control for dialog can auto change the linked window's position.

Introduction

Yes, that is another-another splitter control. This control based the control of this(Another splitter control for dialog). I have take some changes of the origin code to make it much more easy to use. The new splitter-control can auto change the linked control's pos automatically, when the user change the split-bar. That it is. 

Background

I'm working for a GUI application which have many dialogs. But the developer before me make all of the dialogs fixed size. Some times that is ok. But the others will make the interface be had to use. I have to move the scroll bar to see the part which be hide by the fixed sized dialog. When I have a chance to modify it. I decide to make some change. From that time on, I looking for a splitter control which can be used in the dialog. 

There are many kinds of splitter-control in CodeProject. I'm like the one which write by Hung Nguyen very much: Another splitter control for dialog. The commit dialog of the svn use that one too. when I used this control in many of my application. And I found it is have some small problem that I have to call ChangePos function in many place. In other words, it can not move the relation control automatically. So I make a new one to solve this problem. 

Using the code 

Step 1 Add a picture control on your dialog at the resource editor.

Put a picture control on your dialog, give it id IDC_SPLITTER1. Change the size of the control to make it look like a horizontal bar. Double click the control of IDC_SPLITTER1, change the property as below. And then add a vertical one as the same way, give it id IDC_SPLITTER2. 

Actually, all the operate above is just want to make us not calc the spliter size. You can specify the size of the splitter by the CSplitterControl::Create function.

Step 2 Add splitters to the dialog class.

Add SplitterControl.h and SplitterControl.cpp to your project. Insert #include "splittercontrol.h" to the .h file of the dialog class.

And then, add member varibles:

private:
    CSplitterControl    m_wndSplitter1;
    CSplitterControl    m_wndSplitter2;

In the function of OnInitDialog, create the splitters.

There are some notices here:

> Use SPS_VERTICAL or SPS_HORIZONTAL to special the splitter style.

> You can special a RGB color(the default id RGB(120, 120, 120)) for the splitter line.

> You can special the splitter line width(the default is 1 ).

> The width(SPS_VERTICAL) or height(SPS_HORIZONTAL) of the splitter depend on the width of rect when you call Create function.

BOOL CSplitterControlDemoDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    //  Here, I ignore some code we not care about.
    //  You can reference the sample code for detial.

    CRect rc;
    CWnd* pWnd;

    pWnd = GetDlgItem(IDC_SPLITTER1);
    pWnd->GetWindowRect(rc);
    ScreenToClient(rc);
    BOOL bRet = m_wndSplitter1.Create(WS_CHILD | WS_VISIBLE, rc, this, IDC_SPLITTER1, SPS_VERTICAL|SPS_DELTA_NOTIFY);//, RGB(255, 0, 0));
    if (FALSE == bRet)
    {
        AfxMessageBox("m_wndSplitter1 create failed");
    }

    pWnd = GetDlgItem(IDC_SPLITTER2);
    pWnd->GetWindowRect(rc);
    ScreenToClient(rc);
    bRet = m_wndSplitter2.Create(WS_CHILD | WS_VISIBLE, rc, this, IDC_SPLITTER2, SPS_HORIZONTAL, RGB(0, 0, 255));
    if (FALSE == bRet)
    {
        AfxMessageBox("m_wndSplitter2 create failed");
    }


Step 3 Add linked windows.

The splitter can move the linked window pos automaticly as the user change the splitter's pos. So the we should specify which window need change the pos.CSplitterControl::RegisterLinkedWindow function take the work. Check the example below.

    //  register windows for splitter
    this->m_wndSplitter1.RegisterLinkedWindow(SPLS_LINKED_LEFT,     GetDlgItem(IDC_TREE));
    this->m_wndSplitter1.RegisterLinkedWindow(SPLS_LINKED_RIGHT,    GetDlgItem(IDC_LIST));
    this->m_wndSplitter1.RegisterLinkedWindow(SPLS_LINKED_RIGHT,    GetDlgItem(IDC_EDIT));
    this->m_wndSplitter1.RegisterLinkedWindow(SPLS_LINKED_RIGHT,    &m_wndSplitter2);

    this->m_wndSplitter2.RegisterLinkedWindow(SPLS_LINKED_UP,       GetDlgItem(IDC_LIST));
    this->m_wndSplitter2.RegisterLinkedWindow(SPLS_LINKED_DOWN,     GetDlgItem(IDC_EDIT));

    //  relayout the splotter to make them good look
    this->m_wndSplitter1.Relayout();
    this->m_wndSplitter2.Relayout();

Remember that SPLS_LINKED_LEFT means the control is on the left side of the splitter. SPLS_LINKED_RIGHT means the right. The two used for the splitter of vertical. If the control linked to a splitter of vertical with SPLS_LINKED_LEFT, that means the right pos of the control will be changed by the splitter. SPLS_LINKED_RIGHT is similar with SPLS_LINKED_LEFT.

SPLS_LINKED_UP and SPLS_LINKED_DOWN as the name means the control will be at the up side of a horizontal splitter. 

We almost finished, after we have linked the controls to the splitter. In order to make the interface looks like better. we should call CSplitterControl::Relayout function to make the initializing layout. you can call CSplitterControl::Relayout function at any time you need.

Step 4 Splitter's limit pos.

Usualy, we need set the splitter's moving range. That is not very important in the Document-View based application. But in the dialog based application, we have to process the window's edge ourself. so, the limit pos of the splitter is very important for dialog based application. 

In the sizabled dialog, the limit pos of the splitter is not the fixed. Once we change the dialog size. We have to change the new limit pos of the splitter. That is not very good. In my splitter control, they send a notify message to the parent window before you ready to change the splitter pos every time. so if you want to use this set the limit pos, just handle the notify message. The notify message is named SPN_MAXMINPOS. There are some sample code here:

Add message handle function in the .h file of the dialog class for the notify message(SPN_MAXMINPOS):

    afx_msg void OnMaxMinInfo(NMHDR* pNMHDR, LRESULT* pResult);

Map the message:

BEGIN_MESSAGE_MAP(CSplitterControlDemoDlg, CDialog)
    //{{AFX_MSG_MAP(CSplitterControlDemoDlg)
    //  ...
    ON_NOTIFY(SPN_MAXMINPOS, IDC_SPLITTER2, OnMaxMinInfo)
    ON_NOTIFY(SPN_MAXMINPOS, IDC_SPLITTER1, OnMaxMinInfo)
    //  ...
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

Implement the message handle function:

void CSplitterControlDemoDlg::OnMaxMinInfo(NMHDR* pNMHDR, LRESULT* pResult)
{
    //  Get current pos of the child controls
    CRect rcTree;
    CRect rcList;
    CRect rcEdit;
    CRect rcCancel;
    m_wndType.GetWindowRect(rcTree);
    m_lstItem.GetWindowRect(rcList);
    m_txtContent.GetWindowRect(rcEdit);
    m_btnCancel.GetWindowRect(rcCancel);
    
    this->ScreenToClient(rcTree);
    this->ScreenToClient(rcList);
    this->ScreenToClient(rcEdit);
    this->ScreenToClient(rcCancel);
    
    //  return the pos limit
    SPC_NM_MAXMINPOS* pNewMaxMinPos = (SPC_NM_MAXMINPOS*)pNMHDR;
    if (IDC_SPLITTER1 == pNMHDR->idFrom)
    {
        pNewMaxMinPos->lMin = rcTree.left + 50;
        pNewMaxMinPos->lMax = rcCancel.left - STD_GAP;
    }
    else
    {
        pNewMaxMinPos->lMin = rcList.top + 50;
        pNewMaxMinPos->lMax = rcEdit.bottom - 50;
    }
}

Step 5 Some special used method

Not everything be automaticly is ok. The splitter need us to register all of the linked controls to it when the dialog initializing. so if a control is not created at that time, we can not register it to the splitter. So the splitter proveded a another way to change the pos of these controls. The kernel function is CSplitterControl::ChangePos. This function accept a parameter dwLinkedSide to specify the control is which side of the splitter. And the lDelta usually is from the notify message SPN_DELTA.

The SPN_DELTA notify message is sent when the user release the mouse. It is defined in the SplitterControl.h.

//  Notify event : tell the parent to do some special things
//      some times, the parent window can not register the child control for reason it does not created yet.
//      so, SPN_DELTA event give the parent window a chance to change the child control's pos.
#define SPN_DELTA           (WM_USER + 2)
struct SPC_NM_DELTA
{
    NMHDR   hdr;
    LONG    lDelta;
};

There are some sample code to show how to use this notify message:

firstly, we need use the SPS_DELTA_NOTIFY style as we create the splitter.

    BOOL bRet = m_wndSplitter1.Create(WS_CHILD | WS_VISIBLE, rc, this, IDC_SPLITTER1, SPS_VERTICAL|SPS_DELTA_NOTIFY);//, RGB(255, 0, 0));

And then, add message maps:

BEGIN_MESSAGE_MAP(CSplitterControlDemoDlg, CDialog)
    //  ...
    ON_NOTIFY(SPN_DELTA,     IDC_SPLITTER1, OnSplitter1Delta)
    //  ...
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

At last, implement it:

void CSplitterControlDemoDlg::OnSplitter1Delta(NMHDR* pNMHDR, LRESULT* pResult)
{
    //  this function just want to show you how to use the delta event
    *pResult = 0;

    SPC_NM_DELTA* pDelta = (SPC_NM_DELTA*)pNMHDR;
    if (NULL == pDelta)
    {
        return;
    }

    m_wndSplitter1.ChangePos(&m_edHelp, SPLS_LINKED_LEFT, pDelta->lDelta);
}

Thanks

Thanks of Hung Nguyen and his(or her) article Another splitter control for dialog and the demo project. That help me a lot, and my sample code is base on his too. Oh,I'm so lazy.

Points of Interest

The sample way is alway exist.

History

2013-05-19 : first version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

libbyliugang
Software Developer
China China
No Biography provided

Comments and Discussions

 
QuestionIt's already posted before Pinmemberstevehoang22-Oct-14 12:29 
QuestionI have a question PinmemberMember 1074734920-Aug-14 19:05 
GeneralNot Work PinmemberMember 1020599321-Aug-13 7:24 
QuestionMy Vote of 5,Just what I need in MFC! Pinmemberlanmanck15-Aug-13 2:42 
GeneralJust what i needed PinmemberMember 1017340525-Jul-13 13:58 
GeneralMy vote of 5 PinmemberMihai MOGA13-Jun-13 21:44 
QuestionLooks very simple to use Pinmember.dan.g.22-May-13 17:12 
AnswerRe: Looks very simple to use Pinmemberlibbyliugang23-May-13 1:51 
QuestionGreat work ! Pinmemberjustdownloads22-May-13 0:55 
QuestionNot able to download demo Pinmemberp.uday kishore20-May-13 22:05 
AnswerRe: Not able to download demo PinprofessionalMarco Bertschi20-May-13 22:30 
GeneralRe: Not able to download demo Pinmemberp.uday kishore20-May-13 22:36 
AnswerRe: Not able to download demo Pinmemberlibbyliugang23-May-13 1:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141223.1 | Last Updated 23 May 2013
Article Copyright 2013 by libbyliugang
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid