Display multiple types from a single list in a WPF ListBox?
Asked Answered
L

2

10

I have an ObservableCollection<Object> that contains two different types.

I want to bind this list to a ListBox and display different DataTemplates for each type encountered. I can't figure out how to automatically switch the data templates based on the type.

I have attempted to use the DataType property of the DataTemplate and attempted using ControlTemplates and a DataTrigger, but to no avail, either it nothing shows up, or it claims it can't find my types...

Example Attempt below:

I only have the one data template wired to the ListBox right now, but even that doesn't work.

XAML:

<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
    <DataTemplate x:Key="PersonTemplate">
        <TextBlock Text="{Binding Path=Name}"></TextBlock>
    </DataTemplate>

    <DataTemplate x:Key="QuantityTemplate">
        <TextBlock Text="{Binding Path=Amount}"></TextBlock>
    </DataTemplate>

</Window.Resources>
<Grid>
    <DockPanel>
        <ListBox x:Name="MyListBox" Width="250" Height="250" 
ItemsSource="{Binding Path=ListToBind}"
ItemTemplate="{StaticResource PersonTemplate}"></ListBox>
    </DockPanel>
</Grid>
</Window>

Code Behind:

public class Person
{
    public string Name { get; set; }

    public Person(string name)
    {
        Name = name;
    }
}

public class Quantity
{
    public int Amount { get; set; }

    public Quantity(int amount)
    {
        Amount = amount;
    }
}

public partial class Window1 : Window
{
    ObservableCollection<object> ListToBind = new ObservableCollection<object>();

    public Window1()
    {
        InitializeComponent();

        ListToBind.Add(new Person("Name1"));
        ListToBind.Add(new Person("Name2"));
        ListToBind.Add(new Quantity(123));
        ListToBind.Add(new Person("Name3"));
        ListToBind.Add(new Person("Name4"));
        ListToBind.Add(new Quantity(456));
        ListToBind.Add(new Person("Name5"));
        ListToBind.Add(new Quantity(789));
    }
}
Leis answered 3/8, 2010 at 20:31 Comment(0)
R
7

You say that "it claims it can't find my types." That's a problem that you should fix.

The problem, most likely, is that you're not creating a namespace declaration in the XAML that references your CLR namespace and assembly. You need to put something like this in the XAML's top-level element:

xmlns:foo="clr-namespace:MyNamespaceName;assembly=MyAssemblyName"

Once you do this, XAML will know that anything with the XML namespace prefix foo is actually a class in MyAssemblyName in the MyNamespaceName namespace.

Then you can reference that XML namespace in the markup that created the DataTemplate:

<DataTemplate DataType="{foo:Person}">

You can certainly build a template selector, but that's adding a piece of cruft to your software that doesn't need to be there. There's a place for template selectors in WPF applications, but this isn't it.

Ricky answered 4/8, 2010 at 0:56 Comment(3)
+1 You're right. I wonder why I never have seen this very cool option. Here the msdn-link: msdn.microsoft.com/en-us/library/…Cienfuegos
The DataTemplateSelector does seem like cruft once I put this in. Thanks for the suggestion. I figured there must be an easy way to resolve this, and you have provided it!Leis
<DataTemplate DataType="{foo:Person}"> doesn't work for me, but <DataTemplate DataType="{x:Type foo:Person}"> does. Typo?Cicatrize
C
6

You have to use a DataTemplateSelector. See here for an example.

Addiontal information on MSDN

Cienfuegos answered 3/8, 2010 at 20:36 Comment(1)
You don't have to use a template selector; WPF's default template selection method does exactly what's needed here.Ricky

© 2022 - 2024 — McMap. All rights reserved.