Monday, February 17, 2014

Simple ObservableCollection WPF MVVM example

Using ObservableCollection has its benefit. Whenever any element changes (added, deleted or updated or entire list being replaced) within the ObservableCollection instatnce, the bound UI element automatically reflects the changes.


If you would like to know more details of ObservableCollection, please read from the API link below.

http://msdn.microsoft.com/en-us/library/ms668604%28v=vs.110%29.aspx


We can use ObservableCollection object and bindable property in a View Model class and bind this to a UI control such as DataGrid's ItemsSource property. Whenever anything changes in the ObservableCollection, bound grid will automatically reflect the changes in the UI.

ViewModel class as follows:



    public class TradesViewModel : BaseViewModel, ITradeViewModel
    {
        private ObservableCollection<Trade> _trades = new ObservableCollection<Trade>();

        private ICommand _saveCommand;
        private ICommand _loadCommand;

        public TradesViewModel()
        {
            _saveCommand = new SaveTradesCommand();
            _loadCommand = new LoadTradesCommand();
            _trades = GetTrades();
        }

        public ObservableCollection<Trade> Trades { get {return _trades;} }

        public ObservableCollection<Trade> GetTrades()
        {
            if (_trades == null || _trades.Count == 0)
                _loadCommand.Execute(_trades);
            return _trades;
        }
    }



Lets say _loadCommand's code populates the _trades ( ObservableCollection). For demonstrating live updates we have made a infinite loop in the loading where every 2 seconds it loads new trades with quantity and price being different. Below is the code loading trades data every 2 seconds.



public void Execute(object parameter)
        {
            var loadedTrades = parameter as ObservableCollection<Trade>;
            if (loadedTrades != null)
            {
                TaskFactory taskfac = new TaskFactory();
                taskfac.StartNew(() =>
                    {
                        while (true)
                        {
                            var tradelist = Trades.LoadTradesFromDataSource();
                            App.Current.Dispatcher.Invoke(new Action(() =>
                            {
                                loadedTrades.Clear();
                                foreach (var trade in tradelist)
                                    loadedTrades.Add(trade);
                            }));

                            Thread.Sleep(2000);
                        }
                    });
            }
        }


Trade and load trades code:



public class Trade : ITrade   
{
        private string _qty;
        public string Side { get; set; }
        public string Ticker { get; set; }
        public string Qty { get; set; }
        public string Price { get; set; }
        public string Trader { get; set; }
        public string Sales { get; set; }
       }





public class Trades
       {
        public static IEnumerable<Trade> LoadTradesFromDataSource()
        {
            var pricetmp = DateTime.Now.Second; // just to get some number
            var qtytmp = (DateTime.Now.Second + 5) * 1000; // just to get some
number

            return new List<Trade>()
            {
                new Trade(){Side="B", Price=pricetmp.ToString(), Ticker="aol",
Trader="tom", Sales="harry", Qty=qtytmp.ToString()},
                new Trade(){Side="B", Price=pricetmp.ToString(), Ticker="gm",
Trader="tom", Sales="harry", Qty=qtytmp.ToString()},
                new Trade(){Side="B", Price=pricetmp.ToString(), Ticker="ge",
Trader="tom", Sales="harry", Qty=qtytmp.ToString()},
            };
        }
       }



XAML code as below binds the ItemsSource property of the DataGrid to our ViewModel's ObservableCollection property called Trades.


<Grid.DataContext>
            <viewmodel:TradesViewModel/>
        </Grid.DataContext>
        <DataGrid
                AutoGenerateColumns="True"
                HorizontalAlignment="Stretch" Name="dataGrid1" VerticalAlignment="Top"
                ItemsSource="{Binding Trades}"
                />


When we run this application, we see a window coming up with trades populated like below:




and every two seconds it will automatically show new trades data like below:




So as soon as the ObservableCollection data is being updated it is reflecting real-time on the DataGrid.


Notice three things:

Notice 1: I created a new "task" to run the infinite loop to update data every two seconds and therefore had to update using App.Current.Dispatcher.Invoke since ObservableCollection object was instantiated in UI thread. (see details here http://wpfgrid.blogspot.com/2014/02/wpf-using-dispatcher-in-viewmodel.html)

Notice 2:
Also if we simply make _trades = new ObservableCollection ...  it will not wok. Rather we need to clear the collection and add new instances of Trade objects.

Notice 3:
Since in this exmaple (only) we are replacing the enter list (by clearing and add all elements), we did not need to implement INotofyPropertyChanged niterface in "Trade" class. However, we should implement that interface in Trade class and have each property raise property changed event, that way when any property value changes (instead of entire list being changed as shown in this example), it will reflect the change of that particular cell in the DataGrid automatically.


DOWNLOAD CODE

Codes for this simple exmaple can be downloaded from my skydrive here:
https://skydrive.live.com/?mkt=en-US#cid=BA4BC1E3D681B9B3&id=BA4BC1E3D681B9B3!105
Called: SimpleObservableCollectionWPFExample.zip




4 comments:

  1. There are parts missing in this example.

    ReplyDelete
  2. There are parts missing in this example.

    ReplyDelete
  3. Curious. We call execute if _trades is null. After conversion back to OC we would still have null, and on null we .... do nothing?

    ReplyDelete