PublishedEvents modification

Sep 9, 2009 at 9:30 PM

I have been using the "UpdateView" mechanism of Mobile MVC, but was happy to see the new event based method of communication.  When I was converting my code to use the events I realized that I would have to use Invoke to update the view from my controller thread.  I liked how "UpdateView" invoked the gui thread automatically and saved me from having to repeat the same code over and over again so I set out to try and do that with the event method.

To accomplish it I added a PublishedEventConnector class that gets registered for the events instead of the actual event handler.  When an event fires the PublishedEventConnector object will check to see if an invoke is required to the target thread, and perform it if necessary.  Here is PublishedEventConnector class:

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Windows.Forms;

namespace System.Mobile.Mvc
{
    internal class PublishedEventConnector
    {
        private object target;
        private EventInfo sourceEventInfo;
        private MethodInfo consumerMethodInfo;

        public PublishedEventConnector(object target, EventInfo sourceEventInfo, MethodInfo consumerMethodInfo)
        {
            this.target = target;
            this.sourceEventInfo = sourceEventInfo;
            this.consumerMethodInfo = consumerMethodInfo;
        }

        public void OnEvent(object sender, EventArgs e)
        {
            Type targetType = this.target.GetType();
            if (targetType.IsSubclassOf(typeof(Form)))
            {
                Form formTarget = (Form)this.target;
                if (formTarget.InvokeRequired)
                {
                    var consumer = Delegate.CreateDelegate(this.sourceEventInfo.EventHandlerType, this.target, this.consumerMethodInfo);
                    formTarget.Invoke(consumer, new object[] { sender, e });
                    return;
                }
            }

            this.consumerMethodInfo.Invoke(this.target, new object[] { sender, e });
        }
    }
}

 

Then in the Controller class I had to modify HookEvents to this:

private void HookEvents(object source, object target, MethodInfo[] tagetMethods)
        {
            EventInfo[] events = source.GetType().GetEvents(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
            foreach (EventInfo eventInfo in events)
            {
                object[] attribs = eventInfo.GetCustomAttributes(false);
                if (attribs.Length > 0 && attribs[0] is PublishEventAttribute)
                {
                    PublishEventAttribute eventAttrib = attribs[0] as PublishEventAttribute;
                    MethodInfo method = tagetMethods.FirstOrDefault<MethodInfo>(m => m.Name == eventAttrib.TargetName);
                    if (method != null)
                    {
                        PublishedEventConnector eventConnector = new PublishedEventConnector(target, eventInfo, method);
                        MethodInfo targetMethod = eventConnector.GetType().GetMethod("OnEvent");
                        var deleg = Delegate.CreateDelegate(eventInfo.EventHandlerType, eventConnector, targetMethod);
                        eventInfo.AddEventHandler(source, deleg);
                    }
                }
            }
        }

This has not been thoroughly tested, but I have seen it work on a CE 5.0 device, and with the full framework.  I'd be happy to contribute it if the framework architects thought it could be built in permanently.