Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to programmatically add a TypedEventHandler to RoutedEvents?

I'm working on an Extension method and I'm having some problems with a part of it.

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

NOTE: the code is in a PCL that targets Windows8.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 just fine with standard events, so no problems here. When I have RoutedEvents though, I get an exception. I ended up trying to use the TypedEventHandler class as I didn't find another way to get the right delegate for those events (like all the pointer events). But when I try to add the handler, I get an InvalidOperationException.

Is it possible to create handlers at runtime for RoutedEvents as well, or it is just not possible on WinRT?

Thanks for your help!

Sergio

like image 282
Sergio0694 Avatar asked Dec 13 '25 18:12

Sergio0694


1 Answers

You get that exception because WinRT won't allow you to add an event handler using the AddHandler method. The only way i know that can do this is by 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 the handler, use this:

WindowsRuntimeMarshal.RemoveEventHandler(remove, handler);
like image 172
MrCSharp Avatar answered Dec 16 '25 11:12

MrCSharp



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!