Mike
Mike

Reputation: 3284

Updating UI in realtime

I have to create a WPF UI, which subsribes to real time Fx Rate(Currency + rate) updates and displays them in a grid (roughly 1000 updates per second, which means each row in the grid could get updated upto 1000 times per second).The grid would have atleast 50 rows at any point in time.

Towards this, I have created a Viewmodel which subscribes to the update events, and store those updates inside a concurrent dictionary with key as symbol and value as a RateViewModel object. Then I have another observable collection which has all those rateviewmodel objects, and bind that to a grid.

Code:

public class MyViewModel
    {
        private readonly IRatesService ratesService;

        private readonly ConcurrentDictionary<string, RateViewModel> rateDictionary;
        private object _locker = new object();

        public MyViewModel(IRatesService ratesService)
        {
            this.ratesService = ratesService;
            this.ratesService.OnUpdate += OnUpdate;
            rateDictionary = new ConcurrentDictionary<string, RateViewModel>();
            RateViewModels = new ObservableCollection<RateViewModel>();            
        }

        private void OnUpdate(object sender, RateUpdateEventArgs e)
        {
            RateViewModel exisistingRate;
            if (!rateDictionary.TryGetValue(e.Update.Currency, out exisistingRate))
            {
                exisistingRate = new RateViewModel(new Rate(e.Update.Currency, e.Update.Rate));
                rateDictionary.TryAdd(e.Update.Currency, exisistingRate);                
                return;
            }

            lock (_locker)
            {
                exisistingRate.UpdateRate(e.Update.Rate);                
            }

            Application.Current.Dispatcher.BeginInvoke(new Action(() => SearchAndUpdate(exisistingRate)));
        }

        public ObservableCollection<RateViewModel> RateViewModels { get; set; }

        private void SearchAndUpdate(RateViewModel rateViewModel)
        {
            //Equals is based on Currency
            if (!RateViewModels.Contains(rateViewModel))
            {
                RateViewModels.Add(rateViewModel);
                return;
            }

            var index = RateViewModels.IndexOf(rateViewModel);
            RateViewModels[index] = rateViewModel;
        }      
    }

I have 4 questions over this:

Using .NET 4.0 and have omitted INPC for brevity.

*EDIT:*Could you please help me in rewriting this in a better manner taking all the 4 points into account? Psuedocode will do.

Thanks, -Mike

Upvotes: 7

Views: 3583

Answers (3)

Slugart
Slugart

Reputation: 4680

There are a couple of factors to take into account, especially if the number of displayed rates will change dynamically. I'm assuming the 1000 updates/sec are coming from a thread other than the UI thread.

The first is that you will need to marshall the updates to the UI thread - done for you for updates to an existing ViewModel, not done for you for new/deleted ViewModels. With a 1000 updates a second you probably want to control the granularity of the marshalling to the UI thread and the context switching that this entails. Ian Griffiths wrote a great blog series on this.

The second is that if you want your UI to remain responsive you probably want to avoid as many gen 2 garbage collections as possible which means minimising the pressure on the GC. This might be an issue in your case as you create a new Rate object update for each update.

Once you start to have a few screens that do the same thing you'll want to find a way to abstract this updating behaviour out into a common component. Other wise you'll be sprinkling threading code through your ViewModels which is error prone.

I've created an open source project, ReactiveTables, which addresses these three concerns and adds a couple of other features such as being able to filter, sort, join your model collections. Also there are demos showing how to use it with virtual grids to get the best performance. Maybe this can help you out/inspire you.

Upvotes: 0

Yaur
Yaur

Reputation: 7452

1) I wouldn't worry about 50 extra refs floating around

2) Yes, lockless data structures are doable. Interlocked Is your friend here and they are pretty much all one offs. ReaderWriterLock is another good option if you aren't changing what items are in your dictionary often.

3) Generally, if you are dealing with more data more data than the UI can handle you are going to want to do the updates in the background, only fire INPC on the UI thread, and more importantly have a facility to drop UI updates (while still updating the backing field). Basic approach is going to be something like:

  1. Do an Interlocked.Exchange on the backing field
  2. Use Interlocked.CompareExchange to set a private field to 1, if this returns 1 exit becuase there is still a pending UI update
  3. If Interlocked.CompareExchange returned 0, invoke to the UI and fire your property changed event and update you throttling field to 0 (technically there is more you need to do if you care about non x86)

4) SearchAndUpdate Seems superfluous... UpdateRate should be bubbling to the UI and you only need to Invoke to the UI thread if you need to add or remove an item to the observable collection.

Update: here is a sample implementation... things are little more complicated because you are using doubles which don't get atomicity for free on 32 bit CPUs.

class MyViewModel : INotifyPropertyChanged
{
    private System.Windows.Threading.Dispatcher dispatcher;

    public MyViewModel(System.Windows.Threading.Dispatcher dispatcher)
    {
        this.dispatcher = dispatcher;
    }


    int myPropertyUpdating; //needs to be marked volatile if you care about non x86
    double myProperty;
    double MyPropery
    {
        get
        {
            // Hack for Missing Interlocked.Read for doubles
            // if you are compiled for 64 bit you should be able to just do a read
            var retv = Interlocked.CompareExchange(ref myProperty, myProperty, -myProperty);
            return retv;
        }
        set
        {
            if (myProperty != value)
            {
                // if you are compiled for 64 bit you can just do an assignment here
                Interlocked.Exchange(ref myProperty, value);
                if (Interlocked.Exchange(ref myPropertyUpdating, 1) == 0)
                {
                    dispatcher.BeginInvoke(() =>
                    {
                        try
                        {
                            PropertyChanged(this, new PropertyChangedEventArgs("MyProperty"));
                        }
                        finally
                        {
                            myPropertyUpdating = 0;
                            Thread.MemoryBarrier(); // This will flush the store buffer which is the technically correct thing to do... but I've never had problems with out it
                        }
                    }, null);
                }

            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged = delegate {};


}    

Upvotes: 4

Patrick
Patrick

Reputation: 884

Mike -

I would approach this a little differently. You really dont need an Observable Collection unless new Fx rows are being added. Observable Collection as you know only gives you built-in change notification in that scenario. If you have a list of 50 rows (for example) and the Fx object (which represents each individual row) is updated 1000 times a second - then you can very well use the INotifyPropertyChanged on the Fx Properties on the Object and let that mechanism update the UI as they change. My line of thought is - this is a simpler approach for UI updates rather than move them from one collection to another

Now with regards to your second point - 1000 updates in a second (to an existing FX object) - which technically is unreadable from a UI perspective - the approach I have taken is freeze and thaw - which means you essentially intercept the InotifyPropertyChanged (as its firing to the UI) and keep it frequency based - so for example - every 1 sec - whatever my status of all FX objects is (refresh the UI). Now within that second - whatever updates happen to the FX properties - they keep overwriting on themselves - and the latest/correct value when the 1 second interval happens - is shown to UI. That way - data being shown to UI is always correct and relevant when its displayed to UI.

Upvotes: 3

Related Questions