home
Splitter Bars
Part I  Introduction

Ernest Murphy ernie@surfree.com

October 18, 2000 

Get the source code here.
 

Abstract:
------------------------------------------------------------------------------------------ 

  Splitter bars are a distinctive visual control element that help organize a large amount of information in a window. They allow your user to resize the relation between two panes of information. A very common example of a splitter is in Windows File Explorer, which uses a splitter bar to divide two panes of information.

  A vertical splitter bar application is described here. In the sample code, a horizontal splitter bar application is also included to cover both types of splitters.
 

Introduction:
------------------------------------------------------------------------------------------

  Splitter bars are one of the least apparent window elements. They appear as a 4-pixel wide strip of nothing between two child windows. They provide user feedback to their being there by changing the mouse pointer to a horizontal resize pointer while over this bar.

  A search of MSDN will show splitters done in MFC and other libs. This is a shame, as a splitter is actually one of the simplest child window controls you can write.

  To create our splitter, all we need is a window of the same back color as the parent window. Assign it the cursor type of "horizontal resize" (IDC_SIZEWE). That's it.

  The "magic" of this window is just a bit of code in it's windows procedure.

  The main messages of the splitter window we need to handle are WM_MOUSEDOWN, MOUSEUP, and MOUSEMOVE. See 

Custom Windows and Shapes for a deeper explanation of the method I use to move windows with the mouse. 

  At MOUSEDOWN, we save the X-Y point on the splitter where the mouse clicked, and invoke SetCapture to keep the mouse messages in our window. MOUSEUP is even simpler, just invoking ReleaseCapture and letting the mouse run free.

  MOUSEMOVE is where all the magic happens. Two things are first checked: That the left mouse button is down, and that it has moved relative to the initial click hit in MOUSEDOWN. If it has, the scroll window is moved the same distance, and the side windows are re-calculated. Note for a vertical splitter, only the X dimension is checked for the move, and for a horizontal splitter only the Y dimension.


  We will be using MoveWindow to move our child windows around.  MoveWindow wants the top and left dimensions in client dimensions, and also wants the windows new width and height. The closest we can get through the API is GetWindowRect, which returns screen dimensions for the top left and bottom right corners. Converting from screen dimensions to client dimensions is a bit cumbersome, but simple in theory. 

Screen and Client co-ordinates

  One point in the client area that never moves and is always there for reference is the top left corner, or (0,0). We pass this point to ClientToScreen and we get the Screen dimensions of this point. Now we can subtract these screen dimensions from the dimensions we get back from GetWindowRect to convert from screen to client dimensions. 

  In this way, we can compute a windows dimensions in client and width/height terms like so:
  invoke GetWindowRect, hWnd, ADDR rect
pt.x = 0
pt.y = 0
invoke ClientToScreen, ADDR pt
topleft = rect.left - pt.x
topright = rect.top - pt.x
width = rect.right - rect.left
height = rectbottom - rect.top

  I will leave the details of how the child windows are calculated to anyone interested in wading through the source code.

  One last check we make is to see if either of the side windows is getting too small, and adjust the movement to the minimum window width.

  One last further note: When we run through the MOUSEMOVE code, we do a check if the movement is zero. This may seem odd, as how can the mouse move zero and still generate a MOUSEMOVE message? In truth, there are two ways a MOUSEMOVE message is generated. One is obvious, move the mouse over the window. The other is more subtle: move the window under the mouse. By checking for zero movements we get to skip the last loop of the MOUSEMOVE message, and insure we don't create an infinite loop checking the window we just moved a zero amount doesn't try to move zero again.

Conclusion
------------------------------------------------------------------------------------------
  A splitter bar can easily be created by using a window to handle the mouse cursor and give us a winproc to handle messages.

  One further note: The source code for this artical is basically the same as I orgionally developed for the first splitter class, though it has since been tweeked and changed to match the work done re-writing it into the dll form. Most of the re-write was done thruogh cut-and-paste. For example, the same winproc is shared between both programs.

  This is a useful approach to creating a custom control library function, as most of the development work may be done on a single executable, tested after a single compile and link step. If I had started right off with a dll and a second test program, it would have opened the question of "where is the bug? In the .exe or the .dll?" This way, most bugs were already dead when the dll was newborn.
------------------------------------------------------------------------------------------

Part II Splitter Bar DLL

Part III Using the Splitter Bar DLL

Part IV Ready for Prime Time
 

home