views:

287

answers:

2

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;
    }
}
+1  A: 

ListBox.DisplayMemberPath = "Name";

That will tell the listbox to use the 'Name' property of the DataContext

Stephan
I read his question as to say he is going to bind directly to dictionary.Values which would just be IEnumerable<Drink>, if you bind to the whole dictionary then that's correct.
Stephan
Yeah just .Values. But is the above code enough for binding it? Can you please post xaml for this, as this is what I am trying to learn. Thanks.
Joan Venge
+4  A: 

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>

Edit:

To answer your followup 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 counterintuitive 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 useDynamicResource` 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.

Robert Rossney
Thanks Robert, you are awesome. It really works great. I am very new to wpf so this is very useful to me to try to understand. I have a few questions if you don't mind. 1. How do you find more info about the xaml syntax? For instance what's DynamicResource, Grid.IsSharedSizeScope, SharedSizeGroup, etc? Also I noticed when I first create a control by dragging from the toolbox, I get something like: <ListView ... </> so I need to type </ListView>to insert new lines into it. Is that normal? Maybe that doesn't happen if you type everything instead of using the toolbox.
Joan Venge
See my edit for answers.
Robert Rossney
+1 for a well explained follow up. And a nice example as well ;)
Metro Smurf
Thanks Robert, you are such a great teacher. I wish I could learn these kind of insiders for new things that I want to learn. If I could give you more points, I certainly would.
Joan Venge
One of the primary ways I've learned all this stuff is by answering questions on SO. Someone asks a question that I know maybe half the answer to, and I put the time into figure the other half out, and now I understand the subject better than I did at first. Different people learn things in different ways, of course, but this one really works for me. Like, two weeks ago I didn't have any idea how to use `ListView`, but someone asked a question about it, and, well, now I do.
Robert Rossney
Good idea Robert. I am the opposite, I learn by asking (lots) of questions :O
Joan Venge