Binding Dictionary<T> to a WPF ListBox
.net, c#, data-binding, listbox, wpf
Solution
Setting the `ItemsSource` on an items control creates a binding to the enumerator for the source object. The enumerator of a `Dictionary<T1, T2>` is of type `IEnumerable<KeyValuePair<T1, T2>>`. So in your item template, you can bind to the `Key` and `Value` properties, and use the path syntax to get specific properties of the key and value.
Here's an example. First the code that creates and populates the dictionary and adds it to the resource dictionary (there are lots of different ways you can expose the dictionary to data binding; this one's simple):
namespace WpfApplication17
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
Dictionary<string, Drink> d = new Dictionary<string, Drink>();
d.Add("A", new Drink("Nehi", 0));
d.Add("B", new Drink("Moxie", 1));
d.Add("C", new Drink("Vernor's", 2));
d.Add("D", new Drink("Canfield's", 3));
Resources["Drinks"] = d;
InitializeComponent();
}
public class Drink
{
public Drink(string name, int popularity)
{
Name = name;
Popularity = popularity;
}
public string Name { get; set; }
public int Popularity { get; set; }
}
}
}
Now the XAML for populating a `ListBox` (though a `ListView` would be easier, because you wouldn't have to define a template this complicated to make it look nice):
<Window x:Class="WpfApplication17.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">
<Grid Margin="10">
<ListBox ItemsSource="{DynamicResource Drinks}" Grid.IsSharedSizeScope="True">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Key" />
<ColumnDefinition SharedSizeGroup="Name" />
<ColumnDefinition SharedSizeGroup="Popularity" />
</Grid.ColumnDefinitions>
<TextBlock Margin="2" Text="{Binding Key}" Grid.Column="0"/>
<TextBlock Margin="2" Text="{Binding Value.Name}" Grid.Column="1"/>
<TextBlock Margin="2" Text="{Binding Value.Popularity}" Grid.Column="2"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
The XAML for a `ListView` is a lot simpler, and displays more nicely to boot:
<ListView ItemsSource="{DynamicResource Drinks}">
<ListView.View>
<GridView>
<GridViewColumn Header="Key"
DisplayMemberBinding="{Binding Key}" />
<GridViewColumn Header="Name"
DisplayMemberBinding="{Binding Value.Name}" />
<GridViewColumn Header="Popularity"
DisplayMemberBinding="{Binding Value.Popularity}" />
</GridView>
</ListView.View>
</ListView>
To answer your follow-up questions:
I recommend Adam Nathan's Windows Presentation Foundation Unleashed. The chapter on layout with panels explains how the Grid works in considerable detail. The `Grid`'s pretty counter-intuitive in a lot of ways. You think that you'd want to create a single `Grid` that contains many items, but the number of rows and columns in a `Grid` isn't dynamic. So what you do instead is create a `Grid` for each item, and then use the shared-size functionality to make sure that the columns in each `Grid` are the same size. The `ListView` has quirks of its own, but it's a lot more straightforward for the common "display multiple items in a grid" use case.
`DynamicResource` is a markup extension that works a lot like `StaticResource`. The difference is that when the XAML parser resolves `StaticResource` as it parses it - if the referenced resource isn't in the resource dictionary, it throws an exception. `DynamicResource` resolves the reference if the item gets added to the dictionary later. There's a bit of a performance cost to this, but it's negligible in most cases. The code I posted works if you use `StaticResource`, because the XAML gets parsed in `InitializeComponent`. But I don't like having to remember that, so I use `DynamicResource` by default if I'm binding to something that I'm adding to the resource dictionary in code and just don't worry about whether it's being created before or after the XAML gets parsed.
And as for the toolbox: Maybe I'll start using that in VS2010, but I find the one in 2008 unusably buggy. And not very useful anyway. I do almost all of my layout work in the editor, and some in Kaxaml. I think the visual editor in 2008 actually made learning WPF harder, because it imposed an abstraction layer between me and the XAML (which is itself an abstraction layer between me and the WPF object model). And it's not a very good abstraction layer: the design decisions that went into deciding what should be hidden and what should be visible aren't, it seems to me, the right ones. Also it's buggy as hell.
Problem
Given a dictionary of `<string, Drink>`, how would you bind the `dictionary.Values` to a WPF ListBox, so that the items use the `.Name` property? ``` struct Drink { public string Name { get; private set; } public int Popularity { get; private set; } public Drink ( string name, int popularity ) : this ( ) { this.Name = name; this.Popularity = popularity; } } ```