#1,016 – Displaying a Collection of Items in a WrapPanel

You can use data binding to bind a collection of items to a WrapPanel by using the WrapPanel as the ItemsPanel for a simple ItemsControl.  (You could do the same thing with a ListBox).

In the example below, we bind to a collection of Actor objects and set the DataTemplate of our ItemsControl to just display the image of each actor.  The ItemsControl will resize to fit the containing window (via the StackPanel) and the WrapPanel will manage changing the layout of the images as the window size changes.

    <ItemsControl ItemsSource="{Binding ActorList}" Margin="20">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Image Source="{Binding Image}" Height="100"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>

1016-001
1016-002
1016-003

Advertisement

#897 – Content on a TabItem Does Not Automatically Wrap

The individual tabs on a TabControl are each represented by a TabItem.  TabItem is a content control, which means that it contains a single element.  That element is typically a panel, which in turn contains child controls.

If the element that you set for the content of a TabItem is too wide to be completely displayed within the TabControl, it will not automatically wrap.  Below, a horizontally-oriented StackPanel is placed on the first tab and the content does not fit.

    <TabControl Margin="10">
        <TabItem Header="Emperors">
            <StackPanel Orientation="Horizontal">
                <Image Source="Augustus.jpg" Height="100" Margin="5"/>
                <Image Source="Tiberius.jpg" Height="100" Margin="5"/>
                <Image Source="Caligula.jpeg" Height="100" Margin="5"/>
                <Image Source="Claudius.jpg" Height="100" Margin="5"/>
                <Image Source="Nero.jpg" Height="100" Margin="5"/>
            </StackPanel>
        </TabItem>
    </TabControl>

897-001
If you want content on the tab to wrap, you need to use a panel that knows how to wrap its own content (e.g. a WrapPanel).

    <TabControl Margin="10">
        <TabItem Header="Emperors">
            <WrapPanel Orientation="Horizontal">
                <!-- ... -->

897-002

#470 – Elements that Support RightToLeft Flow

Every FrameworkElement has a FlowDirection property that can be either LeftToRight (the default), or RightToLeft.  For panel elements that lay out a series of child elements, this property indicates in which direction the layout should be done.

The FlowDirection property is typically used for cultures where text flows from right to left, e.g. Arabic.  However, you can use this property whenever a right-to-left layout would make sense.

Below are some examples of elements that can use RightToLeft flow.

A Calendar control.

A DatePicker

A Menu, with MenuItem elements

A ListBox

A TreeView

A ProgressBar

A Grid

A StackPanel with Horizontal orientation

#443 – Reversing the Flow Direction in a WrapPanel

You can set a WrapPanel to flow its children from right to left, rather then from left to right by setting the FlowDirection property to RightToLeft.  (The default is LeftToRight).

For a horizontally oriented WrapPanel, elements will fill in the top row from right to left, flowing to the second row when the first one fills up.  For a vertically oriented WrapPanel, elements will fill in the rightmost column from top to bottom and then move to the next column to the left when the first column fills up.

Below are examples of each combination of Orientation and FlowDirection.

Orientation = Horizontal, FlowDirection = LeftToRight (the default).

Orientation = HorizontalFlowDirection = RightToLeft.

Orientation = VerticalFlowDirection = LeftToRight (the default).

Orientation = VerticalFlowDirection = RightToLeft.

You would normally use right-to-left flow in culture that present text from right to left (e.g. Arabic).  But you can also use it whenever this layout makes sense for your application.

#442 – WrapPanel Child Elements Can Be Clipped

By default a WrapPanel will size it’s rows (in Horizontal orientation) or columns (in Vertical orientation) to the size of the tallest/widest element.  This means that no element is ever clipped due to row/column sizing, though they may be clipped if the WrapPanel itself has an explicit size specified.

Child elements can also be clipped if an element ends up larger than the specified ItemWidth and ItemHeight properties.  In the example below, ItemHeight has been set to 50, which is shorter than the Label that has three lines.

#441 – Setting a Consistent Height/Width for Child Elements in a WrapPanel

By default, child elements in a WrapPanel size to fit the largest item on a particular row or column (if their alignment is set to Stretch), or align themselves relative to the largest item on the row or column.

In the example below, the label with 3 lines makes the entire row larger.  But the second row is shorter because all of the labels in that row just have a single line.

You can make all of the child elements in a WrapPanel the same height by setting the WrapPanel’s ItemHeight property to some fixed value.

    <WrapPanel Orientation="Horizontal" ItemHeight="60">
        <Label Background="AliceBlue" Content="William I" VerticalAlignment="Center"/>
        <!-- etc -->

Now the child elements in the second row are the same height as the ones in the first row (and the labels are centered vertically in this larger space).

You can also set a consistent item width using the ItemWidth property.

#440 – How Alignment Properties Behave in a WrapPanel

The alignment properties, when specified for children of a WrapPanel, dictate the alignment within the current row (if Orientation is Horizontal) or column (if Orientation is Vertical).

For example, the VerticalAlignment properties of controls in a WrapPanel whose Orientation is Horizontal indicate the alignment of the control with respect to the other controls within the same row.

In the example below, the William II label is set artificially high, which then dictates the height of a row that it appears in.  Other controls on that same row will then align themselves relative to this height.

    <WrapPanel Orientation="Horizontal">
        <Label Background="AliceBlue" Content="William I" VerticalAlignment="Center"/>
        <Label Background="AntiqueWhite" Content="William II" VerticalAlignment="Center" Height="60"/>
        <Label Background="AliceBlue" Content="Henry I" VerticalAlignment="Bottom"/>
        <Label Background="AntiqueWhite" Content="Stephen" VerticalAlignment="Top"/>
        <Label Background="AliceBlue" Content="Matilda"  VerticalAlignment="Stretch"/>
        <Label Background="AntiqueWhite" Content="Henry II" VerticalAlignment="Center"/>
        <Label Background="AliceBlue" Content="Richard I" VerticalAlignment="Center"/>
        <Label Background="AntiqueWhite" Content="John" VerticalAlignment="Center"/>
    </WrapPanel>

#400 – Using a WrapPanel as the Items Panel for a ListBox

The ItemsPanel property of a ListBox specifies the template that defines the panel used to contain the elements of the ListBox.  You can override the normal vertically stacked layout of a ListBox by defining your own template.

If you set the ItemsPanel template to contain a WrapPanel, the ListBox will show its child elements left to right, wrapping to the next row when each row fills up.

In the example below, we bind a ListBox to a list of movies, specify an ItemTemplate that dictates how each item appears, and then specify a template for the ItemsPanel that contains a WrapPanel.

    <Grid>
        <ListBox ItemsSource="{Binding MovieList}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Vertical">
                        <Image Source="{Binding Image}" Stretch="None"/>
                        <Label Content="{Binding TitleWithYear}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel IsItemsHost="True" Orientation="Horizontal"  />
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
        </ListBox>
    </Grid>

Notice that the layout changes as we resize the parent window.

#399 – WrapPanel Will Change Layout of its Children as its Size Changes

We saw that a WrapPanel will lay out its children either horizontally, filling a row at a time, or vertically, filling a column at a time.

The WrapPanel will also re-calculate the layout of its children whenever the size of the WrapPanel itself changes.  This will cause the location of the child elements to change.

Assume that we have the following WrapPanel whose Orientation property is set to Horizontal.  It lays out its child elements left to right on the first row and then starts a second row when the first row fills.

If we make the WrapPanel wider by making the Window wider, we can fit more child elements onto each row.

Similarly, if we make the WrapPanel narrower, we’ll fit fewer elements onto each row and so we’ll end up with more rows.

#398 – WrapPanel Element

The WrapPanel element serves as a container for a collection of child elements, positioning its children in one of two ways:

  • In a horizontal orientation (the default).  Child elements are added left to right, until a row fills up, and then wrapped to the next row
  • In a vertical orientation.  Child elements are added top to bottom, until a column fills up, and then wrapped to the next column
    <WrapPanel Orientation="Horizontal">
        <Label Background="AliceBlue" Content="William I" />
        <Label Background="AntiqueWhite" Content="William II"/>
        <!-- etc -->
    </WrapPanel>

    <WrapPanel Orientation="Vertical">
        <Label Background="AliceBlue" Content="William I" />
        <Label Background="AntiqueWhite" Content="William II"/>
        <!-- etc -->
    </WrapPanel>