C# supports implicit/explicit conversions to Delegates.
Yes, big news. I was very surprised when I gave this a shot. It’s really just one of those things you really don’t expect to work. So how does this help us? I am sure you are quite sick of writing the following code (the same stuff applies to WPF, you just need to access it via the Dispatch member):
The ‘big news’ allows us to do all the heavy lifting for the dispatch BeginInvoke in one simple class (this is a first try – I’ll get round to reviewing it later on).
/// <summary>
/// Represent a wrapper for a delegate.
/// </summary>
/// <typeparam name="T">The type of event args.</typeparam>
public class InvokeEventWrapper<T>
where T : EventArgs
{
private EventHandler<T> _apparantHandler;
private EventHandler<T> _rawHandler;
private Control _parent;
/// <summary>
/// Initializes a new instance of the <see cref="InvokeDelegateWrapper<T>"/> class.
/// </summary>
/// <param name="parent">The parent.</param>
/// <param name="handler">The handler.</param>
public InvokeEventWrapper(EventHandler<T> handler)
: this(null, handler)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="InvokeDelegateWrapper<T>"/> class.
/// </summary>
/// <param name="parent">The parent.</param>
/// <param name="handler">The handler.</param>
public InvokeEventWrapper(Control parent, EventHandler<T> handler)
{
if (handler == null)
throw new ArgumentNullException("handler");
if (parent == null)
parent = (Control)handler.Target;
_parent = parent;
_rawHandler = handler;
_apparantHandler = new EventHandler<T>(Raise);
}
/// <summary>
/// Performs an implicit conversion from <see cref="SharedTerminals.InvokeDelegateWrapper<T>"/> to <see cref="System.EventHandler<T>"/>.
/// </summary>
/// <param name="wrapper">The wrapper.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator EventHandler<T>(InvokeEventWrapper<T> wrapper)
{
return wrapper._apparantHandler;
}
/// <summary>
/// Raises the event.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The e.</param>
private void Raise(object sender, T e)
{
if (_parent.InvokeRequired)
_parent.BeginInvoke(_rawHandler, sender, e);
else
_rawHandler(sender, e);
}
}
Using it is pretty simple:
Navigate to the event in Intellisense.
Type +=.
Press Tab.
Replace EventHandler with InvokeEventWrapper.
So in the end all of that ugly code becomes:
_items.CollectionChanged += new InvokeEventWrapper<CollectionChangedEventArgs<ConnectionListItem>>( _items_CollectionChanged );
You will need to hold onto a reference for that wrapper if you wish to unsubscribe from the event (one of the things I need to look at).
Jonathan Dickinson works at SourceCode. Everything posted on this blog is his personal opinion and do not necessarily represent the views of his employer or his employer's clients.
Automatically Wrap BeginInvoke (dispatch)
C# supports implicit/explicit conversions to Delegates.
Yes, big news. I was very surprised when I gave this a shot. It’s really just one of those things you really don’t expect to work. So how does this help us? I am sure you are quite sick of writing the following code (the same stuff applies to WPF, you just need to access it via the Dispatch member):
void _items_CollectionChanged(object sender, CollectionChangedEventArgs<ConnectionListItem> e) { if (InvokeRequired) { Invoke(new EventHandler<CollectionChangedEventArgs<ConnectionListItem>>( _items_CollectionChanged ), sender, e); return; } }The ‘big news’ allows us to do all the heavy lifting for the dispatch BeginInvoke in one simple class (this is a first try – I’ll get round to reviewing it later on).
/// <summary> /// Represent a wrapper for a delegate. /// </summary> /// <typeparam name="T">The type of event args.</typeparam> public class InvokeEventWrapper<T> where T : EventArgs { private EventHandler<T> _apparantHandler; private EventHandler<T> _rawHandler; private Control _parent; /// <summary> /// Initializes a new instance of the <see cref="InvokeDelegateWrapper<T>"/> class. /// </summary> /// <param name="parent">The parent.</param> /// <param name="handler">The handler.</param> public InvokeEventWrapper(EventHandler<T> handler) : this(null, handler) { } /// <summary> /// Initializes a new instance of the <see cref="InvokeDelegateWrapper<T>"/> class. /// </summary> /// <param name="parent">The parent.</param> /// <param name="handler">The handler.</param> public InvokeEventWrapper(Control parent, EventHandler<T> handler) { if (handler == null) throw new ArgumentNullException("handler"); if (parent == null) parent = (Control)handler.Target; _parent = parent; _rawHandler = handler; _apparantHandler = new EventHandler<T>(Raise); } /// <summary> /// Performs an implicit conversion from <see cref="SharedTerminals.InvokeDelegateWrapper<T>"/> to <see cref="System.EventHandler<T>"/>. /// </summary> /// <param name="wrapper">The wrapper.</param> /// <returns>The result of the conversion.</returns> public static implicit operator EventHandler<T>(InvokeEventWrapper<T> wrapper) { return wrapper._apparantHandler; } /// <summary> /// Raises the event. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The e.</param> private void Raise(object sender, T e) { if (_parent.InvokeRequired) _parent.BeginInvoke(_rawHandler, sender, e); else _rawHandler(sender, e); } }Using it is pretty simple:
So in the end all of that ugly code becomes:
You will need to hold onto a reference for that wrapper if you wish to unsubscribe from the event (one of the things I need to look at).