Created
May 17, 2010 22:21
-
-
Save jmangelo/404312 to your computer and use it in GitHub Desktop.
Helper Classes for creation of Weak Event Handlers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Copyright 2010, João Angelo | |
// | |
// This file is part of Event Management Helpers. | |
// | |
// Event Management Helpers is free software: you can redistribute it and/or modify | |
// it under the terms of the GNU Lesser General Public License as published | |
// by the Free Software Foundation, either version 3 of the License, | |
// or (at your option) any later version. | |
// | |
// Event Management Helpers is distributed in the hope that it will be useful, | |
// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
// GNU Lesser General Public License for more details. | |
// | |
// You should have received a copy of the GNU Lesser General Public License | |
// along with Event Management Helpers. If not, see <http://www.gnu.org/licenses/>. | |
using System; | |
using System.Reflection; | |
namespace Helpers.Events | |
{ | |
/// <summary> | |
/// Provides methods that allows a listener to subscribe to an event without | |
/// becoming referenced by the event source. | |
/// </summary> | |
public static class WeakEventHandlerWrapper | |
{ | |
/// <summary> | |
/// Wraps the specified event handler in a delegate based call. | |
/// </summary> | |
/// <param name="handler">The handler.</param> | |
/// <exception cref="ArgumentNullException"> | |
/// The parameter <paramref name="handler"/> is a null reference. | |
/// </exception> | |
/// <remarks> | |
/// If the provided <paramref name="handler"/> is a static method no wrapping is performed. | |
/// </remarks> | |
/// <returns>A wrapped event handler.</returns> | |
public static EventHandler WrapInDelegateCall(EventHandler handler) | |
{ | |
if (handler == null) | |
throw new ArgumentNullException("handler"); | |
if (handler.Target == null) | |
return handler; | |
var genericType = typeof(DelegateBasedWeakEventHandler<,>); | |
var concreteType = genericType.MakeGenericType(typeof(EventArgs), handler.Target.GetType()); | |
return (WeakEventHandler<EventArgs>)Activator.CreateInstance(concreteType, handler); | |
} | |
/// <summary> | |
/// Wraps the specified event handler in a delegate based call. | |
/// </summary> | |
/// <typeparam name="TListener"> | |
/// The type of the listener that defines the <paramref name="handler"/>. | |
/// </typeparam> | |
/// <param name="handler">The handler.</param> | |
/// <exception cref="ArgumentNullException"> | |
/// The parameter <paramref name="handler"/> is a null reference. | |
/// </exception> | |
/// <exception cref="ArgumentException"> | |
/// The provided <paramref name="handler"/> is defined by a type different | |
/// than the one specified by <typeparamref name="TListener"/>. | |
/// </exception> | |
/// <remarks> | |
/// If the provided <paramref name="handler"/> is a static method no wrapping is performed. | |
/// </remarks> | |
/// <returns>A wrapped event handler.</returns> | |
public static EventHandler WrapInDelegateCall<TListener>(EventHandler handler) | |
{ | |
if (handler == null) | |
throw new ArgumentNullException("handler"); | |
if (handler.Target == null) | |
return handler; | |
if (handler.Target.GetType() != typeof(TListener)) | |
throw new ArgumentException( | |
"The method is defined in a type different than TListener.", | |
"handler"); | |
return new DelegateBasedWeakEventHandler<EventArgs, TListener>(handler); | |
} | |
/// <summary> | |
/// Wraps the specified event handler in a delegate based call. | |
/// </summary> | |
/// <typeparam name="TArgs">The type of the event data generated by the event.</typeparam> | |
/// <param name="handler">The handler.</param> | |
/// <exception cref="ArgumentNullException"> | |
/// The parameter <paramref name="handler"/> is a null reference. | |
/// </exception> | |
/// <remarks> | |
/// If the provided <paramref name="handler"/> is a static method no wrapping is performed. | |
/// </remarks> | |
/// <returns>A wrapped event handler.</returns> | |
public static EventHandler<TArgs> WrapInDelegateCall<TArgs>(EventHandler<TArgs> handler) | |
where TArgs : EventArgs | |
{ | |
if (handler == null) | |
throw new ArgumentNullException("handler"); | |
if (handler.Target == null) | |
return handler; | |
var genericType = typeof(DelegateBasedWeakEventHandler<,>); | |
var concreteType = genericType.MakeGenericType(typeof(TArgs), handler.Target.GetType()); | |
return (WeakEventHandler<TArgs>)Activator.CreateInstance(concreteType, handler); | |
} | |
/// <summary> | |
/// Wraps the specified event handler in a delegate based call. | |
/// </summary> | |
/// <typeparam name="TArgs">The type of the event data generated by the event.</typeparam> | |
/// <typeparam name="TListener"> | |
/// The type of the listener that defines the <paramref name="handler"/>. | |
/// </typeparam> | |
/// <param name="handler">The handler.</param> | |
/// <exception cref="ArgumentNullException"> | |
/// The parameter <paramref name="handler"/> is a null reference. | |
/// </exception> | |
/// <exception cref="ArgumentException"> | |
/// The provided <paramref name="handler"/> is defined by a type different | |
/// than the one specified by <typeparamref name="TListener"/>. | |
/// </exception> | |
/// <remarks> | |
/// If the provided <paramref name="handler"/> is a static method no wrapping is performed. | |
/// </remarks> | |
/// <returns>A wrapped event handler.</returns> | |
public static EventHandler<TArgs> WrapInDelegateCall<TArgs, TListener>(EventHandler<TArgs> handler) | |
where TArgs : EventArgs | |
{ | |
if (handler == null) | |
throw new ArgumentNullException("handler"); | |
if (handler.Target == null) | |
return handler; | |
if (handler.Target.GetType() != typeof(TListener)) | |
throw new ArgumentException( | |
"The method is defined in a type different than TListener.", | |
"handler"); | |
return new DelegateBasedWeakEventHandler<TArgs, TListener>(handler); | |
} | |
/// <summary> | |
/// Wraps the specified event handler in a reflection based call. | |
/// </summary> | |
/// <typeparam name="TArgs">The type of the event data generated by the event.</typeparam> | |
/// <param name="handler">The handler.</param> | |
/// <exception cref="ArgumentNullException"> | |
/// The parameter <paramref name="handler"/> is a null reference. | |
/// </exception> | |
/// <remarks> | |
/// If the provided <paramref name="handler"/> is a static method no wrapping is performed. | |
/// </remarks> | |
/// <returns>A wrapped event handler.</returns> | |
public static EventHandler<TArgs> WrapInReflectionCall<TArgs>(EventHandler<TArgs> handler) | |
where TArgs : EventArgs | |
{ | |
if (handler == null) | |
throw new ArgumentNullException("handler"); | |
if (handler.Target == null) | |
return handler; | |
return new ReflectionBasedWeakEventHandler<TArgs>(handler); | |
} | |
/// <summary> | |
/// Wraps the specified event handler in a reflection based call. | |
/// </summary> | |
/// <param name="handler">The handler.</param> | |
/// <exception cref="ArgumentNullException"> | |
/// The parameter <paramref name="handler"/> is a null reference. | |
/// </exception> | |
/// <remarks> | |
/// If the provided <paramref name="handler"/> is a static method no wrapping is performed. | |
/// </remarks> | |
/// <returns>A wrapped event handler.</returns> | |
public static EventHandler WrapInReflectionCall(EventHandler handler) | |
{ | |
if (handler == null) | |
throw new ArgumentNullException("handler"); | |
if (handler.Target == null) | |
return handler; | |
return new ReflectionBasedWeakEventHandler<EventArgs>(handler); | |
} | |
} | |
internal abstract class WeakEventHandler<T> where T : EventArgs | |
{ | |
public WeakEventHandler(Delegate handler) | |
{ | |
this.WeakRef = new WeakReference(handler.Target, false); | |
this.Method = handler.Method; | |
} | |
protected WeakReference WeakRef { get; set; } | |
protected MethodInfo Method { get; set; } | |
public abstract void Invoke(object sender, EventArgs e); | |
public static implicit operator EventHandler(WeakEventHandler<T> weakHandler) | |
{ | |
return weakHandler.Invoke; | |
} | |
public static implicit operator EventHandler<T>(WeakEventHandler<T> weakHandler) | |
{ | |
return weakHandler.Invoke; | |
} | |
} | |
internal sealed class ReflectionBasedWeakEventHandler<T> : WeakEventHandler<T> | |
where T : EventArgs | |
{ | |
public ReflectionBasedWeakEventHandler(Delegate handler) | |
: base(handler) { } | |
public override void Invoke(object sender, EventArgs e) | |
{ | |
object target = this.WeakRef.Target; | |
if (target != null) | |
{ | |
this.Method.Invoke(target, new object[] { sender, e }); | |
} | |
} | |
} | |
internal delegate void OpenInstanceEventHandler<TTarget, TArgs>( | |
TTarget target, | |
object sender, | |
TArgs e) where TArgs : EventArgs; | |
internal sealed class DelegateBasedWeakEventHandler<TArgs, TListener> : WeakEventHandler<TArgs> | |
where TArgs : EventArgs | |
{ | |
public DelegateBasedWeakEventHandler(Delegate handler) | |
: base(handler) | |
{ | |
var weakHandler = Delegate.CreateDelegate( | |
typeof(OpenInstanceEventHandler<TListener, TArgs>), | |
null, | |
handler.Method, | |
true); | |
this.Handler = (OpenInstanceEventHandler<TListener, TArgs>)weakHandler; | |
} | |
private OpenInstanceEventHandler<TListener, TArgs> Handler { get; set; } | |
public override void Invoke(object sender, EventArgs e) | |
{ | |
object target = this.WeakRef.Target; | |
if (target != null) | |
{ | |
this.Handler((TListener)target, sender, (TArgs)e); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using NUnit.Framework; | |
namespace Helpers.Events.UnitTests | |
{ | |
using Wrapper = WeakEventHandlerWrapper; | |
[TestFixture] | |
public class WeakEventHandlerWrapperTest | |
{ | |
[Test] | |
[Description("An exception SHOULD be thrown if the provided handler is a null reference.")] | |
public void WrapInReflectionCall_T001() | |
{ | |
Assert.Throws<ArgumentNullException>(() => Wrapper.WrapInReflectionCall(null)); | |
} | |
[Test] | |
[Description("SHOULD return exact same handler if provided with a static method handler.")] | |
public void WrapInReflectionCall_T002() | |
{ | |
EventHandler expected = EventListener.HandleStaticRaised; | |
var actual = Wrapper.WrapInReflectionCall(expected); | |
Assert.AreEqual(expected, actual); | |
} | |
[Test] | |
[Description("Wrapped handler SHOULD be invoked if listener instance is not disposed.")] | |
public void WrapInReflectionCall_T003() | |
{ | |
var test = new EventTestContext(); | |
test.Raiser.Raised += Wrapper.WrapInReflectionCall(test.Listener.HandleRaised); | |
test.Raiser.Raise(); | |
Assert.IsTrue(test.Result.RaisedCalled); | |
} | |
[Test] | |
[Description("Wrapped handler SHOULD NOT be invoked if listener instance is disposed.")] | |
public void WrapInReflectionCall_T004() | |
{ | |
var test = new EventTestContext(); | |
test.Raiser.Raised += Wrapper.WrapInReflectionCall(test.Listener.HandleRaised); | |
test.Listener = null; | |
GC.Collect(); | |
GC.WaitForPendingFinalizers(); | |
test.Raiser.Raise(); | |
Assert.IsFalse(test.Result.RaisedCalled); | |
} | |
[Test] | |
[Description("An exception SHOULD be thrown if the provided handler is a null reference.")] | |
public void WrapInReflectionCall_TArgs_T001() | |
{ | |
EventHandler<RaisedIdEventArgs> handler = null; | |
Assert.Throws<ArgumentNullException>(() => Wrapper.WrapInReflectionCall(handler)); | |
} | |
[Test] | |
[Description("SHOULD return exact same handler if provided with a static method handler.")] | |
public void WrapInReflectionCall_TArgs_T002() | |
{ | |
EventHandler<RaisedIdEventArgs> expected = EventListener.HandleStaticRaisedId; | |
var actual = Wrapper.WrapInReflectionCall(expected); | |
Assert.AreEqual(expected, actual); | |
} | |
[Test] | |
[Description("Wrapped handler SHOULD be invoked if listener instance is not disposed.")] | |
public void WrapInReflectionCall_TArgs_T003() | |
{ | |
var test = new EventTestContext(); | |
test.Raiser.RaisedId += Wrapper.WrapInReflectionCall<RaisedIdEventArgs>( | |
test.Listener.HandleRaisedId); | |
test.Raiser.RaiseId(1); | |
Assert.IsTrue(test.Result.RaisedIdCalled); | |
} | |
[Test] | |
[Description("Wrapped handler SHOULD NOT be invoked if listener instance is disposed.")] | |
public void WrapInReflectionCall_TArgs_T004() | |
{ | |
var test = new EventTestContext(); | |
test.Raiser.RaisedId += Wrapper.WrapInReflectionCall<RaisedIdEventArgs>( | |
test.Listener.HandleRaisedId); | |
test.Listener = null; | |
GC.Collect(); | |
GC.WaitForPendingFinalizers(); | |
test.Raiser.RaiseId(1); | |
Assert.IsFalse(test.Result.RaisedIdCalled); | |
} | |
[Test] | |
[Description("SHOULD NOT affect event data passed to the handler.")] | |
public void WrapInReflectionCall_TArgs_T005() | |
{ | |
var test = new EventTestContext(); | |
const int Id = 1; | |
EventHandler<RaisedIdEventArgs> handler = (sender, e) => Assert.AreEqual(Id, e.Id); | |
test.Raiser.RaisedId += Wrapper.WrapInReflectionCall(handler); | |
test.Raiser.RaiseId(Id); | |
} | |
[Test] | |
[Description("An exception SHOULD be thrown if the provided handler is a null reference.")] | |
public void WrapInDelegateCall_T001() | |
{ | |
Assert.Throws<ArgumentNullException>(() => Wrapper.WrapInDelegateCall(null)); | |
} | |
[Test] | |
[Description("SHOULD return exact same handler if provided with a static method handler.")] | |
public void WrapInDelegateCall_T002() | |
{ | |
EventHandler expected = EventListener.HandleStaticRaised; | |
var actual = Wrapper.WrapInDelegateCall(expected); | |
Assert.AreEqual(expected, actual); | |
} | |
[Test] | |
[Description("Wrapped handler SHOULD be invoked if listener instance is not disposed.")] | |
public void WrapInDelegateCall_T003() | |
{ | |
var test = new EventTestContext(); | |
test.Raiser.Raised += Wrapper.WrapInDelegateCall(test.Listener.HandleRaised); | |
test.Raiser.Raise(); | |
Assert.IsTrue(test.Result.RaisedCalled); | |
} | |
[Test] | |
[Description("Wrapped handler SHOULD NOT be invoked if listener instance is disposed.")] | |
public void WrapInDelegateCall_T004() | |
{ | |
var test = new EventTestContext(); | |
test.Raiser.Raised += Wrapper.WrapInDelegateCall(test.Listener.HandleRaised); | |
test.Listener = null; | |
GC.Collect(); | |
GC.WaitForPendingFinalizers(); | |
test.Raiser.Raise(); | |
Assert.IsFalse(test.Result.RaisedCalled); | |
} | |
[Test] | |
[Description("An exception SHOULD be thrown if the provided handler is a null reference.")] | |
public void WrapInDelegateCall_TListener_T001() | |
{ | |
EventHandler handler = null; | |
Assert.Throws<ArgumentNullException>(() => Wrapper.WrapInDelegateCall<EventListener>(handler)); | |
} | |
[Test] | |
[Description("SHOULD return exact same handler if provided with a static method handler.")] | |
public void WrapInDelegateCall_TListener_T002() | |
{ | |
EventHandler expected = EventListener.HandleStaticRaised; | |
var actual = Wrapper.WrapInDelegateCall<EventListener>(expected); | |
Assert.AreEqual(expected, actual); | |
} | |
[Test] | |
[Description("Wrapped handler SHOULD be invoked if listener instance is not disposed.")] | |
public void WrapInDelegateCall_TListener_T003() | |
{ | |
var test = new EventTestContext(); | |
test.Raiser.Raised += Wrapper.WrapInDelegateCall<EventListener>(test.Listener.HandleRaised); | |
test.Raiser.Raise(); | |
Assert.IsTrue(test.Result.RaisedCalled); | |
} | |
[Test] | |
[Description("Wrapped handler SHOULD NOT be invoked if listener instance is disposed.")] | |
public void WrapInDelegateCall_TListener_T004() | |
{ | |
var test = new EventTestContext(); | |
test.Raiser.Raised += Wrapper.WrapInDelegateCall<EventListener>( | |
test.Listener.HandleRaised); | |
test.Listener = null; | |
GC.Collect(); | |
GC.WaitForPendingFinalizers(); | |
Assert.IsFalse(test.Result.RaisedCalled); | |
} | |
[Test] | |
[Description("An exception SHOULD be thrown on type mismatch between compile time listener type and runtime type of handler target.")] | |
public void WrapInDelegateCall_TListener_T005() | |
{ | |
var test = new EventTestContext(); | |
Assert.Throws<ArgumentException>( | |
() => Wrapper.WrapInDelegateCall<EventRaiser>(test.Listener.HandleRaised)); | |
} | |
[Test] | |
[Description("An exception SHOULD be thrown if the provided handler is a null reference.")] | |
public void WrapInDelegateCall_TArgs_T001() | |
{ | |
EventHandler<RaisedIdEventArgs> handler = null; | |
Assert.Throws<ArgumentNullException>(() => Wrapper.WrapInDelegateCall(handler)); | |
} | |
[Test] | |
[Description("SHOULD return exact same handler if provided with a static method handler.")] | |
public void WrapInDelegateCall_TArgs_T002() | |
{ | |
EventHandler<RaisedIdEventArgs> expected = EventListener.HandleStaticRaisedId; | |
var actual = Wrapper.WrapInDelegateCall(expected); | |
Assert.AreEqual(expected, actual); | |
} | |
[Test] | |
[Description("Wrapped handler SHOULD be invoked if listener instance is not disposed.")] | |
public void WrapInDelegateCall_TArgs_T003() | |
{ | |
var test = new EventTestContext(); | |
test.Raiser.RaisedId += Wrapper.WrapInDelegateCall<RaisedIdEventArgs>( | |
test.Listener.HandleRaisedId); | |
test.Raiser.RaiseId(1); | |
Assert.IsTrue(test.Result.RaisedIdCalled); | |
} | |
[Test] | |
[Description("Wrapped handler SHOULD NOT be invoked if listener instance is disposed.")] | |
public void WrapInDelegateCall_TArgs_T004() | |
{ | |
var test = new EventTestContext(); | |
test.Raiser.RaisedId += Wrapper.WrapInDelegateCall<RaisedIdEventArgs>( | |
test.Listener.HandleRaisedId); | |
test.Listener = null; | |
GC.Collect(); | |
GC.WaitForPendingFinalizers(); | |
test.Raiser.RaiseId(1); | |
Assert.IsFalse(test.Result.RaisedIdCalled); | |
} | |
[Test] | |
[Description("SHOULD NOT affect event data passed to the handler.")] | |
public void WrapInDelegateCall_TArgs_T005() | |
{ | |
var test = new EventTestContext(); | |
const int Id = 1; | |
EventHandler<RaisedIdEventArgs> handler = (sender, e) => Assert.AreEqual(Id, e.Id); | |
test.Raiser.RaisedId += Wrapper.WrapInDelegateCall(handler); | |
test.Raiser.RaiseId(Id); | |
} | |
[Test] | |
[Description("An exception SHOULD be thrown if the provided handler is a null reference.")] | |
public void WrapInDelegateCall_TArgs_TListener_T001() | |
{ | |
EventHandler<RaisedIdEventArgs> handler = null; | |
Assert.Throws<ArgumentNullException>( | |
() => Wrapper.WrapInDelegateCall<RaisedIdEventArgs, EventListener>(handler)); | |
} | |
[Test] | |
[Description("SHOULD return exact same handler if provided with a static method handler.")] | |
public void WrapInDelegateCall_TArgs_TListener_T002() | |
{ | |
EventHandler<RaisedIdEventArgs> expected = EventListener.HandleStaticRaisedId; | |
var actual = Wrapper.WrapInDelegateCall<RaisedIdEventArgs, EventListener>(expected); | |
Assert.AreEqual(expected, actual); | |
} | |
[Test] | |
[Description("Wrapped handler SHOULD be invoked if listener instance is not disposed.")] | |
public void WrapInDelegateCall_TArgs_TListener_T003() | |
{ | |
var test = new EventTestContext(); | |
test.Raiser.RaisedId += Wrapper.WrapInDelegateCall<RaisedIdEventArgs, EventListener>( | |
test.Listener.HandleRaisedId); | |
test.Raiser.RaiseId(1); | |
Assert.IsTrue(test.Result.RaisedIdCalled); | |
} | |
[Test] | |
[Description("Wrapped handler SHOULD NOT be invoked if listener instance is disposed.")] | |
public void WrapInDelegateCall_TArgs_TListener_T004() | |
{ | |
var test = new EventTestContext(); | |
test.Raiser.RaisedId += Wrapper.WrapInDelegateCall<RaisedIdEventArgs, EventListener>( | |
test.Listener.HandleRaisedId); | |
test.Listener = null; | |
GC.Collect(); | |
GC.WaitForPendingFinalizers(); | |
test.Raiser.RaiseId(1); | |
Assert.IsFalse(test.Result.RaisedIdCalled); | |
} | |
[Test] | |
[Description("An exception SHOULD be thrown on type mismatch between compile time listener type and runtime type of handler target.")] | |
public void WrapInDelegateCall_TArgs_TListener_T005() | |
{ | |
var test = new EventTestContext(); | |
Assert.Throws<ArgumentException>( | |
() => Wrapper.WrapInDelegateCall<RaisedIdEventArgs, EventRaiser>( | |
test.Listener.HandleRaisedId)); | |
} | |
[Test] | |
[Description("SHOULD NOT affect event data passed to the handler.")] | |
public void WrapInDelegateCall_TArgs_TListener_T006() | |
{ | |
var test = new EventTestContext(); | |
const int Id = 1; | |
EventHandler<RaisedIdEventArgs> handler = (sender, e) => Assert.AreEqual(Id, e.Id); | |
test.Raiser.RaisedId += Wrapper.WrapInDelegateCall<RaisedIdEventArgs, EventListener>(handler); | |
test.Raiser.RaiseId(Id); | |
} | |
} | |
internal class EventTestContext | |
{ | |
public EventTestContext() | |
{ | |
this.Raiser = new EventRaiser(); | |
this.Result = new EventListenerResult(); | |
this.Listener = new EventListener(this.Result); | |
} | |
public EventRaiser Raiser { get; set; } | |
public EventListener Listener { get; set; } | |
public EventListenerResult Result { get; set; } | |
} | |
internal class EventListenerResult | |
{ | |
public bool RaisedCalled { get; set; } | |
public bool RaisedIdCalled { get; set; } | |
} | |
internal class EventListener | |
{ | |
public EventListener(EventListenerResult result) | |
{ | |
this.Result = result; | |
} | |
public EventListenerResult Result { get; set; } | |
public void HandleRaised(object sender, EventArgs e) | |
{ | |
this.Result.RaisedCalled = true; | |
} | |
public void HandleRaisedId(object sender, RaisedIdEventArgs e) | |
{ | |
this.Result.RaisedIdCalled = true; | |
} | |
public static void HandleStaticRaised(object sender, EventArgs e) { } | |
public static void HandleStaticRaisedId(object sender, RaisedIdEventArgs e) { } | |
} | |
internal class RaisedIdEventArgs : EventArgs | |
{ | |
public int Id { get; set; } | |
} | |
internal class EventRaiser | |
{ | |
public event EventHandler Raised; | |
public event EventHandler<RaisedIdEventArgs> RaisedId; | |
public void Raise() { this.OnRaised(EventArgs.Empty); } | |
public void RaiseId(int id) { this.OnRaisedId(new RaisedIdEventArgs { Id = id }); } | |
protected virtual void OnRaised(EventArgs e) | |
{ | |
var handler = this.Raised; | |
if (handler != null) | |
handler(this, e); | |
} | |
protected virtual void OnRaisedId(RaisedIdEventArgs e) | |
{ | |
var handler = this.RaisedId; | |
if (handler != null) | |
handler(this, e); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment