This chapter is taken from book "Programming Windows Phone 7" by Charles Petzold published by Microsoft press. http://www.charlespetzold.com/phone/index.html
Panels are written entirely in code. There is no XAML involved. When you write a Panel derivative, you'll probably be defining a couple properties to make the panel more flexible. Because these properties are almost always dependency properties, apart from defining those custom properties, a panel always overrides two methods: MeasureOverride and ArrangeOverride, which correspond to the two passes of layout. The first pass is for each parent to determine the size of its children; the second pass is for the parent to arrange its children relative to itself.
For both these jobs, the panel accesses the Children property that your panel inherits from Panel.
MeasureOverride and ArrangeOverride are protected virtual methods. Measure and Arrange are public sealed methods. Your panel overrides MeasureOverride and ArrangeOverride. In MeasureOverride, the panel calls Measure on all its children; within ArrangeOverride the panel calls Arrange on all its children.
A panel does not need to worry about the following properties that might be set on itself or its children:
- HorizontalAlignment and VerticalAlignment
- Margin
- Visibility
- Opacity (does not affect layout at all)
- RenderTransform (does not affect layout at all)
- Height, MinHeight, and MaxHeight
- Width, MinWidth, and MaxWidth
A Single-Cell Grid Clone
Perhaps the simplest panel of all is the Grid that contains no rows or columns, commonly referred to as a "single-cell Grid." I've been using the Grid named ContentPanel as a single-cell Grid; as you've seen, the Grid can host multiple children, but they overlap within the same area. Let's duplicate the functionality of a single-cell Grid with a class named SingleCellGrid.
In a new project named SingleCellGridDemo, I right-clicked the project name, selected Add and New Item from the menu, and picked Class from the dialog box, naming it SingleCellGrid.cs. In the file, I made sure the class was public and derived from Panel.
This class overrides the two methods MeasureOverride and ArrangeOverride. Here's:
namespace SingleCellGridDemo
{
public classSingleCellGrid :Panel
{
protected override Size MeasureOverride(Size availableSize)
{
Size compositeSize =new Size();
foreach (UIElement child in Children)
{
child.Measure(availableSize);
compositeSize.Width = Math.Max(compositeSize.Width, child.DesiredSize.Width);
compositeSize.Height = Math.Max(compositeSize.Height, child.DesiredSize.Height);
}
return compositeSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
foreach (UIElement child in Children)
{
child.Arrange(newRect(newPoint(), finalSize));
}
return base.ArrangeOverride(finalSize);
}
}
}
Now to test it out. The MainPage.xaml file in the SingleCellGridDemo project needs to reference this custom class. In the root element, an XML namespace declaration associates the name "local" with the .NET namespace used by the project:
xmlns:local="clr-namespace:SingleCellGridDemo"
The MainPage.xaml file nests the SingleCellGrid in the content grid, and then
fills it with the same four elements:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<local:SingleCellGrid>
<TextBlock
Text="TextBlock
aligned at right bottom"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
/>
<Image
Source="Images/BuzzAldrinOnTheMoon.png"
/>
<Ellipse
Stroke="{StaticResource
PhoneAccentBrush}"
StrokeThickness="24"
/>
<TextBlock
Text="TextBlock
aligned at left top"
HorizontalAlignment="Left"
VerticalAlignment="Top"
/>
</local:SingleCellGrid>
</Grid>
A Custom Vertical StackPanel
The next Panel derivative I'll show you is the StackPanel, and you'll see how it differs from the single-cell Grid. To keep the code simple, and to avoid defining properties, I'm going to call this custom class VerticalStackPanel. Here's the MeasureOverride method:
protectedoverrideSize MeasureOverride(Size availableSize)
{
Size compositeSize = newSize();
foreach (UIElement child in Children)
{
child.Measure(newSize(availableSize.Width,Double.PositiveInfinity));
compositeSize.Width = Math.Max(compositeSize.Width, child.DesiredSize.Width);
compositeSize.Height += child.DesiredSize.Height;
}
return compositeSize;
}
As usual, the MeasureOverride method loops through all its children and calls Measure on each of them. But notice that the Size offered to each child here consists of the width of the VerticalStackPanel itself and a height of infinity.
In SingleCellGrid, the ArrangeOverride method positioned each of its children in the same location. The VerticalStackPanel needs to stack its children. For that reason, it defines local variables named x and y:
protectedoverrideSize ArrangeOverride(Size finalSize)
{
double x = 0, y = 0;
foreach (UIElement child in Children)
{
child.Arrange(newRect(x, y, finalSize.Width, child.DesiredSize.Height));
y += child.DesiredSize.Height;
}
returnbase.ArrangeOverride(finalSize);
}
The
x variable remains 0 throughout but the y variable
is incremented based on the Height property of each child's DesiredSize. The
Arrange measure is called with x and
y indicating the location of the child relative to the panel's upper-left
corner. The MainPage.xaml file in the VerticalStackPanelDemo project is the same
as the one I showed at the outset of this chapter but using
VerticalStackPanel:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<local:VerticalStackPanel>
<TextBlock
Text="TextBlock
aligned at right bottom"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
/>
<Image
Source="Images/BuzzAldrinOnTheMoon.png"
/>
<Ellipse
Stroke="{StaticResource
PhoneAccentBrush}"
StrokeThickness="24" />
<TextBlock
Text="TextBlock
aligned at left top"
HorizontalAlignment="Left"
VerticalAlignment="Top"
/>
</local:VerticalStackPanel>
</Grid>
The Retro Canvas
The Canvas is certainly the most old-fashioned sort of panel. To position elements within the Canvas you supply horizontal and vertical coordinates relative to the top-left corner.
The Canvas has two unusual characteristics:
- In its MeasureOverride method, Canvas always calls Measure on its children with a size consisting of both an infinite width and an infinite height. (Accordingly, in ArrangeOverride,Canvas sizes each child based on the child's DesiredSize.)
- From its MeasureOverride method, Canvas returns a size consisting of a zero width and a zero height.




Join the conversation! Your thoughts help the community grow.