How can I programmatically add TypedEventHandler to RoutedEvents?

I am working on an extension method and am having some problems with part of it.

This is the idea: I want to create a method that takes an event object and name, waits for the event to be raised, and then returns its event arguments. I would use it to avoid having to manually create delegates and things every time I do something like this in my code.

NOTE. the code is in the PCL, which is for Windows 8.1 and Windows Phone 8.1, so some reflection methods are not available here.

This is what I have so far:

/// <summary>
/// Waits for an event to be raised and returns its arguments
/// </summary>
/// <typeparam name="Targs">The type of the event args to return</typeparam>
/// <param name="target">The target object that will raise the event</param>
/// <param name="eventName">The name of the event to wait</param>
/// <param name="timeout">The time limit (in millisecond) to wait for the desired event to be raised</param>
public static async Task<Targs> WaitEventAsync<Targs>(this object target, String eventName, int timeout)
    where Targs : class
{
    // Arguments check
    if (target == null) throw new ArgumentNullException("The target object can't be null");
    if (eventName == null) throw new ArgumentNullException("The event name can't be null");
    if (timeout <= 0) throw new ArgumentOutOfRangeException("The timeout must be greater than 0");

    // Get the target event
    EventInfo eventInfo = target.GetType().GetRuntimeEvent(eventName);
    if (eventInfo == null) throw new ArgumentException(String.Format("The target object doesn't contain the {0} event", eventName));

    // Prepare the TaskCompletionSource, the return variable and the right handler
    TaskCompletionSource<Targs> tcs = new TaskCompletionSource<Targs>();

    Delegate handler;

    if (eventInfo.EventHandlerType.Equals(typeof(EventHandler<Targs>)))
    {
        handler = new EventHandler<Targs>((sender, args) =>
        {
            tcs.SetResult(args);
        });
    }
    else
    {
        // PROBLEM: when this line is executed, the AddEventHandler method crashes
        handler = new TypedEventHandler<object, Targs>((sender, args) => 
       { 
           tcs.SetResult(args); 
       });
    }

    // Add the handler and wait for the event
    eventInfo.AddEventHandler(target, handler);
    CancellationTokenSource cts = new CancellationTokenSource(timeout);
    try
    {
        // If the event was triggered before the timout expired, return its args
        return await tcs.Task.GetWatchedTask(cts);
    }
    catch (OperationCanceledException)
    {
        // If the timout expired, just return null
        return null;
    }
    finally
    {
        // Remove the handler from the target object
        eventInfo.RemoveEventHandler(target, handler);
    }
}

      

Now everything works fine with standard events, so there are no problems here. However, when I have RoutedEvents

, I get an exception. I ended up trying to use the class TypedEventHandler

as I couldn't find any other way to get the required delegate for these events (like all pointer events). But when I try to add a handler, I get InvalidOperationException

.

Is it possible to create runtime handlers for RoutedEvents, or is it just not possible in WinRT?

Thank you for your help!

Sergio

+3


source to share


1 answer


You are getting this exception because WinRT will not let you add an event handler using the AddHandler method. The only way I know can do it using this code:

    private void AddHandler(FrameworkElement element, object parameter, EventInfo eventInf)
    {
        var addMethod = eventInf.AddMethod;
        var removeMethod = eventInf.RemoveMethod;

        var addParameters = addMethod.GetParameters();
        var delegateType = addParameters[0].ParameterType;

        Action<object, object> handler = (s, e) => ExecuteCommand();
        var handlerInvoke = typeof(Action<object, object>).GetRuntimeMethod("Invoke", new[] { typeof(object), typeof(object) });

        var @delegate = handlerInvoke.CreateDelegate(delegateType, handler);

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

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

      



To remove a handler use this:

WindowsRuntimeMarshal.RemoveEventHandler(remove, handler);

      

+2


source







All Articles