Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <h2>'The' answer</h2> <p><em>(Read more below if you want to see how I got to this solution)</em></p> <p>Usage, given a control with a vanilla <code>MouseDown</code> event, and a specific <code>EventHandler&lt;ValueEventArgs> ValueEvent</code> event:</p> <pre><code>// for 'vanilla' events SetAnyHandler&lt;Subscriber, MouseEventHandler, MouseEventArgs&gt;( h =&gt; (o,e) =&gt; h(o,e), //don't ask me, but it works*. h =&gt; control.MouseDown += h, h =&gt; control.MouseDown -= h, subscriber, (s, e) =&gt; s.DoSomething(e)); //**See note below // for generic events SetAnyHandler&lt;Subscriber, ValueEventArgs&gt;( h =&gt; control.ValueEvent += h, h =&gt; control.ValueEvent -= h, subscriber, (s, e) =&gt; s.DoSomething(e)); //**See note below </code></pre> <p><em>(*This is a workaround from <a href="http://social.msdn.microsoft.com/Forums/en-US/rx/thread/50d01578-9217-43d3-8ccb-e3f859040a68" rel="nofollow noreferrer">Rx</a>)</em></p> <p>(** it is important to avoid invoking the subscriber object directly here (for instance putting subscriber.DoSomething(e), or invoking DoSomething(e) directly if we are inside the Subscriber class. Doing this effectively creates a reference to subscriber, which completely defeats the object...)</p> <p><strong>Note</strong>: in some circumstances, this CAN leave references to the wrapping classes created for the lambdas in memory, but they only weigh bytes, so I'm not too bothered.</p> <p>Implementation:</p> <pre><code>//This overload handles any type of EventHandler public static void SetAnyHandler&lt;S, TDelegate, TArgs&gt;( Func&lt;EventHandler&lt;TArgs&gt;, TDelegate&gt; converter, Action&lt;TDelegate&gt; add, Action&lt;TDelegate&gt; remove, S subscriber, Action&lt;S, TArgs&gt; action) where TArgs : EventArgs where TDelegate : class where S : class { var subs_weak_ref = new WeakReference(subscriber); TDelegate handler = null; handler = converter(new EventHandler&lt;TArgs&gt;( (s, e) =&gt; { var subs_strong_ref = subs_weak_ref.Target as S; if(subs_strong_ref != null) { action(subs_strong_ref, e); } else { remove(handler); handler = null; } })); add(handler); } // this overload is simplified for generic EventHandlers public static void SetAnyHandler&lt;S, TArgs&gt;( Action&lt;EventHandler&lt;TArgs&gt;&gt; add, Action&lt;EventHandler&lt;TArgs&gt;&gt; remove, S subscriber, Action&lt;S, TArgs&gt; action) where TArgs : EventArgs where S : class { SetAnyHandler&lt;S, EventHandler&lt;TArgs&gt;, TArgs&gt;( h =&gt; h, add, remove, subscriber, action); } </code></pre> <h2>The detail</h2> <p>My starting point was <a href="https://stackoverflow.com/questions/371109/garbage-collection-when-using-anonymous-delegates-for-event-handling/1447619#1447619">Egor</a>'s excellent answer (see link for version with comments):</p> <pre><code>public static void Link(Publisher publisher, Control subscriber) { var subscriber_weak_ref = new WeakReference(subscriber); EventHandler&lt;ValueEventArgs&lt;bool&gt;&gt; handler = null; handler = delegate(object sender, ValueEventArgs&lt;bool&gt; e) { var subscriber_strong_ref = subscriber_weak_ref.Target as Control; if (subscriber_strong_ref != null) subscriber_strong_ref.Enabled = e.Value; else { ((Publisher)sender).EnabledChanged -= handler; handler = null; } }; publisher.EnabledChanged += handler; } </code></pre> <p>What bothered me was that the event is hard coded into the method. So that means for each new event, there is a new method to write.</p> <p>I fiddled around and managed to come up with this generic solution:</p> <pre><code>private static void SetAnyGenericHandler&lt;S, T&gt;( Action&lt;EventHandler&lt;T&gt;&gt; add, //to add event listener to publisher Action&lt;EventHandler&lt;T&gt;&gt; remove, //to remove event listener from publisher S subscriber, //ref to subscriber (to pass to action) Action&lt;S, T&gt; action) //called when event is raised where T : EventArgs where S : class { var subscriber_weak_ref = new WeakReference(subscriber); EventHandler&lt;T&gt; handler = null; handler = delegate(object sender, T e) { var subscriber_strong_ref = subscriber_weak_ref.Target as S; if(subscriber_strong_ref != null) { Console.WriteLine("New event received by subscriber"); action(subscriber_strong_ref, e); } else { remove(handler); handler = null; } }; add(handler); } </code></pre> <p>However the problem with that solution is that it is ONLY generic, it can't handle the standard winforms MouseUp, MouseDown, etc...</p> <p>So I tried to make it even <strong>more</strong> generic:</p> <pre><code>private static void SetAnyHandler&lt;T, R&gt;( Action&lt;T&gt; add, //to add event listener to publisher Action&lt;T&gt; remove, //to remove event listener from publisher Subscriber subscriber, //ref to subscriber (to pass to action) Action&lt;Subscriber, R&gt; action) where T : class { var subscriber_weak_ref = new WeakReference(subscriber); T handler = null; handler = delegate(object sender, R e) //&lt;-compiler doesn't like this line { var subscriber_strong_ref = subscriber_weak_ref.Target as Subscriber; if(subscriber_strong_ref != null) { action(subscriber_strong_ref, e); } else { remove(handler); handler = null; } }; remove(handler); } </code></pre> <p>However, as I hinted <a href="https://stackoverflow.com/questions/371109/garbage-collection-when-using-anonymous-delegates-for-event-handling/1687255#1687255">here</a>, this won't compile, because there is no way of constraining T to be a delegate.</p> <p>At that point, I pretty much gave up. There's no point trying to fight with the C# specs.</p> <p>However, yesterday, I discovered the Observable.FromEvent method from the Reactive framework, I didn't have the implementation, but the usage seemed slightly familiar, and very interesting:</p> <pre><code>var mousedown = Observable.FromEvent&lt;MouseEventHandler, MouseDownEventArgs&gt;( h =&gt; new MouseEventHandler(h), h =&gt; control.MouseDown += h, h =&gt; control.MouseDown -= h); </code></pre> <p>It was the first argument that caught my attention. This is the workaround for the absence of a delegate type constraint. We take of it by passing in the function which will create the delegate.</p> <p>Putting all this together gives us the solution shown at the top of this answer.</p> <h2>Afterthought</h2> <p>I thoroughly recommended taking the time to learn about the reactive framework (or whatever it ends up being called). It is VERY interesting, and slightly mindblowing. I suspect that it will also render questions like this totally redundant.</p> <p>So far, the most interesting stuff I've seen has been the videos on <a href="http://channel9.msdn.com/Search/?Term=reactive" rel="nofollow noreferrer">Channel9</a>.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload