none
MVVM pattern: How can viewmdoel detect ComboBox selection change and take action in viewmodel?

    Question

  • In MVVM pattern, we bind a ComboBox with a List<string> in viewmodel class.
    We see some examples in MVVM pattern, when combobox selection change, how the listview control reflects change in the same window.
    All code in xaml by binding to CurrentItem of ComboBox.
    What we need to do is base on comboBox selection change, we need to notice other window/page to reflect the change.
    So far, we have to link a event with comboBox in the xaml, then handle the event in the C# code.
    In the C# code event handler, call to the ViewModel function to do the real work.
    Is this the right way to do that?

    <ComboBox 
       x:Name="cB_LanguageSelector"
       ItemsSource="{Binding Path=AvailableLanguages}"
       SelectionChanged ="ComboBox_SelectionChanged"/>

    private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        string selectedCultureName = cB_LanguageSelector.SelectedItem.ToString();
        sv =
    this.DataContext as ViewModel.SystemOptionViewModel;
        if (sv != null)
            sv.CultureChanges(selectedCultureName);
    }

    Should there a direct way to notice the ViewModel comboBox selection change?

    Thanks!
    Thursday, April 02, 2009 5:49 PM

Answers

  • Add
    IsSynchronizedWithCurrentItem="True" in combobox xaml atrributes

    Write a property on your viewmodel like this

    private ICollectionView _avaibleLanguagesView;
    public ObservableCollection<YourObject> AvaibleLanguages{ get; private set; }
                       
    public YourViewModel()
    { 
       AvaibleLanguages = new ObservableCollection<YourObject>();
       _avaibleLanguagesView = CollectionViewSource.GetDefaultView(AvaibleLanguages);
       _avaibleLanguagesView.CurrentChanged += new EventHandler(OnCurrentChanged);
    }
    
    public ICollectionView AvaibleLanguagesView
    {
       get
       {
          return _avaibleLanguagesView;
       } 
    }
    
    void OnCurrentChanged(object sender, EventArgs e)
    {
        YourObject current = _avaibleLanguagesView.CurrentItem as YourItem;
    //have fun..
    }

    • Marked as answer by JJChen Friday, April 03, 2009 7:34 PM
    • Unmarked as answer by JJChen Friday, April 03, 2009 7:34 PM
    • Marked as answer by JJChen Friday, April 03, 2009 7:36 PM
    Friday, April 03, 2009 6:09 AM
  • The accepted answer is not best practice and not recommended.  The most common approach to this problem is to create a property in your viewmodel that will represent the SelectedItem and execute the method in the setter when the property value changes:

    public MyObject SelectedObject
    {
      get { return _selectedObject;}
      set
      {
         if (selectedObject == value)
           return;
    
         _selectedObject = value;
         OnPropertyChanged("SelectedObject");
    
         PerformMyMethod(_selectedObject);
      }
    }
    


    Then you would bind the SelectedItem of the comboBox.

    <ComboBox x:Name="cB_LanguageSelector" ItemsSource="{Binding Path=AvailableLanguages}" SelectedItem="{Binding SelectedObject}" />
    
    
    • Proposed as answer by The ArchDeacon Thursday, March 08, 2012 8:17 PM
    • Marked as answer by JJChen Thursday, March 08, 2012 10:04 PM
    Saturday, June 11, 2011 2:53 AM
  • Hi Fanou360,
    Thanks for your solution. It is vey helpful!
    Jane
    • Marked as answer by JJChen Friday, April 03, 2009 7:36 PM
    Friday, April 03, 2009 7:36 PM

All replies

  • Add
    IsSynchronizedWithCurrentItem="True" in combobox xaml atrributes

    Write a property on your viewmodel like this

    private ICollectionView _avaibleLanguagesView;
    public ObservableCollection<YourObject> AvaibleLanguages{ get; private set; }
                       
    public YourViewModel()
    { 
       AvaibleLanguages = new ObservableCollection<YourObject>();
       _avaibleLanguagesView = CollectionViewSource.GetDefaultView(AvaibleLanguages);
       _avaibleLanguagesView.CurrentChanged += new EventHandler(OnCurrentChanged);
    }
    
    public ICollectionView AvaibleLanguagesView
    {
       get
       {
          return _avaibleLanguagesView;
       } 
    }
    
    void OnCurrentChanged(object sender, EventArgs e)
    {
        YourObject current = _avaibleLanguagesView.CurrentItem as YourItem;
    //have fun..
    }

    • Marked as answer by JJChen Friday, April 03, 2009 7:34 PM
    • Unmarked as answer by JJChen Friday, April 03, 2009 7:34 PM
    • Marked as answer by JJChen Friday, April 03, 2009 7:36 PM
    Friday, April 03, 2009 6:09 AM
  • Hi Fanou360,
    Thanks for your solution. It is vey helpful!
    Jane
    • Marked as answer by JJChen Friday, April 03, 2009 7:36 PM
    Friday, April 03, 2009 7:36 PM
  • Thanks. This was very helpful.
    Monday, June 08, 2009 5:40 PM
  • Thank you so much.

    I was following Jason Dolinget and Derek Kowald's examples to figure out MVVM. I was able to bind the data, but until now, I was having trouble geting the selected data. this helps so much.

    Sunday, October 24, 2010 11:13 PM
  • How would you do this if you wanted to use  a button? How can i have something be reflected this way as well?
    Tuesday, May 03, 2011 8:41 PM
  • Hi AXWACKLee,

    If putting buttons on the listbox, it is harder to detect which button gets click.

    The simple way to implement this is using ListBox and modify the ListBoxItem to make the each Listboxitem like button.

    So when listbox item gets selected, viewmodel layer get notification and base on that to take action.

    Regards,


    JaneC
    Saturday, May 07, 2011 12:29 AM
  • I bind SelectedValue/SelectedItem of the combo to a property that fires PropertyChanged event and subscribe to it in my ViewModel. I do not know the origin of the change (user/programme), but in my case it does not matter.
    Friday, June 10, 2011 10:33 AM
  • The accepted answer is not best practice and not recommended.  The most common approach to this problem is to create a property in your viewmodel that will represent the SelectedItem and execute the method in the setter when the property value changes:

    public MyObject SelectedObject
    {
      get { return _selectedObject;}
      set
      {
         if (selectedObject == value)
           return;
    
         _selectedObject = value;
         OnPropertyChanged("SelectedObject");
    
         PerformMyMethod(_selectedObject);
      }
    }
    


    Then you would bind the SelectedItem of the comboBox.

    <ComboBox x:Name="cB_LanguageSelector" ItemsSource="{Binding Path=AvailableLanguages}" SelectedItem="{Binding SelectedObject}" />
    
    
    • Proposed as answer by The ArchDeacon Thursday, March 08, 2012 8:17 PM
    • Marked as answer by JJChen Thursday, March 08, 2012 10:04 PM
    Saturday, June 11, 2011 2:53 AM
  • Thanks for this solution.  I have managed to get this all working, MVVM can be a bit frustrating at times.

    However, surely each time the source changes, you will need to re-assign the event handler.  I have done this and it works for me.

    Thanks again.

    Monday, May 21, 2012 11:36 AM
  • Just pointing out that this is not MVVM at all. :)

    Your ViewModel is able to reach up a layer and mess with the view. The point of MVVM is to have a layered approach in which each layer only knows about the layers equal to or below it. When you end up with VMs messing with the View (CollectionViewSource.GetDefaultView) or Models messing with VMs etc... you are on a slippery slope to making a mess of the code base.

    Brian provides the correct answer below. I dont really think that this should be marked as an answer (just in my opinion). MVVM confuses enough people, I dont think answers like this help clarify the confusion.

    I am not trying to be mean, just providing some more weight to Brain's answer.

    Lee


    Lee Campbell http://LeeCampbell.blogspot.com

    Monday, May 21, 2012 4:23 PM
  • Hi Lee,

    Thanks for your comments.  I agree with you that Brian Lagunas points the best practice way that view and viewmodel layers should do to detect selection changing.

    However, I am not sure if using ICollectionView (CollectionViewSource.GetDefaultView) in viewmodel layer does agaist MVVM pattern.

    We actually use ICollectionView.Refresh() function to handle laugage switch on-the-fly in the viewmodel layer. All contents in the listBox will reflect the langauge switch in that way.

    Regards


    JaneC


    • Edited by JJChen Thursday, May 24, 2012 1:22 AM
    Thursday, May 24, 2012 1:20 AM
  • This looks like a hacky way to get around the MVVM's event/command pattern. Can you point to a reference that explains why this is best practice?
    Wednesday, June 18, 2014 3:13 PM