I got this situation where I need to create a wrapper around a WPF window that exposes basic features, such as exposing Loaded and Closed events. (There are other wrapper implementations for other UI platforms)
// This works.
public event EventHandler? Closed
{
add => Ref.Closed += value;
remove => Ref.Closed -= value;
}
// This doesn't work.
public event EventHandler? Loaded
{
add => Ref.Loaded += value;
remove => Ref.Loaded -= value;
}
The problem here is that Loaded is a RoutedEventHandled (whereas Closing isn't). Settings an EventHandler doesn't work.
How can I solve this?
Edit: the only solution I can think of is to create a Dictionary of eventhandler wrappers when I add, so that I can get the same reference in remove. Any prettier solution?
Subscribe to the Loaded event of Ref and raise your own custom event when it's raised:
public event EventHandler Loaded;
...
Ref.Loaded += (ss, ee) => Loaded?.Invoke(this, EventArgs.Empty);
I ended up doing this.
public event EventHandler? Loaded
{
add
{
if (value != null)
{
var handler = new RoutedEventHandler((s, e) => value.Invoke(s, e));
_loadedHandlers.Add(value, handler);
Ref.Loaded += handler;
}
}
remove
{
if (value != null)
{
Ref.Loaded += _loadedHandlers[value];
_loadedHandlers.Remove(value);
}
}
}
private Dictionary<EventHandler, RoutedEventHandler> _loadedHandlers = new();
Related
My class implements interface, And I expose event to the outside world.
I have a polling timer that send data using that event to subscriber using a custom eventArgs.
I want to start the polling timer Only when someone has subscribed to the event, and stop the timer when everyone un-subscribed from it.
How can I detect when someone subscribes / un-subscribes from my event ?
This way i can automatically start/stop the polling timer if no one listen.
You can add your own add/remove methods; for example:
private EventHandler someEvent;
public event EventHandler SomeEvent {
add {
someEvent += value;
if(someEvent != null) EnsureTimerRunning();
}
remove {
someEvent -= value;
if(someEvent == null) StopTimerIfRunning();
}
}
Note that field-like events (i.e. public event EventHandler SomeEvent;) include compiler-generated thread-safety around add/remove - you'll need to decide whether to duplicate that, and if so: how (lock, Interlocked, or just defer to a private backing field-like event and let the compiler worry about it). A very simplistic approach might be:
private EventHandler someEvent;
private readonly object syncLock = new object();
public event EventHandler SomeEvent {
add {
lock(syncLock) {
someEvent += value;
if(someEvent != null) EnsureTimerRunning();
}
}
remove {
lock(syncLock) {
someEvent -= value;
if(someEvent == null) StopTimerIfRunning();
}
}
}
I have this event in a webservice:
public event FindProductsByCharacteristicsCompletedEventHandler FindProductsByCharacteristicsCompleted
{
[MethodImpl(MethodImplOptions.Synchronized)]
add
{
_findProductsByCharacteristicsCompleted += value;
}
[MethodImpl(MethodImplOptions.Synchronized)]
remove
{
_findProductsByCharacteristicsCompleted -= value;
}
}
And im then checking if the event value is null with this later in the class:
private void OnFindProductsByCharacteristicsOperationCompleted(object arg)
{
var handler = _findProductsByCharacteristicsCompleted;
if (handler == null)
return;
handler(this, new FindProductsByCharacteristicsCompletedEventArgs(completedEventArgs.Results, completedEventArgs.Error, completedEventArgs.Cancelled, completedEventArgs.UserState));
}
Your event implementation looks like it is an endless recursion. You are using the property itself in its implementation.
Change it to this:
private FindProductsByCharacteristicsCompletedEventHandler
_findProductsByCharacteristicsCompleted;
public event FindProductsByCharacteristicsCompletedEventHandler
FindProductsByCharacteristicsCompleted
{
[MethodImpl(MethodImplOptions.Synchronized)]
add
{
_findProductsByCharacteristicsCompleted += value;
}
[MethodImpl(MethodImplOptions.Synchronized)]
remove
{
_findProductsByCharacteristicsCompleted -= value;
}
}
And now, implement your method like this:
var handler = _findProductsByCharacteristicsCompleted;
if(handler == null)
return;
handler(this, new FindProductsByCharacteristicsCompletedEventArgs(...));
This has the advantage that it is thread-safe.
Even if someone detached the last handler from the event after you checked for null but before you actually raised the event, you would not get an exception, because you are operating on the unchanged local variable.
I'm making a custom button (Winforms Control Library) and have the code below so that all mouseenter will be added to all controls in my button. When I run it, it causes a stack overflow exception. I have the same code with Click instead of MouseEnter and it works fine. Here is the code:
public new event EventHandler MouseEnter {
add
{
this.MouseEnter += value;
foreach (Control i in Controls)
{
i.MouseEnter += value;
}
}
remove
{
this.MouseEnter -= value;
foreach (Control i in Controls)
{
i.MouseEnter -= value;
}
}
}
here is the click code:
public new event EventHandler Click {
add {
this.Click += value;
foreach (Control i in Controls) {
i.Click += value;
}
}
remove {
this.Click -= value;
foreach (Control i in Controls) {
i.Click -= value;
}
}
}
+= is shorthand for "invoke the adder for this event." You are calling += from your adder. Thus you have unbound recursion, leading to the stack overflow.
Looking at your code, it appears that you are defining the adder yourself in order to add and remove the handler not only from the control, but from all its children as well. This strikes me as a pretty bad idea: subscribers to a given event have the reasonable expectation that they will only be notified when the actual event is fired, and not whenever the event is fired by any number of publishers about which they know nothing.
If you want to create helper methods that do this, that would probably make more sense, since now consumers invoking the methods know exactly what they're getting into. As well, that would get rid of your recursion bug to boot.
Finally, this functionality is probably unnecessary: many events will bubble up from children to parents anyway.
I think you wanted something like this (not sure if its right for the private member):
private EventHandler mouseEnter;
public new event EventHandler MouseEnter {
add
{
this.mouseEnter += value;
foreach (Control i in Controls)
{
i.mouseEnter += value;
}
}
remove
{
this.mouseEnter -= value;
foreach (Control i in Controls)
{
i.mouseEnter -= value;
}
}
}
You have this.MouseEnter calling itself in recursion.
I ended up replaceing this.Click with base.Click.
public new event EventHandler Click {
add {
base.Click += value;
foreach (Control i in Controls) {
i.Click += value;
}
}
remove {
base.Click -= value;
foreach (Control i in Controls) {
i.Click -= value;
}
}
}
I got this event handle and how can I do unit test for this
public class MyLearningEvent
{
private event EventHandler _Closed;
public event EventHandler Closed
{
add
{
_Closed -= value;
_Closed += value;
}
remove
{
_Closed -= value;
}
}
public void OnClosed()
{
if (_Closed != null) _Closed(this, EventArgs.Empty);
}
}
Just modified code so that much clear
Thanks
You should not unit test that code. It's a feature which is built into .NET. Your event handling is flawed imho.
add
{
_Closed -= value;
_Closed += value;
}
Probably means that your invokers don't keep track on if they have subscribed or not. That can lead to memory leaks: http://blog.naviso.fr/wordpress/wp-content/uploads/2011/11/MemoryLeaks-English.jpg
A more robust (and thread safe implementation) is:
public class MyLearningEvent
{
public event EventHandler Closed = delegate {};
public void TriggerClosed()
{
Closed(this, EventArgs.Empty);
}
}
But you should not let anyone else trigger that event (make the TriggerClosed private/protected)
Try this method. This assumes MyClass.Close() raises the MyClass.Closed event.
public void ClosedEventHandlerIsNotCalledAfterBeingRemoved()
{
MyLearningEvent Target = new MyLearningEvent();
EventHandler Target_Closed = new EventHandler((sender, e) => { Assert.Fail("Closed EventHandler was raised after being removed."); });
Target.Closed += Target_Closed;
Target.Closed -= Target_Closed;
Target.OnClosed();
}
I have a class which wraps another class and exposes several events from the class it's wrapping. (The instance it wraps can change)
I used the following code:
public event EventHandler AnEvent;
public OtherClass Inner {
get { /* ... */ }
set {
//...
if(value != null)
value.AnEvent += AnEvent;
//...
}
}
However, the events were raised inconsistently.
What's wrong with this code?
The problem is that Delegates are immutable.
If you add a handler to an event, it creates a new Delegate instance which contains the old handlers and the newly added handler. The old Delegate is not modified and is discarded.
When I write, value.AnEvent += AnEvent, it adds the Delegate containing the current handlers (if any) to the inner class's event. However, changes to the outer class's event are ignored because they don't change the Delegate instance that I added to the inner classes event. Similarly, if I remove a handler after setting the Inner property, the handler isn't removed from the inner class's event.
There are two correct ways to do this.
I can make my own handler that invokes the wrapper's event, like this:
public event EventHandler AnEvent;
public OtherClass Inner {
get { /* ... */ }
set {
if(Inner != null)
Inner.AnEvent -= Inner_AnEvent;
//...
if(value != null)
value.AnEvent += Inner_AnEvent;
//...
}
}
void Inner_AnEvent(object sender, EventArgs e) {
var handler = AnEvent;
if (handler != null) handler(sender, e);
}
The other way is to make a custom event in the wrapper that adds its handlers to the inner class's event, like this:
EventHandler anEventDelegates
public OtherClass Inner {
get { /* ... */ }
set {
//...
if(value != null)
value.AnEvent += anEventDelegates;
//...
}
}
public event EventHandler AnEvent {
add {
anEventDelegates += value;
if (Inner != null) Inner.AnEvent += value;
}
remove {
anEventDelegates -= value;
if(Inner != null) Inner -= value;
}
}
Note that this is not entirely thread-safe.
I solved this problem myself and am posting the question & answer for the benefit of people with similar problems.
The your answer - there are two problems here...
First: in both cases, you are raising the outer event with the wrong sender. Someone subscribing to an event on the outer class would expect those classes to be raised with a sender of that outer class.
This is particularly important in things like winform Controls, or binding-list implementations, where the sender is used to identify the object between many that share a handler.
This should instead be something like:
void Inner_AnEvent(object sender, EventArgs e) {
var handler = AnEvent;
if (handler != null) handler(this, e);
}
The second (much more minor) issue is that you are currently taking out an event on the inner class even if the outer class has no subscribers. You can fix this with a bit more custom handling...
private EventHandler anEvent;
public event EventHandler AnEvent {
add { // note: not synchronized
bool first = anEvent == null;
anEvent += value;
if(first && anEvent != null && inner != null) {
inner.SomeEvent += Inner_AnEvent;
}
}
remove { // note: not synchronized
bool hadValue = anEvent != null;
anEvent -= value;
if(hadValue && anEvent == null && inner != null) {
inner.SomeEvent -= Inner_AnEvent;
}
}
}
(and similar code in the Inner get/set to only subscribe if we have listeners...
if(value != null && anEvent != null)
value.AnEvent += Inner_AnEvent;
This might be a big saver if you have lots of instances of the outer-class, but rarely use the event.