WPF: Bottom dwelling with an ItemsControl

 Here’s something I just stumbled upon. Not quite intuitive, so I thought I’d write it down. Consider this piece of XAML (you can paste it into XamlPad to try it out):

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <DockPanel LastChildFill="False">
        <Button DockPanel.Dock="Bottom" Background="Yellow" Content="X" />
        <Button DockPanel.Dock="Bottom" Background="Red" Content="X" />
        <Button DockPanel.Dock="Bottom" Background="Blue" Content="X" />
        <Button DockPanel.Dock="Bottom" Background="Green" Content="X" />
        <Button DockPanel.Dock="Bottom" Background="Magenta" Content="X" />
        <Button DockPanel.Dock="Bottom" Background="Black" Content="X" />
        <Button DockPanel.Dock="Bottom" Background="Orange" Content="X" />
    </DockPanel>
</Page>

What it does is pretty obvious: it creates a bunch of buttons and docks them all to the bottom of the panel.

Now look at the following XAML. It uses an ItemsControl to create a dynamic structure based on data binding, that also contains a number of buttons. Similar to before, the buttons are children of a DockPanel and they have the DockPanel.Dock attribute attached and set to the value Bottom.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Page.Resources>
        <x:Array x:Key="sequence" Type="sys:Int32">
            <sys:Int32>1</sys:Int32>
            <sys:Int32>2</sys:Int32>
            <sys:Int32>3</sys:Int32>
            <sys:Int32>4</sys:Int32>
            <sys:Int32>5</sys:Int32>
            <sys:Int32>6</sys:Int32>
            <sys:Int32>7</sys:Int32>
            <sys:Int32>8</sys:Int32>
        </x:Array>
    </Page.Resources>
    <ItemsControl ItemsSource="{Binding Source={StaticResource sequence}}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <DockPanel LastChildFill="False" />
            </ItemsPanelTemplate> 
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Button Content="{Binding}" DockPanel.Dock="Bottom" />
            </DataTemplate>
        </ItemsControl.ItemTemplate> 
    </ItemsControl>
</Page>

If you paste this code into XamlPad, you might be as surprised as I was initially to find that this is the output:

So why is that? The answer can also be found in XamlPad, comparing an important part of the visual trees for both examples:

As you can see, the visual tree for the databound case is quite a bit more complex, and most importantly, the Button instances are wrapped in ContentPresenter instances before being included in the DockPanel. So the reason why the docking doesn’t work is because the DockPanel.Dock property is not attached to the actual children of the DockPanel!

Here’s the solution I found for this problem. It is possible to configure the style for the ContentPresenter that is wrapped around the items, and to attach the property to it instead of the Button itself. The ItemsControl has a property called ItemContainerStyle, and it can be used like this:

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Page.Resources>
        <x:Array x:Key="sequence" Type="sys:Int32">
            <sys:Int32>1</sys:Int32>
            <sys:Int32>2</sys:Int32>
            <sys:Int32>3</sys:Int32>
            <sys:Int32>4</sys:Int32>
            <sys:Int32>5</sys:Int32>
            <sys:Int32>6</sys:Int32>
            <sys:Int32>7</sys:Int32>
            <sys:Int32>8</sys:Int32>
        </x:Array>
    </Page.Resources>
    <ItemsControl ItemsSource="{Binding Source={StaticResource sequence}}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <DockPanel LastChildFill="False" />
            </ItemsPanelTemplate> 
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Button Content="{Binding}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate> 
        <ItemsControl.ItemContainerStyle>
            <Style>
                <Setter Property="DockPanel.Dock" Value="Bottom" />
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>
</Page>

With this change, the result is finally what I want – all the buttons are docked to the bottom correctly.

There may be a second possible solution, by making the ItemControl somehow skip creating that additional wrapper altogether, but I haven’t tried doing that.

2 Comments on WPF: Bottom dwelling with an ItemsControl

  1. Hi,A good post. Thanks for the post.I’d like to ask u a question.How can i raise click event of the buttons inside itemscontrol. Thanks.

    Like

  2. Andy Clarke // May 15, 2009 at 11:48 am // Reply

    Excellent post, hadn’t previously used xaml pad either so thanks for that as well! 🙂

    Like

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s