Tagged with events

Adding many entries to an Observable collection in a performance friendly way

I was writing a WPF application which was polling a lot of web services in order to display stuff in a list. The items change infrequently but required constant polling in order to make sure that they stayed the same. The problem appeared when they suddenly changed a lot . You see, the built-in Observable collection does not like when you toy around with it a lot and will send tons of events. So, while my app mostly chugged along with a few new entries an hour, sometimes it got a thousand in one fell swoop – causing unresponsiveness.

I googled around a bit, and found this
article
, that while good did not help my situation. Since I cannot know when I’ll receive many entries, and I’d like to have a simple interface I have improved on this solution a bit.

Behold, the DeferEventObservableCollection:

public class DeferEventObservableCollection : ObservableCollection
{
	private readonly List<NotifyCollectionChangedEventArgs> deferredEvents = new List<NotifyCollectionChangedEventArgs>();
	private bool hasQueuedDispatcherUpdate = false;
	private readonly int threshold;
	private object syncRoot = new object();

	public DeferEventObservableCollection(int threshold = 10)
	{
		this.threshold = threshold;
	}
	
	protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
	{
		lock (syncRoot)
		{
			deferredEvents.Add(e);
			if (!hasQueuedDispatcherUpdate)
			{
				Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
				{
					lock (syncRoot)
					{
						if (deferredEvents.Count > treshold)
						{
							base.OnCollectionChanged(
								new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
						}
						else
						{
							foreach (var ev in deferredEvents)
							{
								base.OnCollectionChanged(ev);
							}
						}
						deferredEvents.Clear();
						hasQueuedDispatcherUpdate = false;
					}
				}));
			}
			hasQueuedDispatcherUpdate = true;
		}
	}
}

What I have made is a collection that will seamlessly handle the issue of suddenly getting a lot of changes in a more graceful way. Adding an item or removing it will catch the event that the observable collection normally fires and queue it up on the dispatcher queue. Since you normally get your items added in the dispatcher thread this event will then be deferred to later on in the same chunk of dispatcher work (or the next chunk). Events keep piling up in this queue and once it’s time to fire the events the collection will evaluate the size of the event list. If it is above some threshold value it will replace the events in the queue with a single Reset event. You’ll need to experiment with the threshold to find the optimum number, the 10 is a raw guess, and it will vary on how many listeners you have on your collection.

Being the concurrency-aware programmer that I am, I have added some extra measure of thread safety so that it cannot go haywire. When emptying the event queue, if another thread should attempt to add a new even to the queue, the lock will prevent this from blowing up in your face.

This simple drop-in replacement changed the performance on my application by several orders of magnitude, and I hope you can find it as useful as I did in your project!

Edit: I fixed the locking, it no longer locks on this which is bad form . Instead it locks on a syncroot as it should. Thanks @pheiberg for pointing it out!

Tagged , , , , ,

I’ve recently had the dubious pleasure of getting to know C++/CLI and revisiting my good old buddy MFC. The task at hand was to integrate a WPF component into an existing MFC application. That task in itself is probably interesting enough to warrant it’s own post but suffice to say that I got this working properly. This means that we’ll end up with a class deriving CDialog that holds a WPF component inside it. Now we want to handle events. In WPF country, this is easy: add this to our WPF class:

public event EventHandler OnMyEvent;

And in our dialog we hold the WPF control in a gcroot<MyWPFControlClassName> . We also have a method we’d like to get called.

void MyDialogClass::OnEvent(System::Object ^sender, System::EventArgs ^args)
{
   // Do stuff
}

First off, what prompted this article is what does not work . You cannot from your unmanaged dialog class pass a pointer to your member function to your event like this.

wpfControl->OnMyEvent += gcnew System::EventHandler(
      this, 
      &MyNativeDialogClass::OnEvent);

This does not work, and it’s because your class is not a managed class since it’s not declared as ref class . The error message is also less than helpful. So how to solve this? You need a wrapper. Create this class somewhere in your project.

#pragma once

template<class T> ref class ManagedEventWrapper {
public:
   typedef void(T::*MemberFunction)(System::Object^ sender, System::EventArgs^ args);

   ManagedEventWrapper(T& host, MemberFunction function)
      : m_host (host)
      , m_function(function) 
   {
   }
   
   void OnEvent(System::Object ^sender, System::EventArgs ^args)
   {
      (m_host.*m_function)(sender, args);
   }

private:
   T& m_host;
   MemberFunction m_function;
};

If you then include this file in your MFC class, then you can subscribe to events like this:

wpfControl->OnMyEvent += gcnew System::EventHandler(
      gcnew ManagedEventWrapper<MyNativeDialogClass>(*this, &MyNativeDialogClass::OnEvent), 
      &ManagedEventWrapper<MyNativeDialogClass>::OnEvent);

This creates the neccessary layer of extra indirection around the native code to make the event handling work properly. You don’t need to save the reference to ManagedEventWraper since the reference is held by the System::EventHandler which makes sure it does not get garbage collected.

Tagged , , , ,
Follow