7

Let's say I have a collection of objects of different classes. Each class has its UserControl DataTemplated in a resource file.

Now I want to use ItemsControl to display the collection, but I want an Border or Expander around each item.

I would expect something like this to work:

<ItemsControl ItemsSource="{Binding MyObjects}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border BorderBrush="Black" BorderThickness="3">
                <ContentPresenter/>
            </Border>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

But the ContentPresenter seems to pick ItemTemplate, because I get a stack overflow.

How do I get each Item's DataTemplate inside the ItemTemplate?

Guge
  • 4,569
  • 4
  • 35
  • 47
  • I think you need to provide some detail about the error you are experiencing. Does your output window provide any clues? – benPearce Oct 23 '10 at 11:50
  • I have a button that adds an item to MyObjects. After I click it, there is nothing new in Output. I get a System.StackOverflowException from WindowsBase.dll. There is no source available, there is no disassembly to show me, there is no details from the exception itself. ("Cannot evalueate expression because the current thread is in a stack overflow state"). The call stack in VS does not give me anything. But if I remove the ContentPresenter from the ItemTemplate, I get only an empty Border for each item, with no stack overflow. – Guge Oct 23 '10 at 12:38

2 Answers2

15

Normally you might consider doing this by templating the item container. The problem is the "generic" ItemsControl uses the ContentPresenter as its item container. So even if you try and set a style with ItemContainerStyle you will find you cannot supply a template because the ContentPresenter does not support control templating (it does support data templating but no use here).

To use a templatable container you will have to derrive from ItemsControl like in this example.

An alternative might be just to use the ListBox control instead. Then you can just provide a custom template by setting a ListBoxItem template via a style.

You can read more about containers here .

(With your permissen I'm adding the solution to your answer, Guge)

    <ListBox ItemsSource="{Binding MyObjects}" Grid.Column="1">
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
        <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListBoxItem}">
                            <Border BorderBrush="Black" BorderThickness="3">
                                <ContentPresenter/>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>
Guge
  • 4,569
  • 4
  • 35
  • 47
Jack Ukleja
  • 13,061
  • 11
  • 72
  • 113
  • Thanks, so much. I sometimes overlook that a Template can be a part of a style. There is something counterintuitive about it, hopefully only inside my own cranial cavity. – Guge Oct 23 '10 at 13:13
  • It is counter-intuitive, as least when compared to other technologies! What it comes down to is remembering that the Style can set any property, and the Template itself is just another property (which is the WPF "magic"). – Jack Ukleja Oct 23 '10 at 13:22
2

I'd just do the following:

<ItemsControl.ItemTemplate>
    <DataTemplate>
        <Border BorderBrush="Black" BorderThickness="3">
            <ContentControl Content={Binding} />
        </Border>
    </DataTemplate>
</ItemsControl.ItemTemplate>

As data context inside DataTemplate tag is an item from the source collection, we can use a ContentControl to display this item. {Binding} means that we're binding to the whole data context. All DataTemplates for your items will be implicitly applied in the same way as if we didn't specify ItemsControl.ItemTemplate.

N. Kudryavtsev
  • 3,556
  • 1
  • 26
  • 30