Splitter
Typically, complex controls that include treeview and/or list controls make use of a splitter control to separate the "panes" of the container. The splitter control is an unobtrusive, almost invisible control that allows the end user to resize other controls. It generally appears as a thin gray horizontal or vertical bar separating other resizable controls, such as panels, group boxes, ListViews, and tree views. When the mouse cursor hovers over the splitter, the cursor changes to either the Cursors.VSplit (
Figure 14-2 shows a screenshot of Microsoft Outlook Express, an application that uses two splitters. The vertical splitter separates the tree view on the left from the two controls on the right, and the horizontal splitter separates the ListView and the text box on the right side. Either of the splitters can be moved by the user, changing the relative sizes of all three controls accordingly.
Figure 14-3. Splitter controls used in Outlook Express
The Splitter class is derived directly from Control, as shown in Figure 14-1. Although it has a few properties and methods of its own, the most commonly used of which are listed and discussed below, the splitter itself typically can be ignored by the developer once it is in place. It goes about resizing things without any explicit coding on your part.
Splitters work in conjunction with a control that is docked to one edge of the containerspecifically, the control docked to the same edge as the splitter. (Docking was covered in Chapter 7.) This control is actually resized and is called the target control. Typically, the remainder of the container is filled with another control whose Dock property has been set to DockStyle.Fill so it will fill all the remaining space left after the target control was resized.
Correct usage of a splitter control depends on a number of factors:
- The splitter and the control(s) it affects must all have the same container.
- The target control, i.e., the control to be resized, must be docked against one edge of the container.
- The splitter must be docked against the same edge of the container as the target.
- The splitter must be higher in the z-order than the control it is resizing.
Z-order, covered in Chapter 7, represents the depth order of the controls (the order of the controls in the direction perpendicular to the plane of the screen). The control with a z-order of 0 is highest in the z-order (at the top of the z-order). The control with a z-order of Controls.Count-1 is lowest in the z-orderat the bottom.
The z-order of controls in a container is determined initially by the order in which they are added to the Controls collection of the container, which is generally the order in which the controls are created. The controls created earlier have a higher z-orderon top of those created lateralthough this appears to be reversed in Visual Studio .NET, as will be discussed shortly. (The z-order can also be modified with the BringToFront or SendToBack menu items in Visual Studio .NET, or with the BringToFront, SendToBack, or UpdateZOrder methods.)
Consider the sample programs listed in Example 14-1 (in C#) and in Example 14-2 (in VB.NET). These programs create a simple form comprised of two group boxes separated by a splitter, as shown in Figure 14-3Figure 14-3, with the mouse cursor over the splitter. The group box on the left has its Dock property set to DockStyle.Left, as does the splitter control separating the two group boxes. The group box on the right has its Dock property set to DockStyle.Fill. When the splitter is moved by the user, the left group box, the target control, is resized; the right group box appears to change size as it fills the remaining space.
Figure 14-4. Splitter control
Example 14-1. Splitter control in C# (splitters.cs)
using System; using System.Drawing; using System.Windows.Forms; namespace ProgrammingWinApps { public class Splitters : Form { public Splitters( ) { Text = "Splitters"; Size = new Size(300,200); GroupBox grpFill = new GroupBox( ); grpFill.Parent = this; grpFill.Dock = DockStyle.Fill; grpFill.Text = "Dock Fill"; Splitter s = new Splitter( ); s.Parent = this; s.Dock = DockStyle.Left; GroupBox grpLeft = new GroupBox( ); grpLeft.Parent = this; grpLeft.Dock = DockStyle.Left; grpLeft.Text = "Dock Left"; } // close for constructor static void Main( ) { Application.Run(new Splitters( )); } } // close for form class } // close form namespace
Example 14-2. Splitter control in VB.NET (splitters.vb)
Option Strict On imports System imports System.Drawing imports System.Windows.Forms namespace ProgrammingWinApps public class Splitters : inherits Form public sub New( ) Text = "Splitters" Size = new Size(300,200) dim grpFill as new GroupBox( ) grpFill.Parent = me grpFill.Dock = DockStyle.Fill grpFill.Text = "Dock Fill" dim s as new Splitter( ) s.Parent = me s.Dock = DockStyle.Left dim grpLeft as new GroupBox( ) grpLeft.Parent = me grpLeft.Dock = DockStyle.Left grpLeft.Text = "Dock Left" end sub ' close for constructor public shared sub Main( ) Application.Run(new Splitters( )) end sub end class end namespace
In Example 14-1 and Example 14-2, the controls are created in the following order:
- grpFill
- splitter
- grpLeft
grpFill is created first, so it is at the top of the z-order. As each control is created, it goes to the bottom of the z-order. The splitter is created before the target, grpLeft, so the splitter is higher in the z-order than grpLeft.
grpLeft is docked to the left edge of the container. The splitter is also docked to the left edge, satisfying one of the requirements listed above for enabling a splitter to resize. When two different controls are docked to the same edge of a container, as is the case here, their relative positions are controlled by the z-order. The control with the higher z-order, in this case the splitter, is pushed toward the center of the container (away from the docking edge). This is the exact behavior you want: the splitter will be in the middle of the container rather than between the target and the edge of the container.
Suppose you had created grpFill after grpLeft in the above examples? The splitter still would have resized grpLeft, as before, since the criteria for the splitter would still be met. However, now grpLeft would be on top of grpFill. The Text displayed at the top left of the grpFill group box would not be visible until grpLeft was resized small enough to uncover most of the underlying grpFill.
14.2.1 Splitters in Visual Studio .NET
When using splitters in Visual Studio .NET, the order in which you create the controls is reversed from how the controls are created in a text editor:
- grpLeft
- splitter
- grpFill
As controls are created in Visual Studio .NET, they go to the top of the z-order rather than the bottom. This is because Visual Studio .NET uses a technique for adding controls to the Controls collection that differs from that shown in Example 14-1 and Example 14-2. Whereas the Parent property of each control is set explicitly in these examples, Visual Studio .NET uses the AddRange method (inserting the arguments in reverse order) to add all controls to the Controls collection at once, with a syntax similar to the following:
this.Controls.AddRange(new System.Windows.Forms.Control[ ] { this.grpFill, this.splitter, this.grpLeft});
Me.Controls.AddRange(New System.Windows.Forms.Control( ) { _ Me. grpFill, Me.splitter, Me. grpLeft })
The controls in the array, which is the argument to the AddRange method, are added to the Controls collection in order, so the first control in the array is at the top of the z-order, the next control is next in the z-order, and so on. However, Visual Studio .NET adds each new control to the beginning of the array, rather than to the end. Ultimately, the controls are pushed to the top of the z-order as they are created.
In addition to the Dock property described above, the splitter control has several potentially useful properties listed in Table 14-1.
Property |
Value type |
Description |
---|---|---|
BackColor |
Color |
Read/write. The color of the control. The default color is the container's background color. |
BackgroundImage |
Image |
Read/write. Background image of the control. If the image is wider than the control, it will be truncated. |
BorderStyle |
BorderStyle |
Read/write. The border style of the control. Valid values are members of the BorderStyle enumeration, listed in Table 14-2. Default is BorderStyle.None. |
Dock |
DockStyle |
Read/write. The dock style of the control. Valid values are members of the DockStyle enumeration, listed in Table 14-3. |
Height |
Integer |
Read/write. Overridden from Control. Thickness of horizontal splitter in pixels. Default is 3 pixels. Minimum value is 1. |
MinExtra |
Integer |
Read/write. The minimum size, in pixels, of the control on the side of the splitter opposite the target. Default is 25. |
MinSize |
Integer |
Read/write. The minimum size, in pixels, of the target control. Default is 25. |
SplitPosition |
Integer |
Read/write. The distance, in pixels, between the splitter and the container edge to which it is docked. Can determine the splitter position after user interaction or set the splitter position programmatically. If the splitter is not docked to a target, then the value is -1. |
Width |
Integer |
Read/write. Overridden from Control. Thickness of vertical splitter in pixels. Default is 3 pixels. Minimum value is 1. |
Value |
Description |
---|---|
Fixed3D |
3-D border. |
FixedSingle |
Single line border. |
None |
No border. |
Value |
---|
Bottom |
Fill |
Left |
None |
Right |
Top |
Although it is generally not necessary for you to write event handlers for the splitter control, two events raised by the splitter control that might be useful are listed in Table 14-4. Both events provide an event argument of type SplitterEventArgs, which exposes the properties listed in Table 14-5.
Event |
Event argument |
Description |
---|---|---|
SplitterMoved |
SplitterEventArgs |
Raised when the splitter is moved. |
SplitterMoving |
SplitterEventArgs |
Raised while the splitter is being moved. |
Property |
Description |
---|---|
SplitX |
Read/write. X coordinate of the upper-left corner of the control, in client coordinates. |
SplitY |
Read/write. Y coordinate of the upper-left corner of the control, in client coordinates. |
X |
Read-only. X coordinate of the mouse cursor, in client coordinates. |
Y |
Read-only. Y coordinate of the mouse cursor, in client coordinates. |