MajkeloDev
MajkeloDev

Reputation: 1661

ComboBox SelectionChanged in MVVM

I have an app in WPF without MVVM, I decided to refactor this to MVVM. I encountered a problem with a ComboBox SelectionChanged event. Basically to make it simpler lets assume i Have one ComboBox and 2 ListView's. Every ComboBoxItem is a collection. First ListView's ItemsSource is bound to the collection from the ComboBox.SelectedValue but only to the part whose one property(of decimal) is greater than zero. Second ListView's ItemsSource is bound to the same colection but to the second part(some prop greater than zero). Below some code to understand

private void myCombo_selectionChanged(object sender, SelectionChangedEventArgs e)
{
    ComboBox myCmb = sender as ComboBox;
    List<myType> myList = myCmb.SelectedValue as List<myType>;

    itemsForListView1 = myList.Where((x) => x.myProp > 0); 
    itemsForListView2 = myList.Where((x) => x.myProp < 0);
    // Above 2 collections are of Type List<myType> and their scope will be whole ViewModel, 
    //so i assume i just need to change them and RaisePropChanged but how to change them without breaking mvvm ?

    MyListView1.ItemsSource = itemsForListView1;
    MyListView2.ItemsSource = itemsForListView2;
}

How can I achieve something similiar in MVVM ?

Upvotes: 1

Views: 1670

Answers (2)

Mike Nowak
Mike Nowak

Reputation: 333

If I understand correctly the thing you want is to handle the SelectionChanged in ViewModel and make some changes to itemsForListView1 and itemsForListView2 instead to what you are doing in View at the moment?

1) In ViewModel you need to: create an ICommand public property, which can be instantiated for example as DelegateCommand from Microsoft.Practices.Composite.Presentation.Commands:

...
public ViewModelConstructor(...)
{
    ...
    SelectionChangedCommand = new DelegateCommand<List<myType>>(SelectionChangedExecute);
    ...
}
...
public List<myType> ItemsForListView1 {get; private set} // Implement INotifyPropertyChanged here
public List<myType> ItemsForListView1 {get; private set} // Implement INotifyPropertyChanged here
...
public ICommand SelectionChangedCommand { get; private set; }

private void SelectionChangedExecute(List<myType> parameter)
{
    ItemsForListView1 = parameter.Where((x) => x.myProp > 0).ToList(); 
    ItemsForListView2 = parameter.Where((x) => x.myProp < 0).ToList();
}
...

2) In View you want to bind Loaded and SelectionChanged to the newly created command in viewmodel, and you want your ListView bind to the collections in ViewModel

<ComboBox x:Name="MyComboBox" SelectedIndex="0" ItemsSource="{Binding YourCollectionWithDifferentLists}">
<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <i:InvokeCommandAction Command="{Binding SelectionChangedCommand}" CommandParameter="{Binding ElementName=Combo, Path=SelectedItem}"></i:InvokeCommandAction>
    </i:EventTrigger>
    <i:EventTrigger EventName="SelectionChanged">
        <i:InvokeCommandAction Command="{Binding SelectionChangedCommand}" CommandParameter="{Binding ElementName=Combo, Path=SelectedItem}"></i:InvokeCommandAction>
    </i:EventTrigger>
</i:Interaction.Triggers>

You might want to use ObservableCollection instead of List if your collection is changing and you want to show the changes on UI. You need to implement INotifyPropertyChanged either way if you are going to create a new collection after a selection changed event.

Upvotes: 2

T N
T N

Reputation: 406

I suggest a solution as in pseudo-code below. Bind SelectedItem with SelectedValue of your myCombo and ItemsListViewX with your ListView

private List<T> selectedItem;
public List<T> SelectedItem
{
    get { return selectedItem; }
    set
    {
        if (value != selectedItem)
        {
            selectedItem = value;

            ItemsListView1 = new ObservableCollection<T>(selectedItem.Where(x=>x.Prop>0));
            ItemsListView2 = new ObservableCollection<T>(selectedItem.Where(x=>x.Prop<0));
            NotifyOfPropertyChange(() => SelectedItem);         
        }
    }
}

private ObservableCollection<T> itemsListView1;
public ObservableCollection<T> ItemsListView1
{
    get { return itemsListView1; }
    set
    {
        if (value != itemsListView1)
        {
            itemsListView1 = value;
            NotifyOfPropertyChange(() => ItemsListView1);
        }
    }
}

private ObservableCollection<T> itemsListView2;
public ObservableCollection<T> ItemsListView2
{
    get { return itemsListView2; }
    set
    {
        if (value != itemsListView2)
        {
            itemsListView2 = value;
            NotifyOfPropertyChange(() => ItemsListView2);
        }
    }
}

Maybe you're interested also in MVVM Framework. It will enhance your databinding.

Upvotes: 2

Related Questions