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();
}
}
}
Related
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();
I have a class that inherits from ObservableCollection<T>. In that class I have a method that changes the collection internally and for which I'd like to suppress CollectionChanged events.
public class ContentBlockList : ObservableCollection<int> {
public void SomeMethod() {
var handlers = CollectionChanged.GetInvocationList();
foreach (NotifyCollectionChangedEventHandler handler in handlers) {
CollectionChanged -= handler;
}
// do stuff here
foreach (NotifyCollectionChangedEventHandler handler in handlers) {
CollectionChanged += handler;
}
}
}
Intuitively it seems like this should work since I'm accessing the event from within its containing object. Unfortunately, the compiler says
The event 'ObservableCollection.CollectionChanged' can only
appear on the left hand side of += or -=
I can get the code to work if I override both CollectionChanged and OnCollectionChanged(), essentially replacing the .NET versions with copies of my own. However, having to do something like that makes me suspicious that I'm ignoring some reason why doing this is a bad idea in the first place. Thanks for any thoughts on this.
As unsubscribing and re-subscribing to an event is a relatively (not really painful but I don't know how many subscribers there is likely to be) slow process I would recommend that you look into overriding both the the OnCollectionChanged and OnPropertyChanged methods for the base ObservableCollection.
So have something that resembles:
public class ContentBlockList : ObservableCollection<int>
{
private bool internallyUpdating;
public void SomeMethod()
{
this.internallyUpdating = true;
// Do Stuff (Add to base collection)
this.internallyUpdating = false;
this.OnPropertyChanged(new PropertyChangedEventArgs(#"Count");
this.OnPropertyChanged(new PropertyChangedEventArgs(#"Item[]");
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if(this.internallyUpdating)
{
return;
}
base.OnCollectionChanged(e);
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if(this.internallyUpdating)
{
return;
}
base.OnPropertyChanged(e);
}
}
What this allows for is the ability to suppress the events being raised while you update internally but doing this in a way that means you do not have to unsubscribe and resubscribe to events.
When adding to this collection normally (i.e. with contentBlockList.Add(1)) you'll fall straight through to calling the base event. But when you are trying to update internally you'll suppress these events until you have finished.
I'd say this is both more efficient in terms of performance but also much neater code than what you were looking at.
On a last note I'd also say that the NotifyCollectionChangedEventAction that you provide is Reset. You've probably done quite a big change to the collection and to handle it you'll want any subscriber to have to refresh their look on the collection, be it a control in a WPF view or even another class that uses the collection.
better use this :
public class ContentBlockList : ObservableCollection<int>
{
ContentBlockList()
{
this.CollectionChanged += ContentBlockList_CollectionChanged;
}
void ContentBlockList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
}
}
if you maintain your code try this
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ContentBlockList pp = new ContentBlockList();
pp.CollectionChanged += pp_CollectionChanged;
pp.CollectionChanged += pp_CollectionChanged1;
pp.Add(11112);
pp.SomeMethod();
}
void pp_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
}
void pp_CollectionChanged1(object sender, NotifyCollectionChangedEventArgs e)
{
}
}
public class ContentBlockList : ObservableCollection<int>
{
public void SomeMethod()
{
var handlers = CollectionChanged.GetInvocationList();
foreach (NotifyCollectionChangedEventHandler handler in handlers)
{
CollectionChanged -= handler;
}
// do stuff here
foreach (NotifyCollectionChangedEventHandler handler in handlers)
{
CollectionChanged += handler;
}
}
public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged;
}
As far as I understood you need to interrupt firing of CollectionChanged to do some work silently. So you can create boolean field like __FireCollectionChanged and then override OnCollectionChanged() to do:
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (__FireCollectionChanged)
base.OnCollectionChanged(e);
}
Then you can control whether event is fired by that boolean field.
And answering the actual question: you can't use the invocation list directly, because the event is not the delegate type field. It's basically just two methods add and remove for subscribe/unsubscribe behavior. Underlying delegate field is created behind the scenes and you typically don't want to use it.
Suppose I want to publish an event from the MarketDataProvider class. The problem is that the event logic resides 2 level deeper.MarketDataProvider --> Level1SocketClient --> Level1MessageHandler.
In short, I would like to raise the event from Level1MessageHandler that MarketDataProvider can publish. The performance here is critical because there are a lot of events generated. I would like to find a clean way of doing it without chaining events from each level.
Rather than having an Event itself, could the intermediate class just pass the Add and Remove calls on to the lowest level class? I.e.
public class Level1MessageHandler
{
public event EventHandler<MessageEventArgs> MessageReceived;
}
public class Level1SocketClient
{
Level1MessageHandler level1Handler;
public event EventHandler<MessageEventArgs> MessageReceived
{
add
{
level1Handler.MessageReceived += value;
}
remove
{
level1Handler.MessageReceived -= value;
}
}
}
This would at least cut out one level of delegate call.
(Or did I get the direction of invocation reversed? Anyway I think the idea is clear.)
Update
An interesting question arises: what happens if the intermediate listener needs to be disposable, and when disposed, remove all the events added through it? You can do it by recording the events added in a local event, like so:
public interface IMessagePublisher<TEventArgs> where TEventArgs : EventArgs
{
event EventHandler<TEventArgs> MessageReceived;
}
public class MessageRePublisher<TEventArgs> : IMessagePublisher<TEventArgs>, IDisposable where TEventArgs : EventArgs
{
readonly IMessagePublisher<TEventArgs> publisher;
public MessageRePublisher(IMessagePublisher<TEventArgs> publisher)
{
this.publisher = publisher;
}
EventHandler<TEventArgs> messageReceivedEventsAdded = null;
public event EventHandler<TEventArgs> MessageReceived
{
[MethodImpl(MethodImplOptions.Synchronized)]
add
{
// events are multicast delegates, which are immutable. We need to remove the previous
// combined event, create a new combined event, then added that.
// More here: http://msdn.microsoft.com/en-us/magazine/cc163533.aspx
if (messageReceivedEventsAdded != null)
publisher.MessageReceived -= messageReceivedEventsAdded;
messageReceivedEventsAdded += value;
if (messageReceivedEventsAdded != null)
publisher.MessageReceived += messageReceivedEventsAdded;
}
[MethodImpl(MethodImplOptions.Synchronized)]
remove
{
if (messageReceivedEventsAdded != null)
publisher.MessageReceived -= messageReceivedEventsAdded;
messageReceivedEventsAdded -= value;
if (messageReceivedEventsAdded != null)
publisher.MessageReceived += messageReceivedEventsAdded;
}
}
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (messageReceivedEventsAdded != null && publisher != null)
{
publisher.MessageReceived -= messageReceivedEventsAdded;
}
}
messageReceivedEventsAdded = null;
}
#endregion
}
The intermediate listener combines all its events into a single concatenated event, then adds and removes that every time.
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.