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

MultiPaneCtrl

, 12 Jul 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
A control that allows you to create multiple areas with tabs that you can pull with the mouse and as a result change the configuration of these areas.

Sample Image

Introduction

This control is based on the CWnd class and can be placed as a child window anywhere, for example, in the client area of a frame or dialog. The control allows you to create hierarchically nested areas, each of which may have tabs. Using the mouse, the user has the ability to move tabs with them and related child windows. This allows the user to customize the location and size of working areas to their liking. One possible use of MultiPaneCtrl is as a window filling the client area of the frame. The result is a simple, easily configurable interface.

Using the code

Each pane can be in one of two states:

  1. Be empty (do not have child panes and tabs) or have tabs.
  2. Have child panes (to be a line).

The current state is determined by the function IsLine. Many functions can be called for panes in only one of two states.

The basic rules for working with the control are:

  1. Root pane is created when you create a control and you can not delete it.
  2. In all operations, instead of a handle of the root pane (GetRoot), you can use NULL.
  3. Adding tabs and converting to a line can only be done for panes that don’t have child panes.
  4. When you convert a pane to a line, create a child pane, and it passes all the tabs to the convertible pane.
  5. When you delete a pane, its tabs pass to the parent pane, provided that this pane doesn’t have child panes and it is the only child of its parent pane.

Here is an example of the algorithm for creating the MultiPaneCtrl (fill panes and tabs):

  1. Initial state.
  2. Sample Image

  3. Adding tabs to the root pane (MultiPaneCtrl::AddTab).
  4. Sample Image

  5. Conversion of the root pane to a line (MultiPaneCtrl::ConvertToLine).
  6. Sample Image

  7. Adding a child pane to the root pane (MultiPaneCtrl::Add).
  8. Sample Image

  9. Conversion of Pane2 to a line (MultiPaneCtrl::ConvertToLine).
  10. Sample Image

  11. Adding a child pane to Pane2 (MultiPaneCtrl::Add).
  12. Sample Image

  13. Adding tabs to Pane3 and Pane4 (MultiPaneCtrl::AddTab).
  14. Sample Image

Removing panes from the MultiPaneCtrl. Example 1:

  1. Initial state.
  2. Sample Image

  3. Removing Pane3.
  4. Sample Image

  5. Removing Pane2.
  6. Sample Image

Remove panes from MultiPaneCtrl. Example 2:

  1. Initial state.
  2. Sample Image

  3. Removing Pane2.
  4. Sample Image

For each pane which doesn’t have child panes, by default, a TabCtrl control is created. It is described in my previous article, located at http://www.codeproject.com/KB/tabs/TabCtrl.aspx. All TabCtrl controls are created as child windows for MultiPaneCtrl and do not form additional levels of nesting. This is especially important because modern versions of Windows have a limited number of nesting windows in each other, which often does not exceed 13-15. The function GetTabCtrl allows you to get a pointer to TabCtrl. It can be called only for a pane which is not a line. You can use this pointer for a particular tabbed pane or call methods of the MultiPaneCtrl class. For example, to add a tab to a pane, use the function MultiPaneCtrl::AddTab.

To create the control and add elements to it, you can do the following steps:

MultiPaneCtrlEx < MultiPaneCtrlStyle_VS2003_client > m_MultiPaneCtrl;
CTreeCtrl m_Tree1, m_Tree2;
CEdit m_Edit1;
CListCtrl m_List1, m_List2;

...
...

// Creation and initialization of child windows.
if(m_Tree1.Create(WS_CHILD | TVS_HASBUTTONS | TVS_LINESATROOT | 
   TVS_HASLINES,CRect(0,0,0,0),this,300)==0 ||
   m_Tree2.Create(WS_CHILD | TVS_HASBUTTONS | TVS_LINESATROOT | 
   TVS_HASLINES,CRect(0,0,0,0),this,301)==0 ||
   m_Edit1.Create(WS_CHILD | ES_MULTILINE,CRect(0,0,0,0),this,302)==0 ||
   m_List1.Create(WS_CHILD | LVS_REPORT,CRect(0,0,0,0),this,303)==0 ||
   m_List2.Create(WS_CHILD | LVS_REPORT,CRect(0,0,0,0),this,304)==0)
    return -1;

m_Tree1.InsertItem(_T("CTreeCtrl 1"));
m_Tree2.InsertItem(_T("CTreeCtrl 2"));
m_Edit1.SetWindowText(_T("CEdit 1"));
m_List1.InsertColumn(0,_T("CListCtrl 1"),LVCFMT_LEFT,100);
m_List1.InsertItem(0,_T("Item 1"));
m_List2.InsertColumn(0,_T("CListCtrl 2"),LVCFMT_LEFT,100);
m_List2.InsertItem(0,_T("Item 1"));

// Creation of MultiPaneCtrl object.
if(m_MultiPaneCtrl.Create(this,WS_CHILD | WS_VISIBLE, 
   CRect(0,0,400,300),100/*id of MultiPaneCtrl*/)==false)
    return -1;

CImageList imagelist,imagelistDis, imagelistSys;
CBitmap bmp,bmpDis, bmpSys;

imagelist.Create(16,16,ILC_COLOR24 | ILC_MASK,7,0);
bmp.LoadBitmap(IDB_BITMAP1);
imagelist.Add(&bmp,RGB(255,0,255));

imagelistDis.Create(16,16,ILC_COLOR24 | ILC_MASK,7,0);
bmpDis.LoadBitmap(IDB_BITMAP2);
imagelistDis.Add(&bmpDis,RGB(255,0,255));

imagelistSys.Create(14,14,ILC_COLOR24 | ILC_MASK,7,0);
bmpSys.LoadBitmap(IDB_BITMAP3);
imagelistSys.Add(&bmpSys,RGB(255,0,255));

m_MultiPaneCtrl.SetImageLists(&imagelist,&imagelistDis);
m_MultiPaneCtrl.SetSystemImageList(&imagelistSys);

m_MultiPaneCtrl.SetCursors(IDC_CURSOR1,IDC_CURSOR2,
                IDC_CURSOR3,IDC_CURSOR4,IDC_CURSOR5);

m_MultiPaneCtrl.SetDockMarkers(MarkersLayoutC(),
                DockingMarkers::Params(40,true,14),50);
m_MultiPaneCtrl.RemoveTabEnable(true);
m_MultiPaneCtrl.DragTabEnable(true);

// Loading state or creation default state.
try
{
    MultiPaneCtrl::Tabs tabs;
    tabs.Add(m_Tree1,_T("Tree1"),-1);
    tabs.Add(m_List1,_T("List1"),0);
    tabs.Add(m_Edit1,_T("Edit1"),1);
    tabs.Add(m_List2,_T("List2"),2);
    tabs.Add(m_Tree2,_T("Tree2"),3);

    // 
    if(m_MultiPaneCtrl.LoadState(AfxGetApp(),_T("MultiPaneCtrl"), 
                       _T("State"),&tabs,false)==false)
    {
        // create default state.
        HPANE h1 = m_MultiPaneCtrl.ConvertToLine(NULL,false);
        m_MultiPaneCtrl.AddTab(h1,tabs[0]);
        m_MultiPaneCtrl.AddTab(h1,tabs[1]);

        HPANE h2 = m_MultiPaneCtrl.Add(NULL);

        HPANE h3 = m_MultiPaneCtrl.ConvertToLine(h2,true);
        m_MultiPaneCtrl.AddTab(h3,tabs[2]);

        HPANE h4 = m_MultiPaneCtrl.Add(h2);
        HPANE h5 = m_MultiPaneCtrl.ConvertToLine(h4,false);
        m_MultiPaneCtrl.AddTab(h5,tabs[3]);
        
        HPANE h6 = m_MultiPaneCtrl.Add(h4);
        m_MultiPaneCtrl.AddTab(h6,tabs[4]);

        m_MultiPaneCtrl.SetEqualPaneSize();
    }
}
catch(std::bad_alloc &)
{    return -1;
}

m_MultiPaneCtrl.Update();

The control doesn’t perform any drawing, and for this, it calls methods of the interface MultiPaneCtrlDraw. Also, for determining the thickness of the border and splitters, it uses an interface IMultiPaneCtrlRecalc. User operations are defined by methods of the interface MultiPaneCtrlUserAbility. These include the ability to insert the pane in a certain area when you drag it with the mouse, as well as showing system buttons (close, menu, scroll). In general, for proper working of the control, it needs to set the style. To do this, you must implement the functions of the IMultiPaneCtrlStyle interface and pass a pointer to it using the method InstallStyle. Besides pointers to the above interfaces, you must return a pointer to the interface ITabCtrlStyle (see article about TabCtrl). This will be used to define the appearance and behavior of all tabs in the control. An object of the class of the style must exist during the working of the control. To do this, you can create an intermediate class like MultiPaneCtrlComplex. If you are working with only one style, then use the template class MultiPaneCtrlEx. The class name of the style is defined as a template parameter, for example:

MultiPaneCtrlEx < MultiPaneCtrlStyle_VS2003_client > m_MultiPaneCtrl;

Some styles have already been created. For example, styles similar to the docking/floating panels in Visual Studio 2003, 2008, and 2010. To create your own styles, see the classes MultiPaneCtrlStyle_VS2003_client, MultiPaneCtrlStyle_VS2008_client_classic etc. The class MultiPaneCtrlRecalcStub creates a default implementation for the functions of the IMultiPaneCtrlRecalc interface. You can use it to create your own style objects.

In the process of pulling tabs with the mouse for better visualization, we ca use docking markers. They appear on the panes, and when you hover on them, they show the position of the future insertion of the pulling tab. To use it, you need to call SetDockMarkers. One of the parameters of this function is an object for defining graphic resources and their location to create the appearance of the marker. Three objects have been created and used in the demo of this article. They are MarkersLayoutA, MarkersLayoutB, and MarkersLayoutC to set markers look similar to the markers used in VS2008, VS2005, and VS2010, respectively. To construct your own markers, use these classes as examples, also see information in the DockingMarkers.h file.

The control does not send messages to the parent window and uses an interface MultiPaneCtrlNotify for the notification of the events. Use SetNotifyManager to set the pointer to your implementation of MultiPaneCtrlNotify.

The control requires a call to Update after you add or delete tabs, as well as change its properties and state.

The loading state of the control is based on the content of the object MultiPaneCtrl::Tabs. It must keep information about tabs in the Registry or another archive, for use when loading. You can load information from the working control (MultiPaneCtrl::SaveTabs) or add tabs manually (MultiPaneCtrl::Tabs::Add).

By default, all drawing is based on double buffering, it excludes any blinking. If you want, use VirtualWindow::DoubleBuffering(false) to disable double buffering.

Good luck.

License

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

Share

About the Author

Borodenko Oleg
Software Developer Closed JSC 'Phazotron-7'
Belarus Belarus
No Biography provided

Comments and Discussions

 
QuestionUndocking Panes... PinmemberFebruaryGuy2621-Oct-14 18:35 
AnswerRe: Undocking Panes... PinmemberBorodenko Oleg21-Oct-14 21:29 
GeneralI am very appreciate of your work, fantastic job PinmembermotoHello17-Mar-14 23:36 
Questionhow to response the message OnTabRButtonDown Pinmemberfangdanwei13-Feb-14 4:36 
AnswerRe: how to response the message OnTabRButtonDown PinmemberBorodenko Oleg13-Feb-14 5:08 
GeneralRe: how to response the message OnTabRButtonDown Pinmemberfangdanwei15-Feb-14 3:54 
GeneralRe: how to response the message OnTabRButtonDown PinmemberBorodenko Oleg15-Feb-14 4:06 
GeneralRe: how to response the message OnTabRButtonDown Pinmemberfangdanwei15-Feb-14 4:17 
GeneralRe: how to response the message OnTabRButtonDown PinmemberBorodenko Oleg15-Feb-14 4:27 
GeneralThanks a lot. Pinmemberfangdanwei15-Feb-14 4:34 
Questionhere is a bug; PinmemberSam.Liang26-Aug-13 5:22 
AnswerRe: here is a bug; PinmemberBorodenko Oleg27-Aug-13 1:08 
GeneralRe: here is a bug; PinmemberSam.Liang27-Aug-13 3:45 
Questionhow about hide/re-show panel or tab ? Pinmemberskyformat99@gmail.com19-Jun-13 19:45 
AnswerRe: how about hide/re-show panel or tab ? [modified] PinmemberSergej8911-Jul-13 4:23 
GeneralRe: how about hide/re-show panel or tab ? Pinmemberskyformat99@gmail.com14-Jul-13 23:23 
GeneralRe: how about hide/re-show panel or tab ? PinmemberBorodenko Oleg15-Jul-13 0:34 
GeneralRe: how about hide/re-show panel or tab ? Pinmemberskyformat99@gmail.com15-Jul-13 15:50 
GeneralRe: how about hide/re-show panel or tab ? PinmemberBorodenko Oleg16-Jul-13 4:47 
Questionhow about update window? PinmemberFansHill17-Feb-13 0:06 
i find something wrong when add tabctrl that is transparent one, i could only do showwindow.
AnswerRe: how about update window? PinmemberBorodenko Oleg17-Feb-13 0:46 
GeneralRe: how about update window? PinmemberFansHill17-Feb-13 5:09 
GeneralMy vote of 5 Pinmemberskyformat99@gmail.com13-Nov-12 4:57 
GeneralMy vote of 5 PinmemberAli Khanlarkhani20-Oct-12 22:12 
Questioncan't download Pinmemberkunpengnet24-Sep-12 17:20 

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.1411028.1 | Last Updated 12 Jul 2010
Article Copyright 2010 by Borodenko Oleg
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid