How can I launch an event that has accessors like this :
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
If it were a normal event I would launch it by:
CanExecuteChanged(sender, EventArgs..).
But here it doesn't work - I can only do
CanExecuteChanged +=..
to attach a method do the event - but I can't Launch it.
Also some documentation on the subject would be appreciated.
Thanks.
EDIT
The event is from class implementing ICommand in WPF. there's nothing more to show :).
And no - the CommandManager.RequerySuggested(this, EventArgs.Empty); doesn't work.
EDIT2 Not sure what to say - Jon's example should have worked yet even if the add method is called correctly - when I try to call the event - it's null :|. I probably will
drop events with accessors.
I think you have events confused with delegates. Only the class exposing the event can raise it... Others can only subscribe-unsubscribe to it. If you are invoking the event from within the class declaring the event, it should work like a regular delegate.
The best page I could find on Events vs Delegates. Read up..
Can you post a bigger snippet.. something seems amiss..
Killer Update
I think I finally see your problem and how to solve it.
Short Answer: It does not know the name of delegate to invoke if you write your own accessors. If you don't.. the compiler adds the private delegate of known name and hence is able to invoke it
This code snippet shows what I mean. This MSDN article showed me the light. Great question dude.. I lost 30 mins. Upvoted :)
public class Hash1
{
private EventHandler myHomeMadeDelegate;
public event EventHandler FancyEvent
{
add
{
//myDelegate += value;
myHomeMadeDelegate = (EventHandler)Delegate.Combine(myHomeMadeDelegate, value);
}
remove
{
//myDelegate -= value;
myHomeMadeDelegate = (EventHandler)Delegate.Remove(myHomeMadeDelegate, value);
}
}
public event EventHandler PlainEvent;
public Hash1()
{
FancyEvent += new EventHandler(On_Hash1_FancyEvent);
PlainEvent += new EventHandler(On_Hash1_PlainEvent);
// FancyEvent(this, EventArgs.Empty); //won't work:What is the backing delegate called? I don't know
myHomeMadeDelegate(this, EventArgs.Empty); // Aha!
PlainEvent(this, EventArgs.Empty);
}
void On_Hash1_PlainEvent(object sender, EventArgs e)
{
Console.WriteLine("Bang Bang!");
}
void On_Hash1_FancyEvent(object sender, EventArgs e)
{
Console.WriteLine("Bang!");
}
}
That event is just subscribing to and unsubscribing from another event. If you want your subscribers (and only your subscribers - not separate ones to the other event) to be invoked, you'll need to keep hold of your subscribers separately. For instance, you could change the code to something like:
private EventHandler canExecuteChanged;
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
canExecuteChanged += value;
}
remove
{
CommandManager.RequerySuggested -= value;
canExecuteChanged -= value;
}
}
Ok, I found that if I want to trigger that event you have to do :
CommandManager.InvalidateRequerySuggested();.
You've got to invoke the underlying events directly. In your case, it looks as though this would be:
<blockquote>CommandManager.RequerySuggested(sender, EventArgs.…)</blockquote>
/EDIT: Ok, I didn't notice that CommandManager is a framework class. In this case, you obviously don't want to do what I've proposed. Jon's solution is to the point: You've got to keep track of your own event and invoke that (e.g. as a delegate). In keeping with Jon's example, invocation would look like this:
canExecuteChanged(sender, EventArgs.Empty);
wow, just had similar problems. The answer that helped me understand is somewhat like Gishu's.
Also from the C# specs, http://www.microsoft.com/en-us/download/details.aspx?id=7029, under "10.8.1 Field-like events" it says "When compiling a field-like event, the compiler automatically creates storage to hold the delegate,"
specs also says:
Thus, an instance event declaration of the form:
class X
{
public event D Ev;
}
could be compiled to something equivalent to:
class X
{
private D __Ev; // field to hold the delegate
public event D Ev {
add {
lock(this) { __Ev = __Ev + value; }
}
remove {
lock(this) { __Ev = __Ev - value; }
}
}
}
If you do something like the code below, the compiler compiles it successfully:
namespace ConsoleApplication1
{
class Program
{
public event EventHandler ss;
Program()
{
if (null != ss)
{
ss(this, EventArgs.Empty) ;
}
}
static void Main(string[] args)
{
new Program();
}
}
}
And if you add accessors to ss above, it will NOT compile:
namespace ConsoleApplication1
{
class Program
{
public event EventHandler ss
{
add { }
remove { }
}
Program()
{
if (null != ss)
{
ss(this, EventArgs.Empty) ;
}
}
static void Main(string[] args)
{
new Program();
}
}
}
There are two kinds of events demonstrated here
Field-like events => we can invoke
events with accessors => we cannot invoke (cannot find this in specs why, maybe I missed it)(and was only testing this on Visual Studio 2005, and the specs was the latest, I guess)
Related
I may be misunderstanding something fundamental here as I'm new to these concepts so please bear with me.
I'm currently removing methods from an event like so:
scheduleView.TouchDown -= scheduleView_TouchDown;
And then on other occasions - adding the methods:
scheduleView.TouchDown += scheduleView_TouchDown;
It all works fine so far, and I can understand it's possible to add several methods, like so:
scheduleView.TouchDown += scheduleView_TouchDown;
scheduleView.TouchDown += scheduleView_AnotherTouchDownEventHandler;
But how would I then later check what methods were wired up to this event?
Interestingly, you can't (at least, from the outside). An event is only obliged to offer 2 accessors - add and remove. There are other accessor methods defined in the CLI spec, but they aren't used in C# or anywhere else AFAIK. The key point: we can't ask an event what is subscribed (and indeed, we shouldn't need to know). All you can do is: add or remove.
If you are worried about double-subscribing, then note that if you try to unsubscribe and you haven't actually subscribed, then under every sane implementation this is simply a no-op; which means you can do:
// make sure we are subscribed once but **only** once
scheduleView.TouchDown -= scheduleView_TouchDown;
scheduleView.TouchDown += scheduleView_TouchDown;
From the perspective of the code raising the event, you rarely need to know who - simply:
// note I'm assuming a "field-like event" implementation here; otherwise,
// change this to refer to the backing-field, or the delegate from the
// event-handler-list
var handler = TouchDown;
if(handler != null) handler(this, EventArgs.Empty); // or similar
There is also a way to break the delegate list into individual subscribers, but it is very rarely needed:
var handler = TouchDown;
if(handler != null) {
foreach(EventHandler subscriber in handler.GetInvocationList()) {
subscriber(this, EventArgs.Empty);
}
}
The main uses for this are:
when you want to perform exception-handling on a per-subscriber basis
when the delegate returns a value or changes state, and you need to handle that on a per-subscriber basis
Yes: If you are within the class that publishes the Event, you can just access the delegate, and you can call the GetInvocationList method to get a list of the subscribers.
No: If you are working outside the class, as the delegate is not exposed to you. You could use reflection to get at it, but that would be a hack, at best.
In the type that declares the event, you can use GetInvocationList() to find out which delegates are subscribed:
public class EventProvider
{
public event EventHandler SomeEvent;
protected virtual void OnSomeEvent(EventArgs args)
{
if (SomeEvent != null)
{
var delegates = SomeEvent.GetInvocationList();
foreach (var del in delegates)
{
Console.WriteLine("{0} has subscribed to SomeEvent", del.Method.Name);
}
SomeEvent(this, args);
}
}
public void RaiseSomeEvent()
{
OnSomeEvent(EventArgs.Empty);
}
}
public class Program
{
public static void Main()
{
EventProvider provider = new EventProvider();
provider.SomeEvent += Callback1;
provider.SomeEvent += Callback2;
provider.RaiseSomeEvent();
}
public static void Callback1(object sender, EventArgs e)
{
Console.WriteLine("Callback 1!");
}
public static void Callback2(object sender, EventArgs e)
{
Console.WriteLine("Callback 2!");
}
}
This produces the following output:
Callback1 has subscribed to SomeEvent
Callback2 has subscribed to SomeEvent
Callback 1!
Callback 2!
I have some C# code that updates some properties of an object. I have an event handler defined to help me respond when the update process is done. Unfortunately, I've learned that this event is getting fired multiple times. I suspect this is happening because the event handler is being set at the wrong time. Currently, it is being set like the following:
myObject.Update_Succeeded += new EventHandler(myObject_Update_Succeeded);
Due to the complexity of the code, I'm having a difficult time of tracking down where it should be set. I would like to only set the event handler it hasn't been previously set. Because of this, I want to do something like this:
ClearEventHandlers(myObject);
or
myObject.Update_Succeeded = null;
myObject.Update_Succeeded += new EventHandler(myObject_Update_Succeeded);
Is there a way to accomplish what I'm trying?
Thank you!
Yes, you can customize the add/remove accessors of your event. This article describes these accessors. But you can do something like:
class MyClass
{
private EventHandler _myEvent;
public event EventHandler MyEvent
{
[MethodImpl(MethodImplOptions.Synchronized)]
add
{
_myEvent = (EventHandler)Delegate.Combine(_myEvent, value);
}
[MethodImpl(MethodImplOptions.Synchronized)]
remove
{
_myEvent = (EventHandler)Delegate.Remove(_myEvent, value);
}
}
public void ClearMyEvent() {
_myEvent = null;
}
...
}
You should be able to remove a handler using the subtract operator like below
myObject.Update_Succeeded -= new EventHandler(myObject_Update_Succeeded);
Or check this out for a way to remove all event handler if you are in doubt
How to remove all event handlers from a control
Proper way should be to detach the handler from each event after you no longer use it:
public class MyObjectListener
{
private readonly MyObject _object;
public class MyObjectListener(MyObject obj)
{
_object = obj;
Attach();
}
// adds event handlers
private void Attach()
{
obj.UpdateSucceeded += UpdateSuceededHandler;
obj.UpdateFailed += UpdateFailedHandler;
}
// removes event handlers
private void Detach()
{
obj.UpdateSucceeded -= UpdateSuceededHandler;
obj.UpdateFailed -= UpdateFailedHandler;
}
...
}
The only thing you need to decide is where to call the Detach method. For example, you can call it in the handler itself:
private void UpdateSuceededHandler(object sender, Data args)
{
Detach();
// do something when it succeeds
}
private void UpdateFailedHandler(object sender, Data args)
{
Detach();
// do something when it fails
}
Or, you could allow users of MyObjectListener to tell it that it no longer needs to listen to the attached object:
public void StopListening()
{
Detach();
}
An object which raises an event should not allow its listeners to modify the event invocation list. Each event listener should subscribe or unsubscribe its own event handlers only.
You better set event handler on the initialization of your object i.e. in your Constructor.
I know I can delegate the subscription of my event to another event,
public event EventHandler MyEvent
{
add { SomeClass.AnotherEvent += value; }
remove { SomeClass.AnotherEvent -= value; }
}
In this case, if AnotherEvent is raised, then MyEvent will be raised as well.
However in my class I cannot raise MyEvent() on my own as usual, because it says:
Error 3 The event 'MyEvent' can only appear on the left hand side of += or -=
I assume this is because the delegation of the subscription above.
Is it possible that I can delegate a subscription as well as invoke it on my own? The objective is so that MyEvent can be raised by two things, AnotherEvent as well as my own code.
Yes, you can - but you'll need your own delegate variable. You could either subscribe to SomeClass.AnotherEvent once and call your own delegate when that event is raised, or just keep subscribers twice:
private EventHandler myEvent;
public event EventHandler MyEvent
{
add
{
myEvent += value;
SomeClass.AnotherEvent += value;
}
remove
{
myEvent -= value;
SomeClass.AnotherEvent -= value;
}
}
Note that if you go for the "subscribe to SomeClass.AnotherEvent once" approach, you may want to consider only subscribing when you first see a subscription to MyEvent, and unsubscribing when myEvent becomes null after unsubscription. It can all get quite tricky in terms of disposal...
You will have to wire up your other someclass's event..
// constructor or initializer..
SomeClass.AnotherEvent += (s,e)=>{
if(MyEvent != null){
MyEvent(s,e);
}
};
// let this be implicit default event
public event EventHandler MyEvent;
protected void RaiseMyEvent(){
if(MyEvent != null){
MyEvent(this,EventArgs.Empty);
}
}
You need to invoke the delegate itself, not the event property. Because MyEvent doesn't have a backing field, that means you would have to invoke the backing field of SomeClass.AnotherEvent, which (usually) only SomeClass can do. As others have stated there are ways around this. See their answers.
I don't think you can, and if you could, I don't think that it would be logical. So I would implement the event normally, and then add an event handler to your class that listens to events from the other class.
You're basically making a class that has an event that is fired when something happens internally in the class AND something happens in a dependent class. I think that the implementation should reflect this. For example:
public MyClass()
{
this.other = new SomeClass;
other.AnotherEvent += SomeClass_AnotherEvent;
}
public event EventHandler MyEvent;
private void SomeClass_AnotherEvent(object sender, EventArgs e)
{
if (MyEvent != null)
MyEvent(this, e); // I think that it would be most logical, that you do not pass the sender
}
This question already has answers here:
How to ensure an event is only subscribed to once
(8 answers)
Closed 8 years ago.
Duplicate of: How to ensure an event is only subscribed to once
and Has an event handler already been added?
I have a singleton that provides some service and my classes hook into some events on it, sometimes a class is hooking twice to the event and then gets called twice.
I'm looking for a classical way to prevent this from happening. somehow I need to check if I've already hooked to this event...
How about just removing the event first with -= , if it is not found an exception is not thrown
/// -= Removes the event if it has been already added, this prevents multiple firing of the event
((System.Windows.Forms.WebBrowser)sender).Document.Click -= new System.Windows.Forms.HtmlElementEventHandler(testii);
((System.Windows.Forms.WebBrowser)sender).Document.Click += new System.Windows.Forms.HtmlElementEventHandler(testii);
Explicitly implement the event and check the invocation list. You'll also need to check for null:
using System.Linq; // Required for the .Contains call below:
...
private EventHandler foo;
public event EventHandler Foo
{
add
{
if (foo == null || !foo.GetInvocationList().Contains(value))
{
foo += value;
}
}
remove
{
foo -= value;
}
}
Using the code above, if a caller subscribes to the event multiple times, it will simply be ignored.
I've tested each solution and the best one (considering performance) is:
private EventHandler _foo;
public event EventHandler Foo {
add {
_foo -= value;
_foo += value;
}
remove {
_foo -= value;
}
}
No Linq using required. No need to check for null before cancelling a subscription (see MS EventHandler for details). No need to remember to do the unsubscription everywhere.
You really should handle this at the sink level and not the source level. That is, don't prescribe event handler logic at the event source - leave that to the handlers (the sinks) themselves.
As the developer of a service, who are you to say that sinks can only register once? What if they want to register twice for some reason? And if you are trying to correct bugs in the sinks by modifying the source, it's again a good reason for correcting these issues at the sink-level.
I'm sure you have your reasons; an event source for which duplicate sinks are illegal is not unfathomable. But perhaps you should consider an alternate architecture that leaves the semantics of an event intact.
You need to implement the add and remove accessors on the event, and then check the target list of the delegate, or store the targets in a list.
In the add method, you can use the Delegate.GetInvocationList method to obtain a list of the targets already added to the delegate.
Since delegates are defined to compare equal if they're linked to the same method on the same target object, you could probably run through that list and compare, and if you find none that compares equal, you add the new one.
Here's sample code, compile as console application:
using System;
using System.Linq;
namespace DemoApp
{
public class TestClass
{
private EventHandler _Test;
public event EventHandler Test
{
add
{
if (_Test == null || !_Test.GetInvocationList().Contains(value))
_Test += value;
}
remove
{
_Test -= value;
}
}
public void OnTest()
{
if (_Test != null)
_Test(this, EventArgs.Empty);
}
}
class Program
{
static void Main()
{
TestClass tc = new TestClass();
tc.Test += tc_Test;
tc.Test += tc_Test;
tc.OnTest();
Console.In.ReadLine();
}
static void tc_Test(object sender, EventArgs e)
{
Console.Out.WriteLine("tc_Test called");
}
}
}
Output:
tc_Test called
(ie. only once)
Microsoft's Reactive Extensions (Rx) framework can also be used to do "subscribe only once".
Given a mouse event foo.Clicked, here's how to subscribe and receive only a single invocation:
Observable.FromEvent<MouseEventArgs>(foo, nameof(foo.Clicked))
.Take(1)
.Subscribe(MyHandler);
...
private void MyHandler(IEvent<MouseEventArgs> eventInfo)
{
// This will be called just once!
var sender = eventInfo.Sender;
var args = eventInfo.EventArgs;
}
In addition to providing "subscribe once" functionality, the RX approach offers the ability to compose events together or filter events. It's quite nifty.
Create an Action instead of an event. Your class may look like:
public class MyClass
{
// sender arguments <----- Use this action instead of an event
public Action<object, EventArgs> OnSomeEventOccured;
public void SomeMethod()
{
if(OnSomeEventOccured!=null)
OnSomeEventOccured(this, null);
}
}
have your singleton object check it's list of who it notifies and only call once if duplicated. Alternatively if possible reject event attachment request.
In silverlight you need to say e.Handled = true; in the event code.
void image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true; //this fixes the double event fire problem.
string name = (e.OriginalSource as Image).Tag.ToString();
DoSomething(name);
}
Please tick me if this helps.
Take the following C# class:
c1 {
event EventHandler someEvent;
}
If there are a lot of subscriptions to c1's someEvent event and I want to clear them all, what is the best way to achieve this? Also consider that subscriptions to this event could be/are lambdas/anonymous delegates.
Currently my solution is to add a ResetSubscriptions() method to c1 that sets someEvent to null. I don't know if this has any unseen consequences.
From within the class, you can set the (hidden) variable to null. A null reference is the canonical way of representing an empty invocation list, effectively.
From outside the class, you can't do this - events basically expose "subscribe" and "unsubscribe" and that's it.
It's worth being aware of what field-like events are actually doing - they're creating a variable and an event at the same time. Within the class, you end up referencing the variable. From outside, you reference the event.
See my article on events and delegates for more information.
Add a method to c1 that will set 'someEvent' to null.
public class c1
{
event EventHandler someEvent;
public ResetSubscriptions() => someEvent = null;
}
class c1
{
event EventHandler someEvent;
ResetSubscriptions() => someEvent = delegate { };
}
It is better to use delegate { } than null to avoid the null ref exception.
The best practice to clear all subscribers is to set the someEvent to null by adding another public method if you want to expose this functionality to outside. This has no unseen consequences. The precondition is to remember to declare SomeEvent with the keyword 'event'.
Please see the book - C# 4.0 in the nutshell, page 125.
Some one here proposed to use Delegate.RemoveAll method. If you use it, the sample code could follow the below form. But it is really stupid. Why not just SomeEvent=null inside the ClearSubscribers() function?
public void ClearSubscribers ()
{
SomeEvent = (EventHandler) Delegate.RemoveAll(SomeEvent, SomeEvent);
// Then you will find SomeEvent is set to null.
}
Setting the event to null inside the class works. When you dispose a class you should always set the event to null, the GC has problems with events and may not clean up the disposed class if it has dangling events.
You can achieve this by using the Delegate.Remove or Delegate.RemoveAll methods.
Conceptual extended boring comment.
I rather use the word "event handler" instead of "event" or "delegate". And used the word "event" for other stuff. In some programming languages (VB.NET, Object Pascal, Objective-C), "event" is called a "message" or "signal", and even have a "message" keyword, and specific sugar syntax.
const
WM_Paint = 998; // <-- "question" can be done by several talkers
WM_Clear = 546;
type
MyWindowClass = class(Window)
procedure NotEventHandlerMethod_1;
procedure NotEventHandlerMethod_17;
procedure DoPaintEventHandler; message WM_Paint; // <-- "answer" by this listener
procedure DoClearEventHandler; message WM_Clear;
end;
And, in order to respond to that "message", a "event handler" respond, whether is a single delegate or multiple delegates.
Summary:
"Event" is the "question", "event handler (s)" are the answer (s).
Remove all events, assume the event is an "Action" type:
Delegate[] dary = TermCheckScore.GetInvocationList();
if ( dary != null )
{
foreach ( Delegate del in dary )
{
TermCheckScore -= ( Action ) del;
}
}
This is my solution:
public class Foo : IDisposable
{
private event EventHandler _statusChanged;
public event EventHandler StatusChanged
{
add
{
_statusChanged += value;
}
remove
{
_statusChanged -= value;
}
}
public void Dispose()
{
_statusChanged = null;
}
}
You need to call Dispose() or use using(new Foo()){/*...*/} pattern to unsubscribe all members of invocation list.
Instead of adding and removing callbacks manually and having a bunch of delegate types declared everywhere:
// The hard way
public delegate void ObjectCallback(ObjectType broadcaster);
public class Object
{
public event ObjectCallback m_ObjectCallback;
void SetupListener()
{
ObjectCallback callback = null;
callback = (ObjectType broadcaster) =>
{
// one time logic here
broadcaster.m_ObjectCallback -= callback;
};
m_ObjectCallback += callback;
}
void BroadcastEvent()
{
m_ObjectCallback?.Invoke(this);
}
}
You could try this generic approach:
public class Object
{
public Broadcast<Object> m_EventToBroadcast = new Broadcast<Object>();
void SetupListener()
{
m_EventToBroadcast.SubscribeOnce((ObjectType broadcaster) => {
// one time logic here
});
}
~Object()
{
m_EventToBroadcast.Dispose();
m_EventToBroadcast = null;
}
void BroadcastEvent()
{
m_EventToBroadcast.Broadcast(this);
}
}
public delegate void ObjectDelegate<T>(T broadcaster);
public class Broadcast<T> : IDisposable
{
private event ObjectDelegate<T> m_Event;
private List<ObjectDelegate<T>> m_SingleSubscribers = new List<ObjectDelegate<T>>();
~Broadcast()
{
Dispose();
}
public void Dispose()
{
Clear();
System.GC.SuppressFinalize(this);
}
public void Clear()
{
m_SingleSubscribers.Clear();
m_Event = delegate { };
}
// add a one shot to this delegate that is removed after first broadcast
public void SubscribeOnce(ObjectDelegate<T> del)
{
m_Event += del;
m_SingleSubscribers.Add(del);
}
// add a recurring delegate that gets called each time
public void Subscribe(ObjectDelegate<T> del)
{
m_Event += del;
}
public void Unsubscribe(ObjectDelegate<T> del)
{
m_Event -= del;
}
public void Broadcast(T broadcaster)
{
m_Event?.Invoke(broadcaster);
for (int i = 0; i < m_SingleSubscribers.Count; ++i)
{
Unsubscribe(m_SingleSubscribers[i]);
}
m_SingleSubscribers.Clear();
}
}