Build Your Own Reusable SplitBar Component

by Michael C. Amundsen

If you've started to create Explorer-type interfaces with the new Windows Common Controls available for VB4-32 and VB5, you may have discovered that a very important GUI tool is missing from the Windows Common Controls: a splitter bar control. As Figure A shows, this control allows users to drag a vertical or horizontal bar across the form to reallocate space between two display areas.

Figure A: This form includes both horizontal and vertical splitter bars.

Although it's disappointing that Microsoft left out such an important control, in this article we'll show you how easy it is to build your own 32-bit component that will handle the task for you. After creating the component as a standalone project, you'll be able to use the References menu option to import the library routines into all your 32-bit VB applications. (Inside Visual Basic subscribers can download the sample files from www.zdjournals.com/ivb. Click on the Source Code link.)

Designing the SplitBar interface

The first step in building the SplitBar component is to design its interface. The interface is the set of exposed properties and methods that will be used by the client. The client is any application that requests services from the SplitBar component.

For this project, we'll build a very simple interface to handle the SplitBar activity. Table A shows all the properties and methods we'll need to execute a horizontal SplitBar.

Table A: Methods and properties for the horizontal split bar

Name Type Property/Method Scope
HostPane Object Property Public
LeftPane Object Property Public
RightPane Object Property Public
SplitBar Object Property Public
SplitOn Boolean Property Public
SetPointer Sub Method Public
ResizePanes Sub Method Public

Let's take a moment to review the table. The first four properties in the table represent objects that will appear on the form. The HostPane is the form or control that holds the other three. Although this may seem like a extra (possibly needless) property, you'll find out later how handy it is. The LeftPane, RightPane, and SplitBar properties will hold the controls that represent each of the three items on the form.

You use the SplitOn property to keep track of the status of user requests. Pressing the mouse button will "turn on" the split bar service. Releasing the mouse button will turn the service off.

The SetPointer method will give the client an easy way to toggle the mouse pointer from normal to split mode. Finally, ResizePanes is the real magic in this component. This routine will keep track of the position of the SplitBar control and automatically resize the LeftPane and RightPane controls within the HostPane.

Implementing the horizontal split class

Now that we've completed the design of the horizontal split services, we're ready to implement that design in VB code. First, start Visual Basic. If you're using VB4-32, remove the default form from the project and replace it with a BAS Module by choosing Module from the Insert menu. Then, select Procedure from the Insert menu and type main for the name. This will make sure the VB4-32 project starts out properly after it's compiled. Finally, add a Class module to the project by selecting Class Module from the Insert menu. If you're using VB5, just select ActiveX DLL project at startup or select New from the File menu, and pick the ActiveX DLL project template.

Now, set the class's Name property to HSplit. If you're using VB4-32, set the Instancing property to 2 - Creatable MultiUse and the Public property to True. In VB5, set the Instancing property to 5 - MultiUse.

Using the information in Table A, add the five properties needed for this component. You can use Procedure from the Insert menu in VB4 or Add Procedure from the Tools menu.

Once you've added the five property declarations, you're ready to add code in the property routines. First, create a set of local variables to store the properties. Section 1 of Listing A shows the code you need to add to the General Declarations section of the class module.

Listing A: HSplit class code

Section 1
Option Explicit

'local variable(s) to hold property value(s)
Private mHostPane As Object 'local copy
Private mTopPane As Object 'local copy
Private mBottomPane As Object 'local copy
Private mSplitBar As Object 'local copy
Private mSplitOn As Boolean 'local copy
Section 2
Public Property Set SplitBar(ByVal vData As Object)
    '
    Set mSplitBar = vData
    '
End Property

Public Property Get SplitBar() As Object
    '
    Set SplitBar = mSplitBar
    '
End Property

Public Property Set BottomPane(ByVal vData As Object)
    '
    Set mBottomPane = vData
    '
End Property

Public Property Get BottomPane() As Object
    '
    Set BottomPane = mBottomPane
    '
End Property

Public Property Set TopPane(ByVal vData As Object)
    '
    Set mTopPane = vData
    '
End Property

Public Property Get TopPane() As Object
    '
    Set TopPane = mTopPane
    '
End Property

Public Property Set HostPane(ByVal vData As Object)
    '
    Set mHostPane = vData
    '
End Property

Public Property Get HostPane() As Object
    '
    Set HostPane = mHostPane
    '
End Property

Section 3
Public Property Let SplitOn(ByVal vData As Boolean)
    mSplitOn = vData
End Property

Public Property Get SplitOn() As Boolean
    SplitOn = mSplitOn
End Property

Section 4
Public Sub SetPointer(pType As Integer)
    '
    mHostPane.MousePointer = pType
    '
End Sub

Section 5
Public Sub ResizePanes(Optional Twips As Single)
    '
    On Error GoTo LocalErr
    '
    If IsMissing(Twips) Then
        Twips = 0
    End If
    '
    With mSplitBar
        .Left = 0
        .Top = .Top + Twips
        .Height = 30
        .Width = mHostPane.ScaleWidth
    End With
    '
    With mTopPane
        .Left = 0
        .Top = 0
        .Width = mHostPane.ScaleWidth
        .Height = mSplitBar.Top
    End With
    '
    With mBottomPane
        .Left = 0
        .Top = mSplitBar.Top + mSplitBar.Height
        .Width = mHostPane.ScaleWidth
        .Height = mHostPane.ScaleHeight - (mTopPane.Height + mSplitBar.Height)
    End With
    '
    Exit Sub

LocalErr:
    ' ignore any errors
End Sub

Now you're ready to add code to each of the property routines. Section 2 of Listing A shows the code needed for the four object properties. Notice what's happening here: The Set Property method accepts a parameter from the client and stores it in the local variable. The Get Property function simply returns the stored item to the client when requested.

Add the code for the SplitOn property from section 3. You'll notice that instead of a Set Property method, there's a Let Property method. The Set statement is needed only for object variables; all other variables use the Let statement.

Next, you can create the two methods for the Hsplit class. First, add the SetPointer method to the class. Note the use of a single integer parameter for the method. This parameter passes the pointer constant to the routine. Add the code from section 4 to your class.

Finally, you're ready to add the code for the ResizePanes method. Add this method to your class, and be sure to add the Optional parameter to the declaration line. If you're using VB4-32, you'll need to make the parameter type Variant (VB4 recognizes only variant data for optional parameters). If you use VB5, declare the variable as Single. Then add the code from section 5.

How the routine works

Several things in the ResizePanes method are worth noting. First, we declare an error handler. This will catch invalid resize values that might occur if the client attempts to minimize the form or drag the split bar beyond the host-pane boundaries. Next, the optional parameter is checked and set to zero, if it wasn't passed.

The next three code sections handle the details of computing and resizing the controls. First, the value passed into the routine locates the split-bar control on the host pane. Next, the left-pane control expands to fill the host pane to the left of the split-bar control. Finally, the right-pane control expands to fill the host pane to the right of the split-bar control.

And that's all there is to it! To be safe, save the class (as HSplit.cls) before continuing. Next, we'll create a Vsplit class that will handle up and down split bars on a host pane.

Building the vertical split class

Now that you've already created the horizontal split bar, building the vertical split bar is really easy. First, you need to redesign the interface slightly to reflect the up-down nature of the GUI interface, then adjust the ResizePanes method to handle the new data. Table B shows the properties and methods for the vertical split bar.

Table B: Methods and properties for the vertical split bar

Name Type Property/Method Scope
HostPane Object Property Public
TopPane Object Property Public
BottomPane Object Property Public
SplitBar Object Property Public
SplitOn Boolean Property Public
SetPointer Sub Method Public
ResizePanes Sub Method Public

You can use Table B as a guide in building the new VSplit class. Add a new class module and set its Name property to VSplit. Be sure to make the class multiuse, createable, and public.

Next, use the Add Procedure menu option to add the five properties. Then add the code from section 1 of Listing B to the General Declarations section. This code will hold the local copy of the property values.

Listing B: VSplit class code

Section 1
Option Explicit

'local variable(s) to hold property value(s)
Private mHostPane As Object 'local copy
Private mLeftPane As Object 'local copy
Private mRightPane As Object 'local copy
Private mSplitBar As Object 'local copy
Private mSplitOn As Boolean 'local copy

Section 2
Public Property Set SplitBar(ByVal vData As Object)
    '
    Set mSplitBar = vData
    '
End Property

Public Property Get SplitBar() As Object
    '
    Set SplitBar = mSplitBar
    '
End Property

Public Property Set RightPane(ByVal vData As Object)
    '
    Set mRightPane = vData
    '
End Property

Public Property Get RightPane() As Object
    '
    Set RightPane = mRightPane
    '
End Property

Public Property Set LeftPane(ByVal vData As Object)
    '
    Set mLeftPane = vData
    '
End Property

Public Property Get LeftPane() As Object
    '
    Set LeftPane = mLeftPane
    '
End Property

Public Property Set HostPane(ByVal vData As Object)
    '
    Set mHostPane = vData
    '
End Property

Public Property Get HostPane() As Object
    '
    Set HostPane = mHostPane
    '
End Property

Public Property Let SplitOn(ByVal vData As Boolean)
    '
    mSplitOn = vData
    '
End Property

Public Property Get SplitOn() As Boolean
    '
    SplitOn = mSplitOn
    '
End Property

Section 3
Public Sub SetPointer(ByVal pType As Integer)
    '
    mHostPane.MousePointer = pType
    '
End Sub

Public Sub ResizePanes(Optional Twips As Single)
    '
    On Error GoTo LocalErr
    '
    If IsMissing(Twips) Then
        Twips = 0
    End If
    '
    With mSplitBar
        .Top = 0
        .Left = .Left + Twips
        .Height = mHostPane.ScaleHeight
        .Width = 30
    End With
    '
    With mLeftPane
        .Top = 0
        .Left = 0
        .Height = mHostPane.ScaleHeight
        .Width = mSplitBar.Left
    End With
    '
    With mRightPane
        .Top = 0
        .Left = mSplitBar.Left + mSplitBar.Width
        .Height = mHostPane.ScaleHeight
        .Width = mHostPane.ScaleWidth - (mLeftPane.Width + mSplitBar.Width)
    End With
    '
    Exit Sub
    '
LocalErr:
    ' Ignore any errors
End Sub

Next, you can add the code to the property routines. See section 2 of Listing B for the details. You'll see that this code is virtually identical to the code from sections 2 and 3 of Listing A. Only the names have been changed to reflect the new property values.

Finally, you can create the two methods and add their code. Section 3 of Listing B has the complete code for the SetPointer and ResizePanes methods for the Vsplit class. If you're using VB4-32, be sure to change the parameter type from Single to Variant for the ResizePanes method.

That's all the code you need to complete the component. Save the new class as VSplit.cls and the project as SplitLib.vbp. Now you're ready to compile your component.

Compiling the SplitBar component

After completing all the code, you need to compile your component before you can use it in a VB project. First, test the code syntax by pressing the [F5] key to run the component. If you entered all the code without errors, in VB4 you should see a short "flicker" of the screen, then a return to the design mode. In VB5, you'll need to click the Stop button to return to design mode. (Since you're building a DLL component, you won't see a sustained running program such as you do when you build an EXE component.) If you get an error message, fix the code and run it again. Once all the runtime errors are fixed, you're ready to compile.

If you're using VB4-32, you'll need to first enter the project name. To do so, select Options from the Tools menu and enter SplitLib in the Project Name field. For VB5, select Properties from the Project menu and enter the same value. It's very important that you enter this name correctly—the client applications use this name to request services from the component.

After entering the project name, you can create the DLL. In VB4-32, select Make OLE DLL File… from the File menu. In VB5, select Make SplitLib.dll from the File menu. Compiling the component also makes the needed registry entries for your workstation. Now you're ready to use your SplitBar component.

Designing the SplitBar test project

To build a test form for the SplitBar component, start a new standard project in VB. Table C and Figure B show the controls and the suggested layout for a test form.

As you build this form, pay attention to two important points. First, the exact placement of the controls isn't important, since they'll be resized at runtime. Second, you should notice that three of the controls (txtTop, picHSplit, and lblBottom) are inside one of the picture controls (picRight). To accomplish this result, you must first place the large picture control on the form, then create each of the other three controls inside the first one. This way, you cause the three new controls to be registered as "children" of the larger, host control.

Table C: Controls for the test form

Control Property Setting
Form Name frmTest
TextBox Name
MultiLine
ScrollBars
Text
TxtLeft
True
3 – Both
Left Pane
Picture Name picSplitter
Picture Name picRight
TextBox Name txtTop
Picture Name picHSplit
Label Name
BorderStyle
LblBottom
1 – Fixed Single

Figure B: We'll test our component with this sample form.

Before you add code to the form, you must add a reference to the SplitLib component to your project. Doing so will import the public properties and methods of the Hsplit and Vsplit classes you created earlier. To add a reference in VB4-32, select References… from the Tools menu to display the list of available components. In VB5, select References… from the Project menu to access the same list. Now, locate and select the SplitLib component, then click OK to import the interface into your test project.

Coding the SplitBar test project

Now you're ready to code the test form to create the split bars and respond to user requests to resize the various panes. First, add the code from section 1 of Listing C to the General Declarations section of the form. This code creates instances of each of the new objects.

Listing C: Coding the test project

Section 1
Option Explicit

Dim spVSet As New VSplit
Dim spHSet As New Hsplit

Section 2
Private Sub Form_Initialize()
    '
    Set spVSet.HostPane = Me
    Set spVSet.LeftPane = txtLeft
    Set spVSet.RightPane = picRight
    Set spVSet.SplitBar = picSplitter
    spVSet.SplitOn = False
    '
    Set spHSet.HostPane = picRight
    Set spHSet.TopPane = txtTop
    Set spHSet.BottomPane = lblBottom
    Set spHSet.SplitBar = picHSplit
    spHSet.SplitOn = False
    '
End Sub

Section 3
Private Sub Form_Resize()
    '
    spVSet.ResizePanes
    '
End Sub

Private Sub picRight_Resize()
    '
    spHSet.ResizePanes
    '
End Sub

Section 4
Private Sub picSplitter_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    '
    spVSet.SetPointer vbSizeWE
    spVSet.SplitOn = True
    '
End Sub

Private Sub picSplitter_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    '
    spVSet.SetPointer vbSizeWE
    '
    If spVSet.SplitOn = True Then
        spVSet.ResizePanes X
    End If
    '
End Sub

Private Sub picSplitter_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
    '
    spVSet.SetPointer vbNormal
    '
    If spVSet.SplitOn = True Then
        spVSet.ResizePanes X
        spVSet.SplitOn = False
    End If
    '
End Sub


Private Sub picHSplit_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    '
    spHSet.SetPointer vbSizeNS
    spHSet.SplitOn = True
    '
End Sub

Private Sub picHSplit_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    '
    spHSet.SetPointer vbSizeNS
    '
    If spHSet.SplitOn = True Then
        spHSet.ResizePanes Y
    End If
    '
End Sub

Private Sub picHSplit_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
    '
    spHSet.SetPointer vbNormal
    '
    If spHSet.SplitOn = True Then
        spHSet.ResizePanes Y
        spHSet.SplitOn = False
    End If
    '
End Sub

Section 5
Private Sub txtLeft_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    '
    spVSet.SetPointer vbNormal
    '
End Sub

Private Sub txtTop_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    '
    spHSet.SetPointer vbNormal
    spVSet.SetPointer vbNormal
    '
End Sub

Private Sub lblBottom_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    '
    spHSet.SetPointer vbNormal
    spVSet.SetPointer vbNormal
    '
End Sub

Next, add the code from section 2 of Listing C to the Form_Initialize event. This code fills the two objects with information about the form including which controls to resize when requested.

You should note that the HostPane property of the vertical set (spVSet) is the form (Me), but the HostPane of the horizontal set (spHSet) is the picRight control. The picRight control also acts as the right-hand control for the SpVSet! You can see how you can "nest" split sets inside one another. There's no logical limit to the number of split sets you can nest in this manner.

Now that you've completed the setup of both the vertical split set (spVSet) and the horizontal split set (spHSet), you must add a line of code to the Resize event of the two HostPanes. The Resize events occur each time the form changes in size and once when the form is loaded into memory. Add the code from section 3 to your test form.

You're now ready to add code to the related mouse events for each control set. Here's what you need to  do. Each time the mouse crosses over the split bar control, it should change from the default arrow to the double-pointed arrow to show the user that he can drag the control around. Consequently, when the user presses the mouse button and moves the mouse, the split bar should follow the mouse and the associated controls (left/right or up/down) should resize to match the re-located SplitBar control. Finally, when the user releases the mouse button, the split bar should remain still and the mouse pointer should revert to the standard arrow.

The code needed to complete these actions is very simple. Section 4 of Listing C shows the lines you add to the MouseMove, MouseDown, and MouseUp events of the designated SplitBar controls. Type this code into your project for both the picSplitter and the picHSplit controls.

The really important lines are the ones that call the ResizePanes method. Note that this time the optional parameter is included in the call. For the picSplitter control, the X parameter is passed, which tells the ResizePanes method how far (left or right) the mouse has moved since the last Move event. Since the picHSplit control is used to control the horizontal mode, the Y parameter is passed instead of the X parameter.

There's one more bit of code you must add to this test form. Since the mouse pointer changes from the default arrow each time the mouse passes over one of the split bar controls, you also need to add code to the other controls to change the mouse pointer back to the default arrow after the mouse leaves the split bar control space. The code in section 5 does just that. Add this to your project now.

Since that right-side control for the vertical split set is the HostPane of the vertical split set, you don't need to add code to the picRight control. However, you must add code to the txtTop and lblBottom controls to handle changing the mouse pointer for both split sets.

That's all the code you need. Save your test project and press [F5] to run the program. Your form should resemble the one in Figure A. You should be able to drag the two split bars and see the controls automatically resize to fill in the gaps.

Notes

Since you built this component as a standalone package (DLL), you can add it to any VB or VBA-compliant program (Excel, Word 97, etc.). Although this is a useful control, it could benefit from some additional features. For example, it would be nice if there were a method that would allow you to set the initial location of the split-bar control within the HostPane. Such a method could ensure that the split bar would appear in the same location at the start of the program.

Even better, wouldn't it be really neat if there were a pair of methods that would allow you to save and restore the split-bar control location between sessions of the form? You could store these methods in an INI file or as Registry entries and recall them during the initialization event.

Finally, if you're really ambitious, you can save yourself (or your customers) a lot of tedious coding by creating an Add-In or Wizard that would prompt for the related controls. The wizard would then automatically add the initialization and mouse-event code to the selected form. That task should keep you busy for a while!

Michael C. Amundsen keeps busy writing and working as an IS consultant, specializing in systems analysis and design. He co-authored Teach Yourself Database Programming with Visual Basic in 21 Days with Curt Smith and was a contributing author for Visual Basic 4 Unleashed. When Mike isn't traveling to client sites or writing, he spends time with his family in Kentucky. You can reach Mike via CompuServe at 102641,1267 or on the Internet at MikeAmundsen@msn.com.

Copyright © 1998, ZD Inc. All rights reserved. ZD Journals and the ZD Journals logo are trademarks of ZD Inc. Reproduction in whole or in part in any form or medium without express written permission of ZD Inc. is prohibited. All other product names and logos are trademarks or registered trademarks of their respective owners.