Rachael
Rachael

Reputation: 1995

Understanding how to use ICollectionView in MVVM with Entity Framework data

I currently have a simple applications consisting of two synched ListBoxes bound to ObservableCollections which wrap two different Entity Framework classes.

When a Part item in listBox1 is selected, it passes the SelectedItem 's navigation key information to listBox2, which displays the respective subset of Vendors entities.

Currently, my ViewModel (MainViewModel.cs) looks like:

public MainViewModel()
{
    _context = new DBEntities();
    _partsCollection = new ObservableCollection<Part>(_context.Parts);
    _vendorsCollection = new ObservableCollection<Vendor>(_context.Vendors);
}

public ObservableCollection<Part> PartsCollection
{
     get { return _partsCollection; }
     set
     {
          OnPropertyChanged("PartsCollection");
          _partsCollection = value;
     }
}


public Observable<Part> SelectedPart
{
     get { return _selectedPart; }
     set
     {
          OnPropertyChanged("SelectedPart");
          _selectedPart = value;
     }
}

public ObservableCollection<Vendor> VendorsCollection
{
     get { return _vendorsCollection; }
     set
     {
          OnPropertyChanged("VendorsCollection");
          _vendorsCollection = value;
     }
}

and my view (MainView) looks like:

    <UserControl.Resources>
    <local:MainViewModel x:Key="MainViewModelDataSource" />
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource MainViewModelDataSource}}">
           <ListBox ItemsSource="{Binding PartsCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"  Margin="43,87,377,57" Name="listBox1"
             SelectedItem="{Binding SelectedPart, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
             IsSynchronizedWithCurrentItem="True" >
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock FontSize="13" Foreground="Black" Padding="3" Text="{Binding shapeName}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    <ListBox IsSynchronizedWithCurrentItem="True" 
             ItemsSource="{Binding ElementName=listbox2,  Path=SelectedItem.Vendors, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" Margin="345,87,75,57" 
             >
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock FontSize="13" Foreground="Black" Padding="3" Text="{Binding mateStyle}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox> 

This works great. However, I would like to bind listBox2 to properties of my viewmodel to update the VendorsCollection. I would like to use the "SelectedPart" property on my viewmodel, which isn't even currently being used right now.

The whole point of EF is to utilize all of the capabilities of it as an ORM, not build an additional ORM again in my viewmodel just to send change notifications. From what I've seen, setting up an ICollectionView is a rather round about method, but I was unable to figure out the bindings.

I'm unsure what the next step is so that I can raise PropertyChanged notifications and update my Vendors ObservableCollection, binding the listBox2 xaml to collection properties of my viewmodel.

Upvotes: 0

Views: 2642

Answers (2)

Mauro Sampietro
Mauro Sampietro

Reputation: 2814

To get PropertyChanged out of the box just pass your ObservableCollection in a BindingList.

EntityFramework in its System.Data.Entity namespace has a ToBindingList extension that helps you in that.

Upvotes: 0

Hannish
Hannish

Reputation: 1532

I'm using MVVM and Entity Framework too, but with code first approach. I was tired of all the MVVM "purity", in which the ViewModel should raise the change notification events, so I implemented INPC on all my model classes and I've never been happier before! It's just crazy to duplicate all your model properties in your ViewModels just to notify changes.

I see you are using DBEntities, so I'm not sure how you would implement change notification in your model objects, but I strongly recommend that you do so. I will make your life much easier.

By the way, be sure to raise the OnPropertyChanged event AFTER you set the backing field to the new value, like this:

 set
 {
      _selectedPart = value; //first you change the private backing field
      OnPropertyChanged("SelectedPart"); //then you notify that it has changed, so everything gets the new value
 }

Regarding the binding, you ARE using the SelectedPart property of your ViewModel, here:

SelectedItem="{Binding SelectedPart, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsSynchronizedWithCurrentItem="True"

You are binding the selected item in the listbox1 to the SelectedPart property, and by setting the IsSynchronizedWithCurrentItem to true, every time you select something in the UI the property in the ViewModel gets updated (and the other way too, if you set the SelectedPart in code the UI will receive the update).

So, to bind to the SelectedPart's vendors in the second listbox just do this:

ItemsSource="{Binding SelectedPart.Vendors, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"

In this case you don't need to create the VendorsCollection, because you can access the same elements via SelectedPart.Vendors (or you can set the VendorsCollection just to be a wrapper with a nice name to return SelectedPart.Vendor, just make sure to check SelectedPart for null).

Regarding the ICollectionView, know that it is only a wrapper around the ObservableCollection (or similar) in order to bind to FrameworkElements (like the ListBox) that inherit from Selector (that is, that they have the SelectedItem property).

WPF creates an ICollectionView in the background when you bind to the ObservableCollection, so you get the SelectedItem. You can create your own ICollectionView, feed it with your ObservableCollection, and bind to the ICollectionView in XAML just the same way you do with the ObservableCollection. If you decide to do so, I recommend that you use a ListCollectionView, with which you can filter and create groups (for use with treeviews).

One final word: remember that the ObservableCollection (and all the ICollectionViews) only raise CollectionChanged events (that is, when you add/remove items to/from the collection). They will not raise anything if you change a property of an element that belongs to the collection, even if it implements INotifyPropertyChanged. If you want to catch that you'll have to create a custom collection, one (of many) example here.

Hope this is useful, regards!

Upvotes: 1

Related Questions