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?
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
}
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>
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}"/>
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.
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>