Search code examples
c#.neteventsweak-eventsweakeventmanager

Using WeakEventManager in Windows.Forms application


When using weak events as described here http://wekempf.spaces.live.com/blog/cns!D18C3EC06EA971CF!373.entry in a Windows.Forms application the WeakEventManager leaks WeakReference objects. I think this is because without a WPF message loop the CleanupOperation is never executed, although ScheduleCleanup is called in WeakEventManager.ProtectedAddListener.

As a workaround I implemented a Cleanup function like so:

internal bool Cleanup()
{
    // The following is equivalent to 
    //    return this.Table.Purge(false);
    // but we need to use reflection to access the private members.

    PropertyInfo pi = typeof(WeakEventManager).GetProperty("Table", BindingFlags.Instance | BindingFlags.NonPublic);
    if (pi == null)
        return false;
    object table = pi.GetValue(this, null);
    MethodInfo mi = table.GetType().GetMethod("Purge", BindingFlags.Instance | BindingFlags.NonPublic);
    if (mi == null)
        return false;
    return (bool)mi.Invoke(table, new object[] { false });
}

and call this after every e.g. 16th call to ProtectedAddListener.

This works, but obviously I like to avoid this (ab)use of reflection.

So my questions are:

  1. is there a way to implement a cleanup function using public/protected members? WeakEventManager.Purge might be useful, but I don't know how to use it.
  2. is there an easy way to run a WPF message loop in a Windows.Forms based application?

Solution

  • This code builds a static function you can keep cached. It will eliminate the pain of reflection every time it is run and essentially does just what you had. Cache it somewhere and invoke it by passing in your weak event manager every time. Except for a one time hit (during the building/compile time) there is no further reflection.

        using System.Windows;
        using System.Linq.Expressions;
        using Expression = System.Linq.Expressions.Expression;
    
        static void Main(string[] args)
        {
            Func<WeakEventManager, bool> cleanUpFunction = BuildCleanUpFunction();
        }
    
        private static Func<WeakEventManager, bool> BuildCleanUpFunction()
        {
            ParameterExpression managerParameter = Expression.Parameter(
                typeof(WeakEventManager),
                "manager"
            );
            return Expression.Lambda<Func<WeakEventManager, bool>>(
                Expression.Call(
                    Expression.Property(
                        managerParameter,
                        "Table"
                    ),
                    "Purge",
                    Type.EmptyTypes,
                    Expression.Default(
                        typeof(bool)
                    )
                ),
                managerParameter
            ).Compile();
        }