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()
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))