Silverlight MVVM, остановка срабатывания SelectionChanged в ответ на сброс ItemsSource - PullRequest
1 голос
/ 07 июня 2011

У меня есть два ComboBox, A & B, каждый из которых связан с Наблюдаемой коллекцией. К каждому прикреплен триггер SelectionChanged, который предназначен для перехвата, когда пользователь меняет выбор. Триггер передает выбор команде.

Коллекции реализуют INotifyPropertyChanged тем, что в Setter каждого из них запускается событие NotifyPropertyChanged. Это необходимо (в подходе MVVM), чтобы уведомить пользовательский интерфейс (представление) об изменении содержимого ComboBox.

Два комбинированных списка являются взаимозависимыми - изменение выбора в A приводит к повторному заполнению B новыми элементами.

Теперь проблема в том, что триггер B SelectionChanged срабатывает в ответ на повторное заполнение его коллекции (а также на изменение пользователем выбора). Из-за сложности кода в Команде это огромная трата ресурсов.

Теоретически я мог бы остановить это, не вызывая событие NotifyPropertyChanged, когда установлена ​​коллекция B (потому что, глядя на стек вызовов, это то, что, похоже, вызывает срабатывание триггера SelectionChanged), однако подход MVVM зависит от этого, чтобы обновляйте пользовательский интерфейс.

Есть предложения?

1 Ответ

2 голосов
/ 07 июня 2011

Почему ComboB нуждается в событии SelectionChanged?Вы можете просто привязать выбранный элемент непосредственно к свойству на виртуальной машине.

Способ, которым я занимался ранее, заключался в том, чтобы привязать выбранный элемент ComboA к виртуальной машине.В установщике этого свойства я пересчитываю доступные элементы для ComboB и назначаю их другому свойству на виртуальной машине, а ItemsSource ComboB привязывается к этому свойству.Конечно, это свойство будет уведомлять (используя INotifyPropertyChanged), но больше ничего не нужно было делать, у моего ComboB не было события SelectionChanged.При использовании этого метода мне также не понадобился SelectionChanged на ComboA, который сохраняет красивый и разреженный код представления - все обрабатывается на виртуальной машине, а регулярное связывание данных заботится обо всем остальном.

Редактировать:

Вот пример настройки необходимых списков из установщиков свойств:

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;
}

А вот немного другой способ сделать это, используяобработчик события PropertyChange на виртуальной машине, он просто меняет место обновления списка.Возможно, это лучший способ сделать это, чем в первом примере, поскольку это означает, что установщики свойств не имеют побочных эффектов:

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;
}
...