Minimum size splitter

WEBINAR: On-demand webcast

How to Boost Database Development Productivity on Linux, Docker, and Kubernetes with Microsoft SQL Server 2017 REGISTER >

For my latest project we needed splitter panes that had proper minimum sizes, in that the user couldn't physically resize the splitter pane smaller than a specified amount. The standard MFC splitter class allows you to set minimum sizes, but these only affect what is displayed, not how small the panes can be. CMinSplitterWnd is a drop-in replacement for (and is derived from) CSplitterWnd.

Instructions:

  1. Add MinSplitterWnd.cpp and MinSplitterWnd.h to your current project (e.g. Menu Project->Add to project->Add files).
  2. Where you currently use CSplitterWnd, replace with CMinSplitterWnd (Note: remember to include MinSplitterWnd.h):
  3. #include "MinSplitterWnd.h" // Minimum size splitter.
    ....
    class CMyWnd : public CMDIChildWnd
    {
        ....
        CMinSplitterWnd m_wndSplitter;
        ....
    };
    
  4. Call SetRowInfo and SetColumnInfo as necessary, specifying the required minimum row and column sizes: (this is normally done in the parent window's OnCreateClient function)
  5. ....
    m_wndSplitter.CreateStatic( this, 2, 2 );      // Create with 2 rows and 2 columns.
    m_wndSplitter.SetRowInfo( 0, 100, 100 );       // Set ideal and minimum size to 100.
    m_wndSplitter.SetRowInfo( 1, 100, 100 );       // Set ideal and minimum size to 100.
    m_wndSplitter.SetColumnInfo( 0, 100, 100 );    // Set ideal and minimum size to 100.
    m_wndSplitter.SetColumnInfo( 1, 100, 100 );    // Set ideal and minimum size to 100.
    ....
    
  6. In the splitter's parent window, add a message handler for OnWindowPosChanging (this prevents the user from sizing the window too small) and add the following code to it:
  7. void CMyWnd::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos) 
    {
    	//	Don't do anything if minimized.
    	if ( IsIconic() )
    	{
    		CMDIChildWnd::OnWindowPosChanging( lpwndpos );
    		return;
    	} // if
    
    	//	Calculate the window rect if we have no client area.
    	CRect rc( 0, 0, 0, 0 );
    	CalcWindowRect( rc );
    
    	//	Add on the minimum client width and height from the splitter.
    	int nMinWidth	= rc.Width() + m_wndSplitter.GetMinClientWidth();
    	int nMinHeight	= rc.Height() + m_wndSplitter.GetMinClientHeight();
    
    	//	Get the current window size.
    	CRect rcWindow;
    	GetWindowRect( rcWindow );
    	GetParent()->ScreenToClient( rcWindow );
    
    	//	If trying to size too small...
    	if ( lpwndpos->cx < nMinWidth )
    	{
    		//	If dragging left border right...
    		if ( rcWindow.left < lpwndpos->x )
    		{
    			//	How much over are we?
    			int nOver = nMinWidth - lpwndpos->cx;
    			
    			//	Adjust left coord.
    			lpwndpos->x -= nOver;
    
    		} // if
    
    		//	Fix width.
    		lpwndpos->cx = nMinWidth;
    	} // if
    
    	//	If trying to size too small...
    	if ( lpwndpos->cy < nMinHeight )
    	{
    		//	If dragging top border down...
    		if ( rcWindow.top < lpwndpos->y )
    		{
    			//	How much over are we?
    			int nOver = nMinHeight - lpwndpos->cy;
    			
    			//	Adjust left coord.
    			lpwndpos->y -= nOver;
    		} // if
    		
    		//	Fix height.
    		lpwndpos->cy = nMinHeight;
    	} // if
    }
    
  8. That's it!

Limitation:

If the minimum sizes exceed the initial frame window size, the window will snap when the window is first resized.

Download demo project and executable - 36 KB



Comments

  • a simple way ...

    Posted by FDogY on 06/08/2004 09:25am

    First of all: Putting the variable nIdealSize by SetColumnInfo() or SetRowInfo() serves only as an initial setting!
    nIdealSize is always updated with the current value of the partial window after the first drawing. Use only this value.
    In this context the additional variable nCurSize leads to confusion. Forget about this.
    Here is the really short solution for the "minimum size splitter"-problem: 
    
    1. you create a class which is derived from CSplitterWnd 
    2. overwrite the function RecalcLayout() as follows: 
    
      void  CMySplitterWnd::RecalcLayout(){
        for (int i = 0; i < m_nCols; i++)  { // Check all columns
          if ( m_pColInfo[i].nIdealSize < m_pColInfo[i].nMinSize ) { 
            m_pColInfo[i].nIdealSize=m_pColInfo[i].nMinSize; 
          }
        }
        for (int i = 0; i < m_nRows; i++)  { // Check all rows
          if ( m_pRowInfo[i].nIdealSize < m_pRowInfo[i].nMinSize ) {
            m_pRowInfo[i].nIdealSize=m_pRowInfo[i].nMinSize; 
          }
        }
        CSplitterWnd::RecalcLayout();
      }
    
    3. create an object of your class (CMySpliterWnd m_SplitterWnd)
    4. after creating the SplitterWindow set the (initial) IdealSize value (e.g. 150) and the MinimalSize (e.g. 20) 
      
      m_SplitterWnd.SetColumnInfo(0, 150, 20);
      m_SplitterWnd.SetRowInfo(0, 150, 20);
    
    ready

    Reply
  • Greate work... How can I do the same in VB

    Posted by Legacy on 06/03/2002 12:00am

    Originally posted by: Navneet Gupta

    Hey Dude !
    Great wrok ..

    I want to implement the same thing in Visual Basic can you guide me how can I do this.


    Reply
  • U do a pretty work.There is a Bug in Dynamic Split

    Posted by Legacy on 12/14/2001 12:00am

    Originally posted by: zsj

    U do a good job,But When I create a dynamic split with CScrollView ,the Splitbar stop in wrong position ,I Think You may make a wrong that U have not considered the size of ScrollBox .

    Reply
  • simpler solution continued...

    Posted by Legacy on 08/20/2001 12:00am

    Originally posted by: Alan Kang

    This solution is similar to Vinayak Tadas's suggestion of 
    
    overriding CSplitterWnd::RecalcLayout. This one works
    with all sub-panes. You would still need to call
    SetRowInfo and SetColumnInfo as necessary but there is
    no need to do anything else

    void CSplitterWndEx::RecalcLayout()
    {
    CSplitterWnd::CRowColInfo* pInfo = m_pColInfo;
    CSplitterWnd::CRowColInfo* pInfoNext = pInfo;
    for (int i = 0; i < m_nCols - 1 && pInfo->nCurSize > -1; i++)
    {
    // Check next column
    int nXDiff = (++pInfoNext)->nMinSize -
    (pInfoNext->nCurSize - (pInfo->nIdealSize - pInfo->nCurSize));

    // Adjust the resizing step so that the size of the next col
    // will be at least its min size
    if (nXDiff > 0) pInfo->nIdealSize -= nXDiff;

    // Adjust the resizing step so that the size of this col
    // will be at least its min size
    pInfo->nIdealSize = max(pInfo->nIdealSize, pInfo->nMinSize);
    }

    pInfoNext = pInfo = m_pRowInfo;
    for (i = 0; i < m_nRows - 1 && pInfo->nCurSize > -1; i++)
    {
    // Check next row
    int nYDiff = (++pInfoNext)->nMinSize -
    (pInfoNext->nCurSize - (pInfo->nIdealSize - pInfo->nCurSize));

    // Adjust the resizing step so that the size of the next row
    // will be at least its min size
    if (nYDiff > 0) pInfo->nIdealSize -= nYDiff;

    // Adjust the resizing step so that the size of this row
    // will be at least its min size
    pInfo->nIdealSize = max(pInfo->nIdealSize, pInfo->nMinSize);
    }
    CSplitterWnd::RecalcLayout();
    }


    Reply
  • An even simple solution .....

    Posted by Legacy on 10/08/1999 12:00am

    Originally posted by: Vinayak Tadas

    Retain only the OnMouseMove function in the above class and modify the 
    
    RecalcLayout() function as below. You can remove all the
    other functions and structures ....


    void CMinSplitterWnd::RecalcLayout()
    {
    CSplitterWnd::CRowColInfo* pInfoArray = m_pColInfo;
    CSplitterWnd::CRowColInfo* pInfo;
    int i =0;
    for (i = 0, pInfo = pInfoArray; i < m_nCols-1; i++,
    pInfo++)
    {
    if (pInfo->nIdealSize < pInfo->nMinSize)
    pInfo->nIdealSize = pInfo->nMinSize;
    pInfo->nCurSize = pInfo->nIdealSize;
    }

    CSplitterWnd::RecalcLayout();
    }

    Reply
  • Thank U mail

    Posted by Legacy on 09/20/1999 12:00am

    Originally posted by: wowow

    This is the article what I was looking for.
    The 'minimum size splitter' may not be very useful for most people, but there's always somebody really, really needs it.( eg., me! )

    I thank U again and I expect many other useful articles from U and the CodeGuru.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • As all sorts of data becomes available for storage, analysis and retrieval - so called 'Big Data' - there are potentially huge benefits, but equally huge challenges...
  • The agile organization needs knowledge to act on, quickly and effectively. Though many organizations are clamouring for "Big Data", not nearly as many know what to do with it...
  • Cloud-based integration solutions can be confusing. Adding to the confusion are the multiple ways IT departments can deliver such integration...

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date