views:

1992

answers:

2

In the code below, when user selects Customer in the combobox, the customer's name is displayed in a textbox. I fill the Combox box with an ObservableCollection property on my ViewModel but how do I handle the SelectedItem event in my ViewModel?

It's easy to implement this with code-behind as shown below, but how do I do this with the MVVM pattern?

I currently have DelegateCommand and AttachedBehaviors in my basic MVVM template that I can use, but I can't figure out how to get them to fire when "combobox selects a new item".

View:

<Window.Resources>
    <DataTemplate x:Key="CustomerTemplate">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="{Binding LastName}"/>
        </StackPanel>
    </DataTemplate>
</Window.Resources>

<DockPanel LastChildFill="False" Margin="10">
    <ComboBox 
        x:Name="CustomerList"
        ItemTemplate="{StaticResource CustomerTemplate}"
        HorizontalAlignment="Left"
        DockPanel.Dock="Top" 
        Width="200"
        SelectionChanged="CustomerSelected"
        ItemsSource="{Binding Customers}"/>

    <TextBlock x:Name="CurrentlySelectedCustomer"/>
</DockPanel>

Code Behind:

private void CustomerSelected(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
    Customer customer = (Customer)CustomerList.SelectedItem;
    CurrentlySelectedCustomer.Text = String.Format("{0} {1}", customer.FirstName, customer.LastName);
}
+1  A: 

You should be able to bind a property in you ViewModel to the SelectedItem property of the combobox. If you set this up as two way binding you will be notified when the SelectedItem is changed because it will trigger the set method on the property.

ViewModel:

public ObservableCollection Customers
{
   get { return _customers; }
   set
   {
       if (_customers != value)
       {
           _customers = value;
           OnPropertyChanged("Customers");
       }
   }
}

public Customer SelectedCustomer
{
   get { return _selectedCustomer; }
   set
   {
       if (_selectedCustomer != value)
       {
           _selectedCustomer= value;
           LastName= value.LastName;
           OnPropertyChanged("SelectedCustomer");
       }
   }
}

public Customer LastName
{
   get { return _lastName; }
   set
   {
       if (_lastName!= value)
       {
           _lastName= value;
           OnPropertyChanged("LastName");
       }
   }
}

Xaml:

<DockPanel LastChildFill="False" Margin="10">
    <ComboBox 
        x:Name="CustomerList"
        ItemTemplate="{StaticResource CustomerTemplate}"
        HorizontalAlignment="Left"
        DockPanel.Dock="Top" 
        Width="200"
        SelectedItem="{Binding SelectedCustomer, Mode=TwoWay}"
        ItemsSource="{Binding Customers}"/>

    <TextBlock x:Name="CurrentlySelectedCustomer"
               Text="{Binding LastName}"/>
</DockPanel>
Martin Harris
exactly what I was looking for, didn't know it was so simple, thanks
Edward Tanguay
Just remember that this puts a dependency between your V/VM... If you test this code without the view, the CurrentlySelectedCustomer will never update! If you use the CollectionView... It will work even if your view do not bind back to the VM!
rudigrobler
In general I agree there are advantages to a CollectionView (although there are some provisos when you want to bind the same list to multiple selectors), but I don't think your comment is correct. There is no more dependency in my version than yours, for example if I wanted to write a test to check that the LastName property updates correctly I don't need a view, I can just set the SelectedCustomer property through the test code.
Martin Harris
Agreed... both will definetly work! The only other problem I can see is that if you want to use a deffirent view... you have to remeber to bind back to the selected item! My approach has a simillar problem because in my view you have to remeber to set the IsSyncronizedWithCurrent to true!I definetely like your sulotion and in some cases it will definetely work I just don't like to be dependent on the view... Using the CV is just a little more de-coupled!
rudigrobler
+4  A: 

Have a look at this application on www.codeproject.com. Here I use the CollectionView to detect the currently selected item

Update

Using CollectionView to detect current selected item

ListCollectionView view = (ListCollectionView)CollectionViewSource.GetDefaultView(Customers); 
view.CurrentChanged += delegate 
{ 
    SelectedCustomer= (Customer)view.CurrentItem; 
};

Just remember to also set IsSynchronizedWithCurrentItem="True"

rudigrobler