InvalidOperationException - “The calling thread cannot access this object because a different thread owns it.”
It’s all due to the multi threaded nature of what’s going on in the background and thankfully the multithreading is going on, giving us our dynamic, performing environment. But there is a slight addition in complexity to resolve this issue, which is the Dispatcher
The Dispatcher is linked to the UI thread to handle events which require access to UI elements, and in the WPF world it looks a bit simpler than the world of Win Forms. To utilise the Dispatcher there is a bit of setup to complete.
To put this example in context, look at the following screen shot:
<Screen shot to come another day, just imagine a piano keyboard for now>
This is part of a larger custom control, representing a keyboard scale. Each key is made up of a rectangle. The fill can be replaced with an alternate fill to indicate that the key is down and subsequently reverted back to the original fill when the key is released. This control has 2 methods to invoke this functionality:
ColourKey(int key);
UnColourKey(int key);
An instance of this control has been created and added on to a WPF window and linked via the MIDI Framework provided by NAudio, to a MIDI Controller Keyboard. These methods on the custom control can’t be called directly from a method not being invoked from the UI – i.e not a button click. As such we need to utilise a delegate, I’ve opted for two in this example.
//// This delegate enables asynchronous calls for setting
//// the Colour of the key
delegate void dColourKey(int key);
delegate void dUnColourKey(int key);
These delegates provide a safe way to call the methods which have been setup on the custom control.:
/// <summary>
/// Called via the delegate after using this.Dispatcher.Invoke
/// </summary>
/// <param name="key">The key to colour on the scale control</param>
private void ColourKey(int key)
{
scale1.ColourKey(key);
}
/// <summary>
/// Called via the delegate after using this.Dispatcher.Invoke
/// </summary>
/// <param name="key">The key to UnColour on the scale control</param>
private void UnColourKey(int key)
{
scale1.UnColourKey(key);
}
To access these methods, via the delegate we need to use the following;
this.Dispatcher.Invoke(new dColourKey(ColourKey),ne.NoteNumber);
A small explanation is in order. The first part is obviously calling the Invoke method on the Dispatcher. The two parameters are where it all happens. The first is initialising the delegate, and passing in the name of the method which is to be called – in this example, the name of the method is ColourKey:
new dColourKey(ColourKey)
The second is the argument which will be passed to the calling method & in this case it’s the number of the note which is being played:
ne.NoteNumber
This small overhead ensures that we don’t have a situation where our InvalidOperationException will be generated.
Extended Learning:
One of the reasons I like to write up items like this is so that I can increase my own understanding about the topic. Being able to explain a concept to some one else not only assists the person who your explaining it to but reinforces your own understanding and through writing this post I’ve opened my own eyes further. I’ve refracted my own code and included the Dispatcher and Delegate code in the custom control; which means it’s no longer required by the calling WPF form. Amazing.
No comments:
Post a Comment