views:

3748

answers:

3

I'm fairly new to WPF and trying to understand how best to extend the ListBox control. As a learning experience, I wanted to build a ListBox whose ListBoxItems display a CheckBox instead of just text. I got that working in a basic fashion using the ListBox.ItemTemplate, explicitly setting the names of the properties I wanted to databind to. An example is worth a thousand words, so...

I've got a custom object for databinding:

public class MyDataItem {
 public bool Checked { get; set; }
 public string DisplayName { get; set; }

 public MyDataItem(bool isChecked, string displayName) {
  Checked = isChecked;
  DisplayName = displayName;
 }
}

(I build a list of those and set ListBox.ItemsSource to that list.) And my XAML looks like this:

    <ListBox Name="listBox1">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <CheckBox IsChecked="{Binding Path=Checked}" Content="{Binding Path=DisplayName}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

This works. But I want to make this template reusable, i.e. I'll want to bind to other objects with properties other than "Checked" and "DisplayName". How can I modify my template such that I could make it a resource, reuse it on multiple ListBox instances, and for each instance, bind IsChecked and Content to arbitrary property names?

+2  A: 

Create your DataTemplate as a resource and then reference it using the ItemTemplate property of the ListBox. MSDN has a good example

<Windows.Resources>
  <DataTemplate x:Key="yourTemplate">
    <CheckBox IsChecked="{Binding Path=Checked}" Content="{Binding Path=DisplayName}" />
  </DataTemplate>
...
</Windows.Resources>

...
<ListBox Name="listBox1"
         ItemTemplate="{StaticResource yourTemplate}"/>
MrTelly
I think the questioner is asking how to make 'Checked' and 'DisplayName' parameters that are supplied to the template, so that the same appearance can be used elsewhere, but possibly with a different binding.
Jonathan
Hmm, yep I see you're right, I'm not sure that that is possible, I'll await a better answer
MrTelly
Jonathan's right; I'd like to be able to bind other types of objects to the same template. Thanks for the try, though!
Matt Winckler
+2  A: 

If you wanted one way display then you could use a converter:

class ListConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return ((IList<MyDataItem>)value).Select(i => new { Checked = i.Checked2, DisplayName = i.DisplayName2 });
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Then the xaml would look something like this:

<Window.Resources>
    <this:ListConverter x:Key="ListConverter" />
</Window.Resources>
<ListBox ItemsSource="{Binding Path=Items, Converter={StaticResource ListConverter}}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <CheckBox IsChecked="{Binding Path=Checked, Mode=OneWay}" Content="{Binding Path=DisplayName, Mode=OneWay}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

That data template you could make generic like above. Two way binding would be a fair bit more difficult.

I think you are better off making your base classes implement a ICheckedItem interface which exposes the generic properties that you want the datatemplates to bind to?

Jake Ginnivan
Interesting approach--thanks! I'll need two-way binding, so I'll probably avoid the converter route. However, the ICheckedItem opens up new possibilities I hadn't thought of. Interfaces may end up being the way to go.
Matt Winckler
+3  A: 

The easiest way is probably to put the DataTemplate as a resource somewhere in your application with a TargetType of MyDataItem like this

<DataTemplate DataType="{x:Type MyDataItem}">
    <CheckBox IsChecked="{Binding Path=Checked}" Content="{Binding Path=DisplayName}" />
</DataTemplate>

You'll probably also have to include an xmlns to your local assembly and reference it through that. Then whenever you use a ListBox (or anything else that uses a MyDataItem in a ContentPresenter or ItemsPresenter) it will use this DataTemplate to display it.

Bryan Anderson
Fascinating! I hadn't realized you could apply the template to the data object itself. (BTW, I found that "TargetType" should actually be "DataType" on a DataTemplate.) The drawback to this, though, is that I would have to define templates for each type of datasource...which I was trying to avoid.
Matt Winckler
Thanks, I've fixed the error. No matter what you'll have to tell the UI which field is IsChecked and which is the Content. You just have to decide where that happens. I tend to prefer it on the DataTemplate level because it has tended to be the easiest place to maintain in my experience.
Bryan Anderson