Silverlight MVVM, stop running SelectionChanged in response to ItemsSource reset

I have two ComboBoxes, A and B, each of which is associated with an Observable Collection. Each of them has a SelectionChanged trigger, which is designed to catch when the user changes the selection. The trigger passes the selection to the command.

Collections implement INotifyPropertyChanged in the event that the NotifyPropertyChanged event is fired in the Setter of each of them. It is necessary (in the MVVM approach) to notify the user interface (view) that has changed the contents of the ComboBox.

Two ComboBoxes are interdependent - changing the selection of B causes B to be re-populated with new elements.

Now the problem is that the B SelectionChanged trigger fires in response to its repopulation (as well as the user changing the selection). Because of the complexity of the code in Command, this is a huge waste of resources.

I could theoretically stop this without raising the NotifyPropertyChanged event when set B is installed (because looking at the call stack is what seems to trigger the SelectionChanged trigger to fire), however the MVVM approach depends on this, update the user interface.

Any suggestions?

+1
source share
1 answer

Why does ComboB need a SelectionChanged event? You can simply bind the selected item directly to a property in the virtual machine.

The way I did this earlier was to associate the selected ComboA element with the VM. In the installer for this property, I recount the available elements for ComboB and assign them to another property in the virtual machine, and ComboB ItemsSource is bound to this property. Of course, this property will notify (using INotifyPropertyChanged), but there was nothing else to do, my ComboB did not have a SelectionChanged event. Using this method, I also don’t need SelectionChanged on ComboA, which keeps the view code beautiful and sparse - everything is processed in a virtual machine, and regular data binding takes care of the rest.

Edit:

The following is an example of setting up required lists from property sets:

public class MyViewModel : INotifyPropertyChanged { //ItemsSource of ComboA is bound to this list public List<SomeObject> ComboAList { get { return _comboAList; } set { _comboAList = value; } } //ItemsSource of ComboB is bound to this list public List<SomeObject> ComboBList { get { return _comboBList; } set { _comboBList = value; OnPropertyChanged("ComboBList"); } } //ItemsSource of the dataGrid is bound to this list public List<SomeObject> DataGridList { get { return _datagridList; } set { _datagridList = value; OnPropertyChanged("DataGridList"); } } //SelectedItem of ComboA is bound to this property public SomeObject FirstSelectedItem { get { return _firstSelectedItem; } set { _firstSelectedItem = value; RefreshListForComboB(); } } //SelectedItem of ComboB is bound to this property public SomeObject SecondSelectedItem { get { return _secondSelectedItem; } set { _secondSelectedItem = value; RefreshListForDataGrid(); } } private void RefreshListForComboB() { //do whatever is necessary to filter or create a list for comboB ComboBList = doSomethingThatReturnsAListForComboB(); } private void RefreshListForDataGrid() { //do whatever is necessary to filter or create the list for the DataGrid DataGridList = doSomethingThatReturnsAListForDataGrid(); } protected void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; #endregion private List<SomeObject> _comboAList, _comboBList, _datagridList; private SomeObject _firstSelectedItem, _secondSelectedItem; } 

And here is a slightly different way of doing this using the PropertyChange event handler in the virtual machine, it just changes when the list is updated. This is perhaps the best way to do this than the first sample, as this means that property definitions have no side effects:

 public class MyViewModel : INotifyPropertyChanged { public MyViewModel() { this.PropertyChanged += new PropertyChangedEventHandler(MyViewModel_PropertyChanged); } private void MyViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { case "FirstSelectedItem": RefreshListForComboB(); break; case "SecondSelectedItem": RefreshListForDataGrid(); break; } } //ItemsSource of ComboA is bound to this list public List<SomeObject> ComboAList { get { return _comboAList; } set { _comboAList = value; } } //ItemsSource of ComboB is bound to this list public List<SomeObject> ComboBList { get { return _comboBList; } set { _comboBList = value; OnPropertyChanged("ComboBList"); } } //ItemsSource of the dataGrid is bound to this list public List<SomeObject> DataGridList { get { return _datagridList; } set { _datagridList = value; OnPropertyChanged("DataGridList"); } } //SelectedItem of ComboA is bound to this property public SomeObject FirstSelectedItem { get { return _firstSelectedItem; } set { _firstSelectedItem = value; OnPropertyChanged("FirstSelectedItem"); } } //SelectedItem of ComboB is bound to this property public SomeObject SecondSelectedItem { get { return _secondSelectedItem; } set { _secondSelectedItem = value; OnPropertyChanged("SecondSelectedItem"); } } private void RefreshListForComboB() { //do whatever is necessary to filter or create a list for comboB ComboBList = doSomethingThatReturnsAListForComboB(); } private void RefreshListForDataGrid() { //do whatever is necessary to filter or create the list for the DataGrid DataGridList = doSomethingThatReturnsAListForDataGrid(); } protected void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; #endregion private List<SomeObject> _comboAList, _comboBList, _datagridList; private SomeObject _firstSelectedItem, _secondSelectedItem; } 
+2
source

All Articles