WPF Datagrid – Update Source on fly with BindingList<T>


 

I was under an assumption that ObservableCollection<T> works perfect with WPF DataGrid until my friend wannabeegeek came up with a strange problem. The problem was making ObservableCollection<T> update the ItemSource on fly while editing them. What’s the big deal right? Wrong.

If you are using MVVM you do not want to wire up the event on DataGrid for an updated source. You will very much think that as long as you bind the collection to the ViewModel’s property and the mode is set to TwoWay – the source is updated automatically when items are edited in the DataGrid.

Your ViewModel will fire the property changed when the collection itself changes – that is if you replace the property itself.  So if you want to know the change in items of the collection you need to subscribe to the CollectionChanged event of the ObservableCollection<T> property in your ViewModel.

MSDN says:

Occurs when an item is added, removed, changed, moved, or the entire list is refreshed.

But there is a problem with ObservableCollection.CollectionChanged event –  it does not fire when your data item’s property changes. It fires only if the entire item is replaced. That is if you have a collection of an Entity “Expense” that has properties “Name” and “Amount” and if you simply change the Amount value in the grid it does not work! You need to replace the entire “Expense” object for you to see the collection change firing up. 

After googling and reading msdn forums I gave up on ObservableCollection<T> and picked up BindingList<T> to do the job.  Here is a demo that explains all the stuff:

.. and the screen shot:

ExpenseView

I have a DataGrid that is simply bound to an “BindingList<Expense>”. Right below that I have a Total that is bound to the “TotalExpense” property. Now here is what I wanted to do – Whenever user edits the amount in the Grid the Total should be calculated as he/she tabs out.

.. and the ViewModel with the solution:

  1. public class DataGridDisplayViewModel : ViewModelBase
  2.     {
  3.         private BindingList<Expense> _expenses;
  4.         private double _totalExpense;
  5.  
  6.         public double TotalExpense
  7.         {
  8.             get { return _totalExpense = _expenses.Sum(x => x.Amount); }
  9.             set { _totalExpense = value; }
  10.         }
  11.         
  12.         public BindingList<Expense> Expenses
  13.         {
  14.             get { return _expenses; }
  15.             set
  16.             {
  17.                 _expenses = value;
  18.                 if (_expenses != null)
  19.                 {
  20.                     _expenses.ListChanged += (o, e) => RaisePropertyChanged(() => this.TotalExpense);
  21.                 }
  22.                 RaisePropertyChanged(() => this.Expenses);
  23.             }
  24.         }
  25.  
  26.       
  27.         public DataGridDisplayViewModel()
  28.         {
  29.             Expenses = new BindingList<Expense>
  30.                             {
  31.                                 new Expense() {Name = "Gas", Amount = 1000},
  32.                                 new Expense() {Name = "Electricity", Amount = 1000},
  33.                                 new Expense() {Name = "Internet", Amount = 100},
  34.                                 new Expense() {Name = "Water", Amount = 100},
  35.                                 new Expense() {Name = "Misc", Amount = 1000}
  36.                             };
  37.         }
  38.     }

The TotalExpense getter simply calculates the sum. And the ListChanged event of Expenses property simply notifies the change in TotalExpense.

.. and in the View:

  1. <Grid>
  2.     <Grid.RowDefinitions>
  3.         <RowDefinition/>
  4.         <RowDefinition/>
  5.     </Grid.RowDefinitions>
  6.     <DataGrid ItemsSource="{Binding ExpensesOb, Mode=TwoWay}" AutoGenerateColumns="True" Grid.Row="0"/>
  7.     <TextBlock Grid.Row="1">
  8.         <Run Text="Total: "/>
  9.         <Run Text="{Binding TotalExpenseOb}"/>
  10.     </TextBlock>
  11. </Grid>

 

That’s it!

Download the full code here. The code contains a tiny MVVM framework(inside core folder). – Read the disclaimer of the blog before you play with my code.

If you know to achieve this exact behavior with ObservableCollection do make a comment to this post and let me know!

/*Nish*/

Advertisements

4 thoughts on “WPF Datagrid – Update Source on fly with BindingList<T>

  1. I made my own ObservableCollection derived class that invokes the CollectionChanged event whenever I need. I don;t know the internals of BindingList so I thought of making a custom solution. Works fine.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s