views:

817

answers:

2

I have an ItemsControl that is data bound to a list of decimals. I need to add one extra control to the ItemsControl (an option to specify the number manually). Is there a way to do this in XAML? I know I can manually add the item in the code behind, but I'm trying to understand WPF a little better and want to see if there is a declarative way to do it.

Note that modifying the list I'm binding to so that it includes the extra button (possibly by changing to a list of strings instead of decimals) isn't a good alternative because I want to attach a command to that last button.

Also, adding an extra button after the ItemsControl isn't a good option either, because my control uses a UniformGrid and I want my extra control in that same grid.

Here is my XAML:

<ItemsControl ItemsSource="{Binding PossibleAmounts}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Name="ButtonsGrid">
            </UniformGrid>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button>
                <TextBlock Text="{Binding StringFormat='\{0:C\}'}"/>
            </Button>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Basically, I want one more button in the UniformGrid.

+5  A: 

You can use the CompositeCollection class for this purpose, it combines multiple collections or individual items as the ItemsSource for an ItemsControl.

There's a good example in the MSDN article, or here's another one:

<Grid 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"&gt;
    <Grid.Resources>
        <x:Array x:Key="intData" Type="{x:Type sys:Int32}">
            <sys:Int32>1</sys:Int32>
            <sys:Int32>2</sys:Int32>
            <sys:Int32>3</sys:Int32>
        </x:Array>
        <x:Array x:Key="stringData" Type="{x:Type sys:String}">
            <sys:String>Do</sys:String>
            <sys:String>Re</sys:String>
            <sys:String>Mi</sys:String>
        </x:Array>
    </Grid.Resources>
    <ListBox>
        <ListBox.ItemsSource>
            <CompositeCollection>
                <CollectionContainer Collection="{StaticResource intData}"/>
                <CollectionContainer Collection="{StaticResource stringData}"/>
                <ListBoxItem>One more item!</ListBoxItem>
                <ListBoxItem>Two more items!</ListBoxItem>
            </CompositeCollection>
        </ListBox.ItemsSource>
    </ListBox>
</Grid>
Robert Macnee
This is exactly what I needed. Thank you!
Eddie Deyo
Brilliant! Didn't expect such thing was implemented as a standard feature in WPF!
Dmitry Tashkinov
+3  A: 

The problem with CompositeCollection, though, is that it doesn't inherit the parent element DataContext, so you won't be able to write:

<CollectionContainer Collection={Binding ...}" />

inside it - or rather it'll let you, but you won't get anything out of it. Since the original post required that - {Binding PossibleAmounts} - rather than binding to a StaticResource - it's not really a solution.

See:

http://blogs.msdn.com/nickkramer/archive/2006/08/18/705116.aspx

http://www.vistax64.com/avalon/90-compositecollections-collectioncontainer-binding-issue.html

http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/bd1a78c1-67ef-4d1e-9cbe-70bbe8eb8b44/

very good point. thank you.
frameworkninja
This problem is solved by adding a freezable object to the resources that stores the binding and serves as the source for the collection property binding.
Dmitry Tashkinov