3

I'm implementing modified version of Behavior for my Windows 8 app according to this guide. It works except one place where the Reactive framework is required:

protected override void OnAttached()
{
  var evt = AssociatedObject.GetType().GetRuntimeEvent(Event);
  if (evt != null)
  {
    Observable.FromEventPattern<RoutedEventArgs>(AssociatedObject, Event)
      .Subscribe(se => FireCommand());
  }
  base.OnAttached();
}

The question is simple, how to achieve similar funcitonality without the Reactive frmaework? I've browsed the source of of Rx that can be obtained here, but I'ts just too complicated to me.
I've also succeeded porting to code with the only problem that it work only for fixed type of EventHandler:

protected override void OnAttached()
{
    EventInfo evt = AssociatedObject.GetType().GetRuntimeEvent(Event);
    if (evt != null)
    {
        AssignEvent<ItemClickEventHandler>(AssociatedObject, Event, FireCommand);
    }
    base.OnAttached();
}

protected void AssignEvent<T1>(object instance, string eventName, T1 handler)
{
    EventInfo runtimeEvent = instance.GetType().GetRuntimeEvent(eventName);
    Func<T1, EventRegistrationToken> add = a => (EventRegistrationToken)runtimeEvent.AddMethod.Invoke(instance, new object[] { a });
    Action<EventRegistrationToken> remove = a => runtimeEvent.RemoveMethod.Invoke(runtimeEvent, new object[] { a });

    WindowsRuntimeMarshal.AddEventHandler(add, remove, handler);
}

Any idea, how to make it dynamic so I don't have to use specific event handler "ItemClickEventHandler"? Note in classic .NET it' quite simple, but in WinRT I cannot use Delegate.CreateDelegate(...)

Update: Thanks to Brandon I was able to finish the method, it now looks like this:

protected override void OnAttached()
{
    EventInfo evt = AssociatedObject.GetType().GetRuntimeEvent(Event);
    if (evt != null)
    {
        MethodInfo addMethod = evt.AddMethod;
        MethodInfo removeMethod = evt.RemoveMethod;
        ParameterInfo[] addParameters = addMethod.GetParameters();
        Type delegateType = addParameters[0].ParameterType;
        Action<object, object> handler = (s, e) => FireCommand(e as RoutedEventArgs);
        MethodInfo handlerInvoke = typeof(Action<object, object>).GetRuntimeMethod("Invoke", new[] { typeof(object), typeof(object) });
        Delegate @delegate = handlerInvoke.CreateDelegate(delegateType, handler);

        Func<object, EventRegistrationToken> add = a => (EventRegistrationToken)addMethod.Invoke(AssociatedObject, new object[] { @delegate });
        Action<EventRegistrationToken> remove = t => removeMethod.Invoke(AssociatedObject, new object[] { t });

        WindowsRuntimeMarshal.AddEventHandler(add, remove, handler);
    }
    base.OnAttached();
}

Now I can remove 800kB of Rx dlls, thanks again!

Martin Suchan
  • 10,600
  • 3
  • 36
  • 66
  • Any particular reason to remove the Rx code? I thought there was a core Rx library that worked even on RT? – Brandon May 20 '13 at 18:36
  • First of all I want to decrease the size of my app package to half and also I want to know, how this works internally. – Martin Suchan May 21 '13 at 07:44
  • Is it a requirement that you register the event handler via `WindowsRuntimeMarshal.AddEventHandler()`? It is not hard to solve the problem if we can just call the `Add` and `Remove` methods of the EventInfo object. – Brandon May 21 '13 at 19:56
  • 1
    First of all for using EventInfo.Add you need to know the target EventHandler type and second, when I tried to use it with the proper type: "Adding or removing event handlers dynamically is not supported on WinRT events." – Martin Suchan May 22 '13 at 07:42

1 Answers1

2

I trolled through the Rx source, and here is the important bit of functionality:

MethodInfo addMethod = eventInfo.GetAddMethod();
MethodInfo removeMethod = eventInfo.GetRemoveMethod();
var addParameters = addMethod.GetParameters();
var delegateType = addParameters[0].ParameterType;
Action<object, object> handler = (object sender, object eventArgs) => FireCommand();
MethodInfo handlerInvoke = typeof(Action<object, object>).GetMethod("Invoke");
Delegate delegate = handlerInvoke.CreateDelegate(delegateType, handler);

Func<EventRegistrationToken> add = a => (EventRegistrationToken)addMethod.Invoke(instance, new object[] { delegate });
Action<EventRegistrationToken> remove = t => removeMethod.Invoke(instance, new object[] { t });

It looks like the important info is they are using MethodInfo.CreateDelegate.

Brandon
  • 38,310
  • 8
  • 82
  • 87