Using the WPF ObservableCollection with EF Entities

The ObservableCollection is a special WPF collection that provides proper notifications to the UI when items are added, removed, or the list is refreshed because it implements INotifyCollectionChanged. It’s common to use this collection (or inherit from it) to contain your business objects you want to bind to in WPF. 

Class Window1
Private CustomerData As ObservableCollection(Of Customer)

You can then set up a CollectionViewSource and use it’s View property to get a reference to the ListCollectionView in order to add and remove items instead of working with the source collection directly. This decouples your data source (and therefore any collection logic) from the form itself making it much easier to change sources later. I’ve showed how to use CollectionViewSources before but basically you just declare them in the Window.Resources section and bind to them in XAML:

<Window x:Class=”Window1″
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Title=”Window1″ Height=”282″ Width=”440″ Name=”Window1″>
<
Window.Resources>
<
CollectionViewSource x:Key=”CustomerSource” />
</
Window.Resources>
<
Grid DataContext=”{Binding Source={StaticResource CustomerSource}}”>

And then you can set the Source property in code to your collection and obtain the ListCollectionView.

Dim customerSource = CType(Me.Resources(“CustomerSource”), CollectionViewSource)
customerSource.Source = Me.CustomerData

Me.View = CType(customerSource.View, ListCollectionView)


Then you use the View to add and remove items from the collection and the UI will update properly:

Private Sub btnDelete_Click() Handles btnDelete.Click
If Me.View.CurrentPosition > -1 Then
‘removes the currently selected customer from the underlying collection
Me.View.RemoveAt(Me.View.CurrentPosition)
End If
End Sub

Private Sub btnAdd_Click() Handles btnAdd.Click
‘adds a new customer to the underlying collection
Dim customer = CType(Me.View.AddNew, Customer)
‘do something with customer if needed…
Me.View.CommitNew()
End Sub


Calling these methods on the ListCollectionView will execute the InsertItem and RemoveItem methods on the ObservableCollection.


Now if you are using an Entity Data Model (EDM) the designer in Visual Studio 2008 SP1 will generate entity classes for you that you can also bind to in your UI. Access to these entities is done through the ObjectContext and the designer also creates a class for you that inherits from this when you create the EDM. It is named something like xxxEntites. (For instance, in Visual Studio 2008 SP1 “Add New Item” and select ADO.NET Entity Data Model and name it Northwind.edmx. Generate from Database and select Northwind. Select all the tables and then the designer will generate an ObjectContext called NorthwindEntities and entity classes based on the tables in the database.)


Because the ObjectContext is what tracks changes on entities you can place entities inside an ObservableCollection but in order for the ObjectContext to be notified that adds and deletes need to be tracked you need to write a bit of code. The easiest thing to do is to create your own class that inherits from ObservableCollection and override the InsertItem and RemoveItem methods so that you can tell the ObjectContext to either add or delete the entity which will ultimately execute against the database. In the constructor pass a reference to the ObjectContext. You can also pass in any collection of entities, say from a LINQ query, and then add them to the ObservableCollection. For example:

Imports NorthwindDAL
Imports System.Collections.ObjectModel

Public Class CustomerCollection
Inherits ObservableCollection(Of Customer)

Private _context As NorthwindEntities
Public ReadOnly Property Context() As NorthwindEntities
Get
Return
_context
End Get
End Property

Sub New(ByVal customers As IEnumerable(Of Customer), ByVal context As NorthwindEntities)
MyBase.New(customers)
_context = context
End Sub

Protected Overrides Sub InsertItem(ByVal index As Integer, ByVal item As Customer)
Me.Context.AddToCustomers(item)
MyBase.InsertItem(index, item)
End Sub

Protected Overrides Sub RemoveItem(ByVal index As Integer)
Me.Context.DeleteObject(Me(index))
MyBase.RemoveItem(index)
End Sub

End Class


Then you can use the collection on your WPF form instead like so:

Imports NorthwindDAL

Class Window1
Private db As New NorthwindEntities
Private CustomerData As CustomerCollection
Private View As ListCollectionView

Private Sub Window1_Loaded() Handles MyBase.Loaded

Dim results = From c In db.Customers _
Where c.City.ToLower = “seattle” _
Order By c.LastName, c.FirstName _
Select c

Me.CustomerData = New CustomerCollection(results, db)

        Dim customerSource = CType(Me.Resources(“CustomerSource”), CollectionViewSource)
customerSource.Source = Me.CustomerData
Me.View = CType(customerSource.View, ListCollectionView)
End Sub
    Private Sub btnSave_Click() Handles btnSave.Click
Try
db.SaveChanges()
MsgBox(“Customer data was saved.”)
Catch ex As Exception
MsgBox(ex.ToString())
End Try
End Sub
    Private Sub btnDelete_Click() Handles btnDelete.Click
If Me.View.CurrentPosition > -1 Then
Me.View.RemoveAt(Me.View.CurrentPosition)
End If
End Sub

Private Sub btnAdd_Click() Handles btnAdd.Click
Dim customer = CType(Me.View.AddNew, Customer)
‘do something with customer if needed…
Me.View.CommitNew()
End Sub

End Class

Now any updates, adds or deletes you make in the UI will be propagated to the database through the Entity Framework. 


Enjoy!