How to handle the SelectionChanged event of ComboBox with MVVM in wpf?
Asked Answered
I

6

50

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?

Intubate answered 29/12, 2011 at 9:53 Comment(0)
C
71

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
}
Clothespress answered 29/12, 2011 at 10:14 Comment(10)
i tried this but its not triggering the selection changed event.Intubate
How you checked this? You should not subscribe to a control event in this caseCarte
It is not supposed to trigger the event, as the logic happens when MySelectedItem changes value instead. I've updated my answer with an event handler, if you want to use that instead.Clothespress
Its worked well.. And How to change the content of the label at run time in mvvm. I have set the value to the property which is assigned to the label control?Intubate
Bind Label's Text attribute to a string property on the ViewModel. Then just change that property in the code. The binding will update the view.Clothespress
it also need to use observablecollection for label?Intubate
For label just use string propery and implement INotifyPropertyChanged in your ViewModelCarte
I am not sure why SelectedItem is not trigger the property I binded to it. I am not subscribed to the SelectionChanged event. What I only done is "SelectedItem={Binding Browser.SelectedBrowser, Mode=TwoWay}" but when I subscribe to the SelectionChanged event and removed the SelectedItem binding, it's working. Any ideas?Wop
What is mean by path in SelectedItem - SelectedItem="{Binding Path=MySelectedItem}"Latisha
This is very bad, dont use it, any code run in property setter will be run on main thread that mean it will block the app until it is done and if it is long it will be blocking the UI, just put a sleep in // Some logic here and see what happens, use only events, they wont block changing combo selection, the are fire and forget.Custer
S
43

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>
Schaffer answered 14/9, 2017 at 20:58 Comment(6)
This was the easiest method for me using MVVM. Thanks!Jural
Easier to handle in SelectedRegion setter and comparing old value. Why bother creating EventTrigger?Juncaceous
@Juncaceous There are numerous benefits to encapsulating the Selection Changed logic in an ICommand implementation. One in particular is being able to use async/await in your ICommand.Execute method (although it would be nice if this method would return a Task instead).Dado
@Dado yeah, depends on the situation. I don't like WPF in overall because it's over-engineered and it's pretty much dead - all new apps from giant companies use electron or something else, maybe Avalonia is better. I would do everything in some sort of HTML now, or C++ and Qt or Sciter ;)Juncaceous
Right now I have like 5 or more electron apps on my PC... very resource-hungry but UI is very nice.Juncaceous
Agreed, this is the preferred way to handle UI events that don't natively support Command (such as button click). There's a good MS article if anyone is still interested.. social.technet.microsoft.com/wiki/contents/articles/…Delp
A
6

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}"/> 
Arioso answered 29/12, 2011 at 18:12 Comment(2)
Although this is a good practice, it doesn't help solving the problem. @Intubate wanted her code in the view model to be notified when the user change the selection from UI in order to do some operations accordingly. Notify property change is used to do the opposite of that.Retroflexion
@Retroflexion IMO it helps. If value != currentvalue then selection changed. Same thing as using i:Interaction.Triggers.Juncaceous
B
6

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>

Biddy answered 16/12, 2018 at 16:8 Comment(2)
Does this support object sender parameter?Anking
I think so but I am not sureNonferrous
C
2

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.

Carte answered 29/12, 2011 at 9:56 Comment(0)
B
0

There are some breaking changes for the newer versions of WPF
Starting from .NET framework 4.6.2 and for .NET Core, the above solutions does not work, they works for older versions of WPF.
here is the new way

  1. Install Microsoft.Xaml.Behaviors.Wpf package

  2. Use the namespace xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

  3. Then the comobox should be

<ComboBox ItemsSource="{Binding Items}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <i:InvokeCommandAction Command="{Binding SelectionChangedCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ComboBox>

notice: there are no changes to the code at all, the only needed thing is the Nuget package and updating the namespace

Biddy answered 28/11, 2023 at 14:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.