Export (0) Print
Expand All

Custom Panels

Silverlight

Silverlight provides several layout containers: Canvas, StackPanel, and Grid. If you are creating a complex layout that is not easily achieved by using the provided containers, you can create a custom panel, which allows you to define layout behavior for the panel's children. To do this, derive from Panel and override its MeasureOverride and ArrangeOverride methods.

This topic contains the following sections.

Before following the steps in this topic, you should be familiar with how objects are sized and positioned in Silverlight. For details, see Silverlight Layout System.

The first step in a panel's layout pass is to measure each child and determine how much space the panel will allocate to that child. In addition, return the amount of space available to the entire panel.

The following example shows an override of the MeasureOverride method for a panel (BlockPanel) that puts 9 children in a 3x3 grid where each cell is 100x100.


// First measure all children and return available size of panel
protected override Size MeasureOverride(Size availableSize)
{

    // Measure first 9 children giving them space up to 100x100, remaining children get 0x0 
    int i =0;
    foreach (FrameworkElement child in Children)
    {
        if (i < 9)
        {
            child.Measure(new Size(100, 100));
        }
        else
        {
            child.Measure(new Size(0, 0));
        }

        i++;
    }


    // return the size available to the whole panel, which is 300x300
    return new Size(300,300);
}


In MeasureOverride, you must call the Measure method of each child element, passing the space that the panel can allocate. Then the layout system calculates the DesiredSize of each child element based on the available size. In this example, we allocate 100x100 to the first 9 children and 0x0 to the remaining children. The size that the parent element allocates to the child can be based on the availableSize value that is passed into the MeasureOverride method. The value of availableSize represents the area that the panel can occupy in the layout.

When Measure is called, the layout system determines the DesiredSize of the child element based on the availableSize passed to Measure and the natural size of the element. The natural size is determined by the element's Width and Height properties or the native size of an image. The DesiredSize is generally set to the lesser of the availableSize and the natural size.

After the DesiredSize of the children has been set, the panel must determine how much space to request from its parent as the return value from its override of MeasureOverride. The appropriate value can be determined in a number of ways:

  • Calculated based on the total DesiredSize values of all the child elements.

  • A predetermined size, as in the current example.

  • A very large size, which will allow the panel excess space for arranging its children.

Caution note Caution:

Do not return the availableSize value that was passed to MeasureOverride. The return value for MeasureOverride must be a finite value, but the availableSize passed in can be infinity in some scenarios.

After the Measure pass is complete, the Arrange pass begins. In the Arrange pass, you must determine the position and size of each child's layout slot and set the final size of the panel.

The following code shows the ArrangeOverride method for the BlockPanel from the Measuring section.


// Second arrange all children and return final size of panel
protected override Size ArrangeOverride(Size finalSize)
{
    // Get the collection of children
    UIElementCollection mychildren = Children;

    // Get total number of children
    int count = mychildren.Count;

    // Arrange children
    // We're only allowing 9 children in this panel.  More children will get a 0x0 layout slot.
    int i;
    for (i = 0; i < 9; i++)
    {

        // Get (left, top) origin point for the element in the 3x3 block
        Point cellOrigin = GetOrigin(i, 3, new Size(100, 100));

        // Arrange child
        // Get desired height and width. This will not be larger than 100x100 as set in MeasureOverride.
        double dw = mychildren[i].DesiredSize.Width;
        double dh = mychildren[i].DesiredSize.Height;

        mychildren[i].Arrange(new Rect(cellOrigin.X, cellOrigin.Y, dw, dh));

    }

    // Give the remaining children a 0x0 layout slot
    for (i = 9; i < count; i++)
    {
        mychildren[i].Arrange(new Rect(0, 0, 0, 0));
    }


    // Return final size of the panel
    return new Size(300, 300);
}

// Calculate point origin of the Block you are in
protected Point GetOrigin(int blockNum, int blocksPerRow, Size itemSize)
{
    // Get row number (zero-based)
    int row = (int)Math.Floor(blockNum / blocksPerRow);

    // Get column number (zero-based)
    int column = blockNum - blocksPerRow * row;

    // Calculate origin
    Point origin = new Point(itemSize.Width * column, itemSize.Height * row);
    return origin;

}


In ArrangeOverride, call Arrange on each child, passing a Rect. This sets the point of origin, the height, and the width of the child's layout slot in the parent panel. In this example, the first 9 children are placed in one cell of the 3x3 grid based on their order in the Children collection. The first child is placed in the top-left cell, so the Rect passed is (0,0,100,100), which means it will be placed in the top-left corner of the panel with a width and height of 100. The first 9 children are given a layout slot of 100x100. Any remaining children are given a layout slot of 0x0. If the child's desired size is larger than the allocated space, it will be clipped. Each child element positions itself in the layout slot based on other layout properties such as HorizontalAlignment, VerticalAlignment, and Margin.

Note Note:

Your custom panel should not consider settable properties such as Visibility, Margin, or MinWidth when determining layout. The Silverlight layout system will handle all these properties. So, for example, you do not need to skip the layout for elements with Visibility of Collapsed because this will be handled by the layout system.

After each child is arranged, return the final size of the panel. This is the size that the panel will request from its parent container. In this example, the BlockPanel is always set to be 300x300.

In order to use your panel in XAML, you must define the XML namespace for your custom panel class. Then when you create an instance of your custom panel, you reference it using your XML namespace.

The following code example shows how to create an instance of the BlockPanel.

Run this sample


 <UserControl x:Class="BlockPanel.Page"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
 xmlns:my="clr-namespace:BlockPanel;assembly=BlockPanel"
>



<Grid x:Name="LayoutRoot" Background="Black">

    <my:BlockPanel Background="Black" HorizontalAlignment="Left" VerticalAlignment="Top" >
        <Rectangle Fill="Red" Height="500" Width="500" Margin="2"/>
        <Rectangle Fill="Salmon" Height="500" Width="500" Margin="2"/>
        <Rectangle Fill="Orange" Height="500" Width="500" Margin="2"/>
        <Rectangle Fill="Yellow" Height="500" Width="500" Margin="2"/>
        <Rectangle Fill="Lime" Height="500" Width="500" Margin="2"/>
        <Rectangle Fill="Green" Height="500" Width="500" Margin="2"/>
        <Rectangle Fill="Turquoise" Height="500" Width="500" Margin="2"/>
        <Rectangle Fill="Blue" Height="500" Width="500" Margin="2"/>
        <Rectangle Fill="Purple" Height="500" Width="500" Margin="2"/>
        <TextBlock Text="This Text Does Not Appear"/>
    </my:BlockPanel>

</Grid>


If you create a custom control that contains a lot of scrollable content (similar to a ListBox) and the control loads slowly or does not scroll smoothly, consider using virtualization. The word "virtualize" refers to a technique by which a subset of UI elements are generated from a larger number of data items based on which items are visible on-screen. Generating many UI elements when only a few elements might be on the screen can adversely affect the performance of your application. You can virtualize a custom control with scrollable content by deriving your control from VirtualizingStackPanel. For more information, see VirtualizingStackPanel.

Community Additions

ADD
Show:
© 2014 Microsoft