1. Problem
You need to implement custom layout logic for child controls inside a parent container.
2. Solution
Implement a custom Panel to encapsulate the custom layout logic.
3. How It Works
The presentation of your controls and other visual elements is a significant portion of ouvrer an irresistible user interface. However, it always helps if the framework that you employ provides of the assistance by carrying out this provision. There are some common scenarios of provision, such as arranging your elements in a pile vertically or horizontally, or specifying their position in terms of lines and columns in a provision like a table. There is also absolute positioning, where you provide there precise X and For your element.
The libraries of Silverlight include several containers of provision which help the process. The containers of provision are elements which can contain other elements such as children and apply a specific logic of provision which arranges the children consequently. The fabric, StackPanel, and roast it found in System.Windows.Controls are some of these containers of provision, with their absolute positioning being composed of logic of provision, ordered stacking, and style of table placing, respectively.
3.1. Motivation and Mechanics
The challenge for framework designers is that it is
hard to foresee all possible layout scenarios and implement a container
for each in the framework. Consequently, there needs to be a way in
which you can easily implement your own layout logic and plug it in so
that it functions seamlessly with the rest of the framework types, just
the way the built-in containers do.
The System.Windows.Controls.Panel abstract class was designed for exactly this purpose. The set of standard built-in layout containers like Grid and StackPanel extend the Panel class to implement their layout logic, and so can you.
To create your custom layout logic, you need to provide implementations of two virtual methods: MeasureOverride() and ArrangeOverride().
At runtime, these two methods are called on any custom panel
implementation you build to give you an opportunity to appropriately
lay out any contained children. Note that these two methods are defined
in the FrameworkElement type, from which Panel itself derives. However, FrameworkElement has no built-in notion of child items, whereas the Panel class does by exposing a Children collection. Therefore, you will use the Panel class as the root for all custom layout containers.
Now, let's look at the MeasureOverride() and ArrangeOverride() methods.
3.2. The MeasureOverride() Method
Layout essentially happens in two passes. In the
first pass, the runtime provides an opportunity for the container to
evaluate the size requirements of all its children and return the total
size requirement based on its layout logic.
This first pass is implemented in MeasureOverride(). The parameter passed in to MeasureOverride() by the runtime is the total availableSize for all children of that container to be laid out. The goal of the MeasureOverride()
method is to look at each child individually, calculate the size
requirements of each, and compute a total size requirement, which is
then returned from MeasureOverride() to the runtime. It is in
computing this calculated total that you apply your layout logic.
However, there is a standard way for measuring the desired size of each
child that goes into that calculation. You need to call the Measure() method on each child, passing in the availableSize parameter, and the child returns its desired size. Measure() is a method implemented in UIElement, and it is a requirement to call Measure() on each child to guarantee accurate size measurement and placement in your layout.
This computed total may be greater than the availableSize
parameter passed in, to indicate that more room is required for ideal
layout of all the children for this container. However, what is finally
granted is up to the runtime, based on the overall UI and the room
available within the plug-in to display all content inside and outside
this container.
3.3. The ArrangeOverride() Method
In the second pass of the layout process, the layout
system calculates the most space it can allocate to a container based
on the rest of the UI, and then calls ArrangeOverride(), passing that value in as the finalSize parameter. Keep in mind that the finalSize value may be less than the desired size that your MeasureOverride() implementation had calculated.
It is now up to your implementation of ArrangeOverride() to figure out a strategy of laying out the child elements within the finalSize determined by the layout system. The actual process of laying each individual child is done by calling the Arrange() method on the child itself. The Arrange() method accepts a Rectangle that determines the final area within which the child should be positioned. The return value from ArrangeOverride() is the finalSize required by the container, and unless your implementation can lay everything out within a space smaller than the finalSize value passed in, this in most cases is the unchanged value contained in the finalSize parameter.
Note that it is mandatory to call the Measure() and Arrange() methods on all children elements to have them laid out and rendered by the layout system. And since that is what you do inside MeasureOverride() and ArrangeOverride(),
implementing overrides for both of these methods is also a requirement
when implementing a layout container like a custom panel.