WPF - MVVM: ComboBox value after SelectionChanged
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}"/>
Alistair Tweed
Updated on July 09, 2022Comments
-
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 onSelectionChanged
. I have managed to figure it out using eitherCallMethodAction
orInvokeCommandAction
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 SolutionIt 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 aTextBox
to theSelectedIndex
of theComboBox
, which gets updated usingINotifyPropertyChanged
.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 over 10 yearsThis 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!