Using the WPF ObservableCollection with EF Entities

Published 08 May 09 03:29 PM

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!

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Using the WPF ObservableCollection with EF Entities | Microsoft Share Point said on May 8, 2009 7:24 PM:

PingBack from http://microsoft-sharepoint.simplynetdev.com/using-the-wpf-observablecollection-with-ef-entities/

# Rico Alexander said on May 9, 2009 1:07 PM:

Don't understand why you just don't add entities directly to your observable collectioin instead of adding them to the collection view.  Wouldn't the collectionview automatically reflect those changes instead of having to create that observable collection inherited class.

# TheLudditeDeveloper said on May 9, 2009 2:09 PM:

Very timely article.

Thanks

# presiden lovestory said on May 10, 2009 8:59 PM:

wow... thankz for your informations.... nice article...

# Keith said on May 11, 2009 12:26 AM:

You could further simplify the code for the inherited ObservableCollection classes by getting rid of the IsLoading field and checks on insert by loading the collection via the base class:

Sub New(ByVal customers As IEnumerable(Of Customer), ByVal context As NorthwindEntities)

      MyBase.New(customers)

       _context = context

   End Sub

# Beth Massi said on May 11, 2009 10:40 AM:

Hi Rico,

Views allow the same data collection to be displayed in the UI in different ways, depending on sorting, filtering, or grouping criteria. Calling AddNew on the view gives you transacted adding (with CommitNew/CancelNew) and maintains the filter, sort,etc. It also separates your model (source collection) from your presenter (UI).

HTH,

-B

# Beth Massi said on May 12, 2009 11:50 AM:

Hi Keith,

I totally forgot about the base constructor, thanks! I updated the code. You may want to expose IsLoading as a readonly property for some other reason but we don't need it here.

Cheers,

-B

# Visual Studio Data said on May 15, 2009 10:06 PM:

I’ve been writing a lot about building WPF business applications with Entity Framework using Visual Studio

# Robert Reijntjes said on May 17, 2009 4:36 AM:

I created a master/detail viewmodel based on your articles and your video. Below is the link to an article about it on my blog:

http://reyntjes.blogspot.com/2009/04/master-detail-viewmodel_24.html

Maybe you like it.

Anyway, thanks for the articles.

Robert

# csaket said on June 20, 2009 3:40 AM:

A question.

This is great when the action is initiated by the user and thus through the UI.

What about the other way? If there is an update in the database, how to propogate that to the UI?

Example there would be a timer where a lookup is made to the database and data pulled up.

Where do we go about changing the collection so that the new/changed data reflects on the ui.

What I am doing now is not elegant and would like to know a best practices way of doing this and if you know of any posts that go through this.

(right now I am pulling up a list of simple objects that contain the data over wcf and then generating an observable collection and setting up the ui through a collectionviewsource. On the update thread another collection comes through and now I have to reconcile the two. Just looking to know what would be the best approach.)

Thanks

# gill bate said on October 15, 2009 6:10 PM:

Thanks!

Here is the VB version of it.

Imports System.ComponentModel

Imports System.Collections.ObjectModel

Public Class SortableObservableCollection(Of T)

   Inherits ObservableCollection(Of T)

   Public Overloads Sub Sort(Of TKey)(ByVal KeySelector As Func(Of T, TKey))

       Sort(KeySelector, ListSortDirection.Ascending)

   End Sub

   Public Overloads Sub Sort(Of TKey)(ByVal KeySelector As Func(Of T, TKey), ByVal dir As ListSortDirection)

       If dir = ListSortDirection.Ascending Then

           ApplySort(Items.OrderBy(KeySelector))

       Else

           ApplySort(Items.OrderByDescending(KeySelector))

       End If

   End Sub

   Public Overloads Sub Sort(Of TKey)(ByVal KeySelector As Func(Of T, TKey), ByVal comparer As IComparer(Of TKey))

       ApplySort(Items.OrderBy(KeySelector, comparer))

   End Sub

   Private Sub ApplySort(ByVal sortedItems As IEnumerable(Of T))

       Dim lst = sortedItems.ToList

       For Each item As T In lst

           Move(IndexOf(item), lst.IndexOf(item))

       Next

   End Sub

End Class

Example of how to use it:

Public Class Chicken

   Public Property Name() As String

   ......

End Class

Public Class IHasLotsaChickens

   Inherits SortableObservableCollection(Of Chicken)

   Public Sub SortMaChickens()

       Me.Sort(Function(chic) chic.Name, ListSortDirection.Descending)

   End Sub

End Class

haha :D

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required

About Beth Massi

Beth is a Program Manager on the Visual Studio Community Team at Microsoft and is responsible for producing and managing content for business application developers, driving community features and team participation onto MSDN Developer Centers (http://msdn.com), and helping make Visual Studio one of the best developer tools in the world. She also produces regular content on her blog (http://blogs.msdn.com/bethmassi), Channel 9, and a variety of other developer sites and magazines. As a community champion and a long-time member of the Microsoft developer community she also helps with the San Francisco East Bay .NET user group and is a frequent speaker at various software development events. Before Microsoft, she was a Senior Architect at a health care software product company and a Microsoft Solutions Architect MVP. Over the last decade she has worked on distributed applications and frameworks, web and Windows-based applications using Microsoft development tools in a variety of businesses. She loves teaching, hiking, mountain biking, and driving really fast.

This Blog

Syndication

Page view tracker