Currently I'm doing some research for a project I'm working on. One of the things I am looking into is creating a custom application layout that should manage itself without requiring developers to write their own layout logic. In this post I am going to show off some of a sample I used to get a feeling with the matter.
First things first
The result of the sample looks like what you see in the screenshot. It's just a bunch of buttons you can add with the "Add button" button. The first button will be slightly larger than the others and will be placed a bit different in the panel.
Creating the custom panel
The panel works with two method overrides:
- MeasureOverride
- ArrangeOverride
Although there are quite a few steps the following things are important to keep in mind when building a custom layout panel. The first step in the drawing process of the contents of a panel is measuring the actual size of each of the children within a panel. WPF and silverlight start at the root of the visual tree and invoke Measure on each of the elements in the element collection at root level. Each of these elements call Measure on their children, until the bottom of the tree is reached.
The second step is arranging the children in the visual tree. This step moves all elements in the tree in the position where they belong. Again this is done by starting at the root of the tree and moving down by invoking Arrange on all the children. Each child then calls Arrange on its children, etc.
The key to making a custom layout panel lies in overriding ArrangeOverride and MeasureOverride. This makes it possible to influence the size of the child elements added to the panel and how they are positioned on the panel. To make my custom panel I implemented an algorithm where the first button is twice as big as the others and the other buttons are aligned to the right in a way Office 2007 does on the ribbon control.
The logic for measuring the controls looks like this:
1: protected override Size MeasureOverride(Size availableSize)
2: {
3: for (int index = 0; index < Children.Count; index++)
4: {
5: Size targetSize = index > 0 ? new Size(24, 24) : new Size(48, 48);
6:
7: Children[index].Measure(targetSize);
8: }
9:
10: return new Size((Children.Count - 1) / 2, 24);
11: }
And finally the logic for arranging the child controls looks like this:
1: protected override Size ArrangeOverride(Size finalSize)
2: {
3: for (int index = 0; index < Children.Count; index++)
4: {
5: if (index == 0)
6: {
7: Children[index].Arrange(new Rect(new Point(0, 0), new Size(48, 48)));
8: }
9: else
10: {
11: bool unevenItem = index % 2 != 0;
12: double offsetX = 48 + (Math.Floor((index - 1) / 2) * 24);
13: double offsetY = unevenItem ? 0 : 24;
14:
15: Children[index].Arrange(new Rect(new Point(offsetX, offsetY), new Size(24, 24)));
16: }
17: }
18:
19: return finalSize;
20: }
Conclusion
Creating custom panels for silverlight and WPF is in fact really simple. It's a matter of getting the algorithm right for measuring and positioning the elements on the panel. The rest is organized by the runtime so that you can add all kinds of elements to your custom panel.
The sample code is attached to this post.