GridSplitter: FUBAR or Just Poorly Documented?
December 20, 2005
Roscoe, NY
The Splitter control in WinForms 1.0 was a bit awkward. If you created your panels and Splitter in the wrong order, you'd end up with a splitter at the edge of the window. I spent about 10 pages (1050 through 1060) in my first Windows Forms book on ways to use the Splitter.
The SplitContainer of WinForms 2.0 is much better, and quite elegant in my opinion. It's derived from ContainerControl (like Form) and has two properties named Panel1 and Panel2. I cover SplitContainer on pages 109 to 111 of my new WinForms book.
For awhile, it seemed as if there wouldn't even be a splitter control in Avalon. It puzzled me, but I suspected some great weighty issues were at play regarding user interface philosophy. Or somebody forgot to code it. I don't know. But eventually something was introduced called the GridSplitter. What a mess!
The GridSplitter can only be a child of a Grid. As with other children of Grid you use the static Grid.SetRow and Grid.SetColumn attached methods to specify the cell where you want the GridSplitter. By default, the splitter appears on the right edge of that cell.
However, all the sample code I've seen (not much, I admit) place the GridSplitter in a cell that is already occupied by some other element. The GridSplitter then sits on top of the right part of that element. There's even an extended passage in the documentation of GridSplitter about how to solve the related problem of a GridSplitter that is buried underneath the element in that cell:
-
Child controls of a Grid control are rendered in the order that they appear in markup or in code. Therefore, a grid splitter control can be obscured by other controls that are rendered after the grid splitter control is rendered. To make sure that a grid splitter control is not obscured by other children of a Grid control, do one of the following:
- Specify margins for the controls adjacent to grid splitter controls in the markup or code. These margins make sure a grid splitter control is reachable when other controls are layered on top.
- Define the grid splitter controls after all other controls in the markup or in code when defining a Grid control. Defining the grid splitter controls last renders them on top of any other controls in the Grid.
Online, that passage can be found here. The official code sample for GridSplitter is here. As you can see from compiling and running this sample, the GridSplitter appears on just one edge of one cell. Surely you should use Grid.SetRowSpan with a GridSplitter but nobody seems to mention that.
The fun begins when you decide you want a horizontal splitter rather than a vertical one. This is easy to play with in the official sample: If you just change the ResizeDirection property to Rows, the splitter remains vertical and everything looks the same, but you can actually grab it and move the whole middle row up and down!
Nowhere does the documentation mention that the crucial properties for using GridSplitter sanely are HorizontalAlignment and VerticalAlignment. By default, GridSplitter sets these properties to Right and Stretch, respectively, and that's why the splitter appears on the far right of the cell. Eventually I might have figured that out on my own, but fortunately this sample code includes radio buttons to experiment with those properties. Set these properties correctly, and you can leave ResizeDirection at it's default Auto setting and GridResizeBehavior at its default BasedOnAlignment setting.
So why can't the GridSplitter occupy its own cell in the Grid? Why can't you mimic the WinForms 2.0 SplitContainer by using a 3-column single-row Grid with a GridSplitter in the middle cell for a vertical splitter, and a 3-row single-column Grid with a GridSplitter in the middle cell for a horizontal splitter?
Well, why not? The key is setting HorizontalAlignment and VerticalAlignment to Center and Stretch (respectively) for a vertical splitter and Stretch and Center for a horizontal splitter. You'll also want to set the column width or row height to "Auto" so the cell becomes the same size as the splitter. SplitTheClient.xaml shows both a horizontal and vertical GridSplitter occupying its own cell in nested grids. Buttons occupy the other cells, but any other element could do. The splitting seemed to be a little sluggish so I set the ShowsPreview property to true.