WPF - MVVM: ComboBox value after SelectionChanged

41,376

Solution 1

Why not do it the simpler way

<ComboBox MaxHeight="25" 
          ItemsSource="{Binding Source}" 
          SelectedItem="{Binding TheSelectedItem, Mode=TwoWay}" />

In your ViewModel declare the combo box items and use a property "Source" to return it to the view

List<string> _source = new List<string>{"Item 1", "Item 2", "Item 3"};
public List<string> Source 
{ 
    get { return _source; } 
}

Then define one property which holds the selected item

string _theSelectedItem = null;
public string TheSelectedItem 
{ 
    get { return _theSelectedItem; } 
    set { _theSelectedItem = value; } // NotifyPropertyChanged
}

Also don't forget to implement the INotifyPropertyChanged interface while setting the _source

Solution 2

If you just want to get notified when your current item changes, why not use tools that are already part of WPF instead of all these dependencies.

First get the underlying view to your collection and use the CurrentChanged event on it.

ComboBoxList = new ObservableCollection<string>();
var view = CollectionViewSource.GetDefaultView(ComboBoxList);
view.MoveCurrentTo(ComboBoxList[0]);
view.CurrentChanged += new EventHandler(ComboBoxCurrentChanged);

In your xaml you just bind your collection and set IsSynchronizedWithCurrentItem to true.

<ComboBox IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding ComboBoxList}"/>
Share:
41,376
Alistair Tweed
Author by

Alistair Tweed

Updated on July 09, 2022

Comments

  • Alistair Tweed
    Alistair Tweed almost 2 years

    I am new to C# and MVVM, and I've spent all day trying to get the value of a ComboBox to my ViewModel on SelectionChanged. I have managed to figure it out using either CallMethodAction or InvokeCommandAction with the resources:

    • System.Windows.Interactivity.dll
    • Microsoft.Expression.Interactions.dll

    My problem is that both these methods return the value of the ComboBox before it has changed. Could anyone explain how to get the value after the change?

    I have spent hours searching through SO and Google for a solution, so I wonder if others are too. Any advice will be appreciated!

    My code is below:

    MainWindow.xaml

    <Window x:Class="SelectionChange.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
            xmlns:si="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
            xmlns:vm="clr-namespace:SelectionChange"
            Title="MainWindow" Width="300" Height="300">
        <Window.DataContext>
            <vm:ViewModel />
        </Window.DataContext>
        <Grid>
            <ComboBox Name="SelectBox" VerticalAlignment="Top" SelectedIndex="0">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <si:CallMethodAction MethodName="SelectionChanged" TargetObject="{Binding}" />
                        <!--<i:InvokeCommandAction Command="{Binding SelectionChangedCommand}" CommandParameter="{Binding ElementName=SelectBox, Path=Text}" />-->
                    </i:EventTrigger>
                </i:Interaction.Triggers>
                <ComboBoxItem Content="Item 1" />
                <ComboBoxItem Content="Item 2" />
                <ComboBoxItem Content="Item 3" />
            </ComboBox>
        </Grid>
    </Window>
    

    ViewModel.cs

    namespace SelectionChange
    {
        using System;
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Input;
    
        public class ViewModel
        {
            public ViewModel()
            {
                SelectionChangedCommand = new SelectionChangedCommand();
            }
    
            public ICommand SelectionChangedCommand
            {
                get;
                set;
            }
    
            public void SelectionChanged(object sender, EventArgs e)
            {
                ComboBox SelectBox = (ComboBox)sender;
                MessageBox.Show("Called SelectionChanged: " + SelectBox.Text);
            }
        }
    }
    

    SelectionChangedCommand.cs

    namespace SelectionChange
    {
        using System;
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Input;
    
        public class SelectionChangedCommand : ICommand
        {
            public SelectionChangedCommand()
            {
            }
    
            public event EventHandler CanExecuteChanged;
    
            public bool CanExecute(object parameter)
            {
                return true;
            }
    
            public void Execute(object parameter)
            {
                MessageBox.Show("Executed SelectionChangedCommand: " + parameter);
            }
        }
    }
    




    Edit: My Solution

    It turns out I didn't understand Binding well enough and instead was trying to implement something simple in a rather complicated way! Instead of using dependencies, I have now achieved what I needed using regular bindings. As an example, I've bound a TextBox to the SelectedIndex of the ComboBox, which gets updated using INotifyPropertyChanged.

    MainWindow.xaml Screenshot

    MainWindow.xaml

    <Window x:Class="SelectionChange.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:vm="clr-namespace:SelectionChange"
            Title="MainWindow" Width="300" Height="300">
        <Window.DataContext>
            <vm:ViewModel />
        </Window.DataContext>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
    
            <ComboBox SelectedItem="{Binding SelectedItem}" SelectedIndex="0" Grid.Column="0" VerticalAlignment="Top">
                <ComboBoxItem Content="Item 1" />
                <ComboBoxItem Content="Item 2" />
                <ComboBoxItem Content="Item 3" />
            </ComboBox>
    
            <!-- TextBox to display the ComboBox's SelectedIndex -->
            <TextBox Text="{Binding SelectedIndex}" Grid.Column="1" VerticalAlignment="Top" />
        </Grid>
    </Window>
    

    ViewModel.cs

    namespace SelectionChange
    {
        using System;
        using System.ComponentModel;
        using System.Windows.Controls;
    
        public class ViewModel : INotifyPropertyChanged
        {
            public ViewModel()
            {   
            }
    
            // Property to store / retrieve ComboBox's SelectedIndex
            private int _SelectedIndex;
            public int SelectedIndex { get; set; }
    
            // Property to bind to ComboBox's SelectedItem
            private ComboBoxItem _SelectedItem;
            public ComboBoxItem SelectedItem
            {
                get { return _SelectedItem; }
                set
                {
                    _SelectedItem = value;
    
                    // SelectedItem's Content
                    string Content = (string)value.Content;
    
                    // SelectedItem's parent (i.e. the ComboBox)
                    ComboBox SelectBox = (ComboBox)value.Parent;
    
                    // ComboBox's SelectedIndex
                    int Index = SelectBox.SelectedIndex;
    
                    // Store the SelectedIndex in the property
                    SelectedIndex = Index;
    
                    // Raise PropertyChanged with the name of the stored property
                    RaisePropertyChanged("SelectedIndex");
                }
            }
    
            // INotifyPropertyChanged
            public event PropertyChangedEventHandler PropertyChanged;
            private void RaisePropertyChanged(string PropertyName)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
            }
        }
    }
    
  • Alistair Tweed
    Alistair Tweed over 10 years
    This is closest to what I ended up with, and I guess, depending on what you wanted to do with the ComboBox, could be the solution (for someone else). @aks81, Thanks for your answer!