42
votes

How to raise / handle the SelectionChanged event of WPF's ComboBox using the MVVM pattern?
Explain in detail please I am new to WPF.

What I want, is to do some operations when the ComboBox item selection changed. How can I achieve it, in an MVVM way?

5

5 Answers

61
votes

MVVM solution:

Bind the ItemsSource and SelectedItem properties of the ComboBox to properties in your ViewModel:

<ComboBox ItemsSource="{Binding MyItems}" SelectedItem="{Binding MySelectedItem}"/>

In MainViewModel.cs:

public ObservableCollection<string> MyItems { get; set; }

private string _mySelectedItem;
public string MySelectedItem
{
  get { return _mySelectedItem; }
  set
  {
    // Some logic here
    _mySelectedItem = value;
  }
}

Code-behind solution:

If you don't want to use MVVM, you can add use this:

 <ComboBox SelectionChanged="ComboBox_SelectionChanged" />

And add this in MainWindow.xaml.cs:

private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    // Some logic here
}
36
votes

I'm a big fan of this method.

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

<ComboBox Grid.Column="2"  DisplayMemberPath="Data.name" ItemsSource="{Binding Model.Regions}" SelectedItem="{Binding Model.SelectedRegion}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <i:InvokeCommandAction Command="{Binding RegionChangedCmd}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ComboBox>
6
votes

Your ViewModel needs to implement INotifyPropertyChanged.

public class MyViewModel : INotifyPropertyChanged
{
    private string _mySelectedItem;
    public string MySelectedItem
    {
        get
        {
            return _mySelectedItem;
        }
        set
        {
            if (_mySelectedItem != value)
            {
                _mySelectedItem = value;
                // Perform any pre-notification process here.
                if (null != PropertyChanged)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("MySelectedItem"));
                }
            }
        }
    } 
}

The previously posted XAML is correct:

<ComboBox ItemsSource="{Binding MyItems}" SelectedItem="{Binding MySelectedItem}"/> 
4
votes

Just an enhancement of this solution which exists above, In case you are using Prism Library
(if not, then stop reading now, there is nothing for you)

I really like this solution and I think it is better than any other solution, I just want to make a small enhancement to that solution provided by the Prism Library.

that solution is using

<i:InvokeCommandAction Command="{Binding RegionChangedCmd}" />

notice the i: before the InvokeCommandAction. It means that the InvokeCommandAction class exists in the xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" namespace. This is good and fine, but notice that the Prism library has exactly the same class with the same name InvokeCommandAction. It just exists in another namespace, in the xmlns:prism="http://prismlibrary.com/" namespace.

So actually you can replace the following XAML

<i:InvokeCommandAction Command="{Binding RegionChangedCmd}" />

with this XAML

<prism:InvokeCommandAction Command="{Binding RegionChangedCmd}" />

OK, we can do this, what is the benefit?
To notice the benefit, write the following command in the ViewModel

public ICommand RegionChangedCmd { get; }

public ViewModelConstructor() 
{
   RegionChangedCmd = new DelegateCommand<SelectionChangedEventArgs>(RegionChangedCmdExecuted);
}

public void RegionChangedCmdExecuted(SelectionChangedEventArgs e)
{
   // e parameter is null     if you use <i:InvokeCommandAction>
   // e parameter is NOT null if you use <prism:InvokeCommandAction>
}

e parameter is null if you use <i:InvokeCommandAction>
e parameter is NOT null if you use <prism:InvokeCommandAction>

2
votes

As first let's make things clear - you can not change event rather you can subscribe to.

Since you've not provided any information regarding where from you want to handle selection changes I will assume most common scenario - handling in the underlying ViewModel. According to MVVM ViewModel should not know anything about View so you can not subscribe directly from ViewModel to the event of a View's control. But you can bind a property of ViewModel to either SelectedItem or SelectedIndex so it would trigger whilst selection changes.

<ComboBox 
       SelectedIndex="{Binding SelectedIndexPropertyName}" 
       ... />

There are other solutions doing handling in code behind of a View by accessing a ViewModel via view.DataContext but I would suggest avoid such practice, this are work around cases.