Silverlight MVVM, остановить срабатывание SelectionChanged в ответ на сброс ItemsSource

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

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

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

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

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

Какие-либо предложения?


person Laurence    schedule 07.06.2011    source источник


Ответы (1)


Зачем 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;
}
person slugster    schedule 07.06.2011
comment
К сожалению, мне нужно событие SelectionChanged для A и B. A и B действуют как фильтры для списка (показанного в DataGrid под ComboBoxes), и я хочу, чтобы список повторно заполнялся, как только A или B изменяются. - person Laurence; 07.06.2011
comment
@Laurence, еще раз вам не нужно событие SelectedItemChanged в представлении, проверьте мое редактирование для двух примеров, которые показывают вам, как полностью сохранить его в виртуальной машине. - person slugster; 08.06.2011
comment
Извините, не прочитал ваш ответ должным образом. Я понял это полностью сейчас, и это работает отлично. Спасибо. - person Laurence; 10.06.2011