WPF ListBox using ItemsSource and ItemTemplate

2019-05-16 08:27发布

问题:

I'm confused as to how bindings are resolved when I have both an ItemsSource and an ItemTemplate in a WPF ListBox.

I have an ObservableCollection<int> called ListOfIndexes. For each index, I want to look up its record in a database table. I hope to do that in the IndexToObjectDescriptionConverter.

<ListBox ItemsSource="{Binding ListOfIndexes}" 
         ItemTemplate="{Binding Converter={StaticResource IndexToObjectDescriptionConverter}}" />

But a breakpoint in the converter is telling me that the value being read in by the ItemTemplate binding is of the window itself — i.e., the DataContext of the ItemsSource and ItemsTemplate is the same.

Pardon a bit of candidness, but this seems DUMB. The entire point of the ItemTemplate is to render each element within the ItemsSource, so I guess I figured that the DataContext of the ItemTemplate would be the individual element being rendered.

So, that said, how do I tell the ItemTemplate that it should worry about the individual elements represented by the ItemsSource and not use the entire window's DataContext?

回答1:

You need to use a data template for the ItemTemplate. This is then applied to each item in the list

MSDN docs are here: http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.itemtemplate(v=vs.110).aspx

The issue her is about data context scope. When you bind any property on the ListBox, it will use the data context of the ListBox - hence why that data context is being passed to the converter. If you set a data template inside the ItemTemplate, it will apply that template to each item in the list. I guess based on the simple code you've provided you would need to have the converter inside the data template:

<ListBox ItemsSource="{Binding ListOfIndexes}">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <ContentControl Content="{Binding Converter={StaticResource IndexToObjectDescriptionConverter}}"/>
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

In this case, the ContentControl will be rendered for each item, with that item as it's data context.



回答2:

Firstly, I'd advise that you have a good read of the Data Templating Overview page in MSDN so that you can get a better understanding of this data binding process.

I want to look up its record in a database table. I hope to do that in the IndexToObjectDescriptionConverter.

That is your first mistake. An IValueConverter is responsible for converting data bound values, not accessing databases. Access your data in your view model and populate public properties with the results. Then data bind those properties to UI controls in the XAML.

Pardon a bit of candidness, but this seems DUMB

Only to those that do not understand the situation.

how do I tell the ItemTemplate that it should worry about the individual elements represented by the ItemsSource and not use the entire window's DataContext?

You don't tell it anything... simply defining the correct XAML is enough:

In Resources:

<DataTemplate x:Key="SomeDataTemplate">
    <!-- The DataContext here is set to an item from the data bound collection -->
</DataTemplate>

In XAML:

<ListBox ItemsSource="{Binding ListOfIndexes}" 
     ItemTemplate="{StaticResource SomeDataTemplate}" />

That's it.