1

2つのComboBox、AとBがあり、それぞれがObservableコレクションにバインドされています。それぞれにSelectionChangedトリガーが付加されており、ユーザーが選択を変更したときにキャッチすることを目的としています。トリガーは選択をコマンドに渡します。

コレクションはINotifyPropertyChangedを実装し、それぞれのセッターでNotifyPropertyChangedイベントが発生します。これは、ComboBoxのコンテンツが変更されたことをUI(ビュー)に通知するために(MVVMアプローチで)必要です。

2つのComboBoxは相互に依存しています。Aで選択を変更すると、Bに新しいアイテムが再入力されます。

ここでの問題は、BのSelectionChangedトリガーが、コレクションの再入力(およびユーザーが選択を変更したこと)に応答して起動することです。コマンドのコードは複雑であるため、これはリソースの膨大な浪費です。

理論的には、Bのコレクションが設定されているときにNotifyPropertyChangedイベントを発生させないことでこれを止めることができます(コールスタックを見ると、これがSelectionChangedトリガーを起動させるように見えるためです)が、MVVMアプローチはUIを維持するためにこれに依存していますリフレッシュしました。

助言がありますか?

4

1 に答える 1

2

ComboBにSelectionChangedイベントが必要なのはなぜですか?選択したアイテムをVMのプロパティに直接バインドできます。

以前にこれに取り組んだ方法は、ComboAの選択したアイテムをVMにバインドすることでした。そのプロパティのセッターで、ComboBで使用可能なアイテムを再計算し、それらをVM上の別のプロパティに割り当てると、ComboBのItemsSourceがこのプロパティにバインドされます。もちろん、そのプロパティは(INotifyPropertyChangedを使用して)通知しますが、他に何もする必要はありません。私のComboBにはSelectionChangedイベントがありませんでした。このメソッドを使用することで、ComboAでSelectionChangedも必要ありませんでした。これにより、ビューのコードが適切でスパースな状態に保たれます。すべてがVMで処理され、残りは通常のデータバインディングが処理し​​ます。

編集:

プロパティセッター内から必要なリストを調整する例を次に示します。

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

また、VMで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;
}
于 2011-06-07T12:37:07.990 に答える