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:

  1. Navigate to the event in Intellisense.
  2. Type +=.
  3. Press Tab.
  4. 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).

This entry was posted in C#, Programming, Tricks. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>