Hands-on MVVM 3: ItemsSource and SelectedItem

display_asI presented this material at Dallas XAML last night (May 4, 2010). If you attended, I hope you had as much fun as I did. If not, just download the demo code and follow along.

In part 2, we saw why we need to fire PropertyChanged events. It’s not for the benefit of the properties that the view is changing directly. It’s for the dependent properties.

Display strategy
Now we want to add another new feature to the system. We want to let the user choose how a person is displayed. They can choose from among three display strategies: last-first, first-last, or email.

The chosen display strategy is stored with the person. The user can choose to display one person using their formal name (last-first), and another using their informal name (first-last). It is not a global setting.

The user selects a display strategy from a combo box. We add this to the person view.

<ComboBox Grid.Row="4" Grid.Column="1" x:Name="displayAs"
    ItemsSource="{Binding DisplayAsOptions}" SelectedItem="{Binding DisplayAs}"/>

This combo box needs to show a list of options. We’ll generate these options by applying different display strategies to the person.

public IEnumerable<string> DisplayAsOptions
{
    get
    {
        return Enum.GetValues(typeof(DisplayStrategy))
            .OfType<DisplayStrategy>()
            .Select(displayStrategy => DisplayUsingStrategy(displayStrategy));
    }
}

public string DisplayUsingStrategy(DisplayStrategy displayStrategy)
{
    if (displayStrategy == DisplayStrategy.LastFirst)
        return string.Format("{0}, {1}", _lastName, _firstName);
    else if (displayStrategy == DisplayStrategy.FirstLast)
        return string.Format("{0} {1}", _firstName, _lastName);
    else
        return _email;
}

The DisplayAsOptions property gets all values of the DisplayStrategy enumeration and projects that set through the DisplayUsingStrategy method. The DisplayUsingStrategy method formats the person for the given strategy. The result is a collection of strings.

When the user selects one of those strings, they will set the DisplayAs property. Since DisplayAsOptions is an IEnumerable<string>, DisplayAs has to be a string.

public string DisplayAs
{
    get { return DisplayUsingStrategy(_displayAs); }
    set
    {
        _displayAs = (DisplayStrategy)DisplayAsOptions
            .ToList()
            .FindIndex(option => option == value);
    }
}

The DisplayAs getter just applies the current display strategy for the person. The setter, however, has to find the selected string in the list of options. The index of this string corresponds to the ordinal value of the enumeration that generated it.

The important thing here is the duality of the DisplayAsOptions and DisplayAs properties. DisplayAsOptions has type IEnumerable<string>. DisplayAs has type string. These types must agree, because one is data bound to ItemsSource and the other to SelectedItem.

Update the title

Now that the user can select the display strategy for the person, we should use that display strategy in the title of the window. We make the necessary change to the Title property.

public string Title
{
    get
    {
        return "Person - " + DisplayUsingStrategy(_displayAs);
    }
}

When we run the app, it doesn’t look like it’s working. When we change the display strategy, the title doesn’t update. Did we forget to compile? No. We just forgot to tell XAML that the title depends upon DisplayAs.

public string DisplayAs
{
    get { return DisplayUsingStrategy(_displayAs); }
    set
    {
        _displayAs = (DisplayStrategy)DisplayAsOptions
            .ToList()
            .FindIndex(option => option == value);
        FirePropertyChanged("Title");
    }
}

Now the title updates when we change the display strategy.

Messy model

Take a step back and look at the model that we’ve created. The Person class has the following properties.

  • FirstName
  • LastName
  • Email
  • Phone
  • DisplayAs
  • DisplayAsOptions
  • Title

How many of these are actually the responsibility of the model? How many are just there to satisfy the view? As we’ve added features to the application, we’ve allowed the view’s concerns to creep into the data model. If we continue in this vein, the data model will get very messy.

In part 4, we’ll pull the view’s concerns out of the data model. That’s the first time in this series that we’ll really see the MVVM pattern.

Leave a Reply

You must be logged in to post a comment.