.netinversion-of-controlopennetcfopennetcf.ioc

OpenNETCF.IoC.UI SmartPart creation failing


I am creating a Windows Mobile application and I am having some problems with the OpenNETCF.IoC.UI library when creating SmartParts with EventPublication's.

My subscription is on the main for container and the publication is on an object that inherits from OpenNETCF.IoC.UI.SmartPart

Application Startup

Public Class Startup
    Inherits SmartClientApplication(Of MainContainer)

    Public Shared Sub Main()
        Dim appStarter As New Startup
        appStarter.Start()
    End Sub

End Class

Subscription on MainContiner form

<EventSubscription(EventNames.Navigate, ThreadOption.UserInterface)> _
Public Sub NavigateSmartPart(ByVal sender As Object, ByVal e As GenericEventArgs(Of String))
     .....
End Sub

Publisher

Namespace Views

    Public Class ViewLogon

        <EventPublication(EventNames.Navigate)> _
        Friend Event NavigateToSmartPart As EventHandler(Of GenericEventArgs(Of String))

        Public Overrides Sub OnActivated()
        End Sub

    End Class

End Namespace

Issue

If I try to compose this smart part I get and NullReferenceException in the OpenNETCF.IoC.ObjectFactory.AddCollectionEventHandlers<TKey, TItem> method on the source.EventInfo.AddEventHandler(instance, intermediate); line.

Removing the EventPublication works without any problems.

        private static void AddCollectionEventHandlers<TKey, TItem>(object instance, IEnumerable<KeyValuePair<TKey, TItem>> collection, PublicationDescriptor[] sourceEvents, SubscriptionDescriptor[] eventSinks)
        {
            if (collection == null) return;

            var invokerControl = RootWorkItem.Items.Get<TheInvoker>("IOCEventInvoker");

            foreach (var item in collection)
            {
                if (instance.Equals(item.Value)) continue;

                if((item.Value is WorkItem) && (collection is ServiceCollection))
                {
                    // this prevents recursion (and a stack overflow) for WorkItems
                    continue;
                }

                foreach (var source in sourceEvents)
                {
                    // wire up events
                    foreach (var sink in GetEventSinksFromTypeByName(item.Value.GetType(), source.Publication.EventName, ThreadOption.Caller))
                    {
                        Delegate d = Delegate.CreateDelegate(source.EventInfo.EventHandlerType, item.Value, sink);
                        source.EventInfo.AddEventHandler(instance, d);
                    }

                    foreach (var sink in GetEventSinksFromTypeByName(item.Value.GetType(), source.Publication.EventName, ThreadOption.UserInterface))
                    {
                        // wire up event handlers on the UI thread
                        Delegate d = Delegate.CreateDelegate(source.EventInfo.EventHandlerType, item.Value, sink);

                        if (source.EventInfo.EventHandlerType == typeof(EventHandler))
                        {
                            // unsure why so far but this fails if the EventHandler signature takes a subclass of EventArgs as the second param
                            // and if you use just EventArgs, the arg data gets lost
                            BasicInvoker invoker = new BasicInvoker(invokerControl, d);
                            Delegate intermediate = Delegate.CreateDelegate(source.EventInfo.EventHandlerType, invoker, invoker.HandlerMethod);
                            source.EventInfo.AddEventHandler(instance, intermediate);
                        }
                        else if ((source.EventInfo.EventHandlerType.IsGenericType) && (source.EventInfo.EventHandlerType.GetGenericTypeDefinition().Name == "EventHandler`1"))
                        {
                            BasicInvoker invoker = new BasicInvoker(invokerControl, d);
                            Delegate intermediate = Delegate.CreateDelegate(source.EventInfo.EventHandlerType, invoker, invoker.HandlerMethod);
                            source.EventInfo.AddEventHandler(instance, intermediate);
                        }

                    }
                }

                // back-wire any sinks
                foreach (var sink in eventSinks)
                {
                    foreach (var ei in GetEventSourcesFromTypeByName(item.Value.GetType(), sink.Subscription.EventName))
                    {
                        try
                        {
                            // (type, consumer instance, consumer method)
                            Delegate d = Delegate.CreateDelegate(ei.EventHandlerType, instance, sink.MethodInfo);

                            if (sink.Subscription.ThreadOption == ThreadOption.Caller)
                            {
                                ei.AddEventHandler(item.Value, d);
                            }
                            else
                            {
                                // wire up event handlers on the UI thread
                                if ((ei.EventHandlerType.IsGenericType) && (ei.EventHandlerType.GetGenericTypeDefinition().Name == "EventHandler`1")
                                    || (ei.EventHandlerType == typeof(EventHandler))
#if !WINDOWS_PHONE
                                    || (ei.EventHandlerType == typeof(KeyEventHandler))
#endif


                      )
                            {
                                // unsure why so far but this fails if the EventHandler signature takes a subclass of EventArgs as the second param
                                // and if you use just EventArgs, the arg data gets lost


                                BasicInvoker invoker = new BasicInvoker(invokerControl, d);
                                Delegate intermediate = Delegate.CreateDelegate(ei.EventHandlerType, invoker, invoker.HandlerMethod);
                                ei.AddEventHandler(item.Value, intermediate);
                            }
                            else
                            {
                                throw new ArgumentException("ThreadOption.UserInterface only supported for EventHandler and EventHandler<T> events");
                            }
                        }
                    }
                    catch (ArgumentException)
                    {
                        throw new ArgumentException(string.Format("Unable to attach EventHandler '{0}' to '{1}'.\r\nDo the publisher and subscriber signatures match?", ei.Name, instance.GetType().Name));
                    }
                }
            }

            WorkItem wi = item.Value as WorkItem;
            if (wi != null)
            {
                AddEventHandlers(instance, wi, false);
            }
        }
    }

Stacktrace

   at System.Reflection.EventInfo.AddEventHandler(Object target, Delegate handler)
   at OpenNETCF.IoC.ObjectFactory.AddCollectionEventHandlers[TKey,TItem](Object instance, IEnumerable`1 collection, PublicationDescriptor[] sourceEvents, SubscriptionDescriptor[] eventSinks)
   at OpenNETCF.IoC.ObjectFactory.AddEventHandlers(Object instance, WorkItem root, Boolean walkUpToRoot)
   at OpenNETCF.IoC.ObjectFactory.AddEventHandlers(Object instance, WorkItem root)
   at OpenNETCF.IoC.ObjectFactory.DoInjections(Object instance, WorkItem root)
   at OpenNETCF.IoC.ManagedObjectCollection`1.Add(ISmartPart item, String id, Boolean expectNullId)
   at OpenNETCF.IoC.ManagedObjectCollection`1.AddNew(Type typeToBuild, String id, Boolean expectNullId, Boolean wrapDisposables)
   at OpenNETCF.IoC.ManagedObjectCollection`1.AddNew(Type typeToBuild, String id)
   at OpenNETCF.IoC.ManagedObjectCollection`1.AddNew[TTypeToBuild](String id)
   at StockMovement.IoC.Container.RegisterViews()
   at StockMovement.IoC.Container.RegisterParts(DeckWorkspace workspace)
   at StockMovement.MainContainer.MainContainerLoad(Object sender, EventArgs e)
   at System.Windows.Forms.Form.OnLoad(EventArgs e)
   at System.Windows.Forms.Form._SetVisibleNotify(Boolean fVis)
   at System.Windows.Forms.Control.set_Visible(Boolean value)
   at System.Windows.Forms.Application.Run(Form fm)
   at OpenNETCF.IoC.UI.SmartClientApplication`1.OnApplicationRun(Form form)
   at OpenNETCF.IoC.UI.SmartClientApplication`1.Start(IModuleInfoStore store)
   at OpenNETCF.IoC.UI.SmartClientApplication`1.Start()
   at StockMovement.Startup.Main()

Solution

  • OK, By shear luck I have figured it out. then event needs to be public in order to wire up the pub/sub.

    <EventPublication(EventNames.Navigate)> _
    Friend Event NavigateToSmartPart As EventHandler(Of GenericEventArgs(Of String))
    

    Changing to below works.

    <EventPublication(EventNames.Navigate)> _
    Public Event NavigateToSmartPart As EventHandler(Of GenericEventArgs(Of String))