How to remove the event handler after it had fired? - c#

How to remove the event handler after the event handler had fired so that it can execute only once?
c.Click += (o,e)=>{
Console.WriteLine("Clicked!");
/*I want to remove this event handler now so that it doesn't fire anymore*/
}

You need to store the event handler in a variable, so that you can refer to it later. Because you want to refer to the handler from within the handler you also need to declare it before you initialize it. Finally, you cannot use an uninitialized variable, so you need to initialize it to null first, even though that null value will never be read from.
EventHandler handler = null;
handler = (o,e)=>{
Console.WriteLine("Clicked!");
c.Click -= handler;
}
c.Click += handler;
The other option would be to use a named method, rather than an anonymous method.

This becomes easier if you use a named method instead of an anonymous lambda method.
c.Click += MyHandler;
void MyHandler(object sender, EventArgs e)
{
Console.WriteLine("Clicked!");
((Button)sender).Click -= MyHandler;
}

Related

Single event handler instance without static

I had an event in a usercontrol
public event EventHandler EH_OpenDefaultClick;
A form when a radio button is click will subscribe to this event. However this form will not be destroyed and whenever the radio button is click, i will subscribe to the event
ucStdButton_Recipe.EH_OpenDefaultClick += ucStdButton_Recipe_EH_OpenDefaultClick;
with the only line above, every time it will be subscribe, hence triggering multiple time. I can add in the code to compare if it is null. But eventually codes get untidy with all these == null
if (ucStdButton_Recipe.EH_OpenDefaultClick == null)
ucStdButton_Recipe.EH_OpenDefaultClick += ucStdButton_Recipe_EH_OpenDefaultClick;
I cannot declare the event as static as the event is subscribe during runtime. Is there any declaration type allowing me to subscribe more easily without the comparison statement
Maybe something like this could help you.
private EventHandler eh_OpenDefaultClick;
public event EventHandler EH_OpenDefaultClick
{
add
{
eh_OpenDefaultClick -= value;
eh_OpenDefaultClick += value;
}
remove
{
eh_OpenDefaultClick -= value;
}
}
Removing handler before adding it ensures that there is no more than one instance of the same handler attached at any moment. If you want to have just one handler ever, change eh_OpenDefaultClick -= value; to if (eh_OpenDefaultClick == null)
Of course you will need to call private eh_OpenDefaultClick to raise the event

How can i set a control event to null

H,
I have a listView with an event set to ItemChecked, now i need to do something like:
var tmp = this.listView.ItemChecked;
this.listView.ItemChecked = null; //set the event to null
// run some code which checkes some item in the list
// wehre the event shouldnt get fired on
this.listView.ItemChecked = tmp; //set the event again
Problem is that i can not read the event, i get the message that it can be used only on the left side of a statement.
Any Ideas?
this.listView.ItemChecked -= myEventHandler;
// run some code
this.listView.ItemChecked += myEventHandler;
You can unsubscribe and resubscribe to do what you're trying to do:
private void listView1_ItemChecked(object sender, ItemCheckedEventArgs e)
{
listView1.ItemChecked -= new ItemCheckedEventHandler(listView1_ItemChecked);
// do stuff
listView1.ItemChecked += new ItemCheckedEventHandler(listView1_ItemChecked);
}
You could remove then add the event handler. Assuming your code is in a method named ItemChecked
listView.ItemChecked -= ItemChecked;
// do whatever
listView.ItemChecked += ItemChecked;
However, I prefer checking for re-entrant calls.
object alreadyInItemChecked;
void ItemChecked(object s, EventArgs e)
{
if (Monitor.TryEnter(alreadyInItemChecked))
{
try
{
// do whatever
}
finally
{
Monitor.Exit(alreadyInItemChecked)
}
}
}
You can subscribe / unsubscribe to events as something like this.
this.someObject.someObjectEvent += someEventHandler;
this.someObject.someObjectEvent -= null;
So subscribe when you want the event to be fired and unsubscribe when not needed.

Calling an event handler in C#

I've been trying to learn how to use event handlers in C# but I can't figure out what handler(this, e) does in the following code:
public event EventHandler ThresholdReached;
protected virtual void OnThresholdReached(EventArgs e)
{
EventHandler handler = ThresholdReached;
if (handler != null)
{
handler(this, e);
}
}
Is it trying to call the event handler method (this) with the event (e)?
It invokes all registered event listeners which are registered on the ThresholdReached event.
The handler != null check makes sure at least one listener is registered to that event.
In C# 6.0 and above you can use Null Propagation:
handler?.Invoke(this, e);
handler(this, e) will call every registered event listener. Event listeners subscribe with help of the += operator and unsubscribe with -= operator to that event.
this is there to give the event listener to know who raised the ThresholdReached event. Who was the sender of the event.
e is the event argument which is also passed into the listener method which can contain more useful informations about the ThresholdReached event e.g. which threshold was reached.
It is raising a ThresholdReached event with arguments sender=this and eventarguments = e.
In fact, it is the same as the following;
public event EventHandler ThresholdReached;
protected virtual void OnThresholdReached(EventArgs e)
{
if (ThresholdReached != null)
{
ThresholdReached(this, e);
}
}
If there are any listeners to this event; it will simply call listener delegates;
this.ThresholdReached += new EventHandler(Form1_ThresholdReached);
Then, when this event is raised Form1_ThresholdReached function will be called with this and e parameters.
The code in your example copies all registered handlers to the local variable handler, checks that the invocation list is not empty and invokes all members of the copied invocation list with the arguments this and e.
The reason for the fact that you get a snapshot of the current invocation list is that delegates are immutable. You get a reference to the current multicast delegate, and when handlers are added or removed the backing field points to a new delegate created from two immutable ones.
The usual reason to copy the invocation list to a local variable is some form of thread-safety: a handler could be unsubscribed between the usual nullity check (check that the invocation list isn't empty) and the actual invocation: that way you might accidentally fire an event with no handlers and a NullReferenceException would be thrown.
handler refers to your ThresholdReached event. So, if anyone is subscribing to ThresholdReached events, their registered handler will be called with arguments thisand e.
It is Triggering the ThresholdReached event. Passing a reference to itself, this. Passing arguments about the event in e.
Is it trying to call the event handler method (this) with the event (e)?
No, not literally. It is calling the event handler with the EventArgs e and using this as sender. It might as well be:
if (ThresholdReached != null)
{
ThresholdReached(this, e);
}
Or, to circumvent the null check:
public event EventHandler ThresholdReached = delegate { };
protected virtual void OnThresholdReached(EventArgs e)
{
ThresholdReached(this, e);
}
But, as #Oded noted, the first piece isn't thread-safe, because EventHandler handler = ThresholdReached creates a copy of the handler, which is better explained in this question.
The call to handler represents a function call in another object or class. When you create the object, you will be able to write a piece of code that looks like this:
obj.ThreasholdReached += new EventHandler(someFunction);
someFunction in that class will be defined like this
public someFunction(object sender, EventArgs e) {...}
the OnThreasholdReached function in the original object is what publishes the event to any other class that has assigned a function to the ThreasholdReached handler. Using handler as a go-between is an entirely unnecessary extra step. You are still saying if ThreasholdReached != null, its the same thing.
In Summary: The line of code handler(this, e) is actually the call to whatever subscriber someFunction(object sender, EventArgs e) has been assigned to the ThreasholdReached event of the object.

Event fires more and more times

I have a silverlight mvvm application that loads main view with 2 user controls loaded into 2 ContentControls, one with listbox showing items and other with edit button. When i click edit button, 2 new user controls load into the ContentControls, one showing data to edit (EditData) and other having Save and Cancel button (EditAction).
When i click save button, it raises an event that is defined in seperate GlobalEvents.cs class like:
public event EventHandler OnSaveButtonClicked;
public void RaiseSaveButtonClicked()
{
this.OnSaveButtonClicked(this, EventArgs.Empty);
}
and i subscribe to it in the other user control EditData, because i need to transfer that edited data via custom EventArgs, so i have put in the constructor of it's ViewModel:
this.globalEvents.OnSaveButtonClicked += (s, e) => SaveData();
and in the Save data:
public void SaveData()
{
globalEvents.RaiseSaveData(EditedGuy);
}
which raises another event that loads previous user controls into their ControlContent and shows edited data in list box. Thats all fine, but whenever i click on edit and then save again, it raises the event twice, and again 3 times, then 4 and so on. How can i make it to be raised only ONE time? I thought it could be because every time i click edit, a new instance of the user control is loaded and i dont know, maybe the subscription to the event stays, so i have tried to paste
this.globalEvents.OnSaveButtonClicked -= (s, e) => SaveData();
to the Dispose() method but without success. How can i make this work?
You can't use lambdas when you want to unregister from events.
this.globalEvents.OnSaveButtonClicked += (s, e) => SaveData();
This will create one instance - let's call it instance A - of type EventHandler and add it as a handler.
this.globalEvents.OnSaveButtonClicked -= (s, e) => SaveData();
This will not remove instance A from the event but create a new instance - instance B - and tries to remove it from the event.
To fix this problem, either create a little method or save that anonymous method in a field:
class ViewModel
{
private EventHandler _saveButtonClickedHandler;
// ...
public ViewModel()
{
_saveButtonClickedHandler = (s, e) => SaveData();
this.globalEvents.OnSaveButtonClicked += _saveButtonClickedHandler;
// ...
}
public void Dispose()
{
this.globalEvents.OnSaveButtonClicked -= _saveButtonClickedHandler;
// ...
}
// ...
}
this.globalEvents.OnSaveButtonClicked += (s, e) => SaveData();
This line is being called multiple times so you are adding a new event handler every time.
You need to either move that line to somewhere where it's only called once or change the event handler to:
this.globalEvents.OnSaveButtonClicked += SaveData;
public void SaveData(object sender, EventArgs e)
{
globalEvents.RaiseSaveData(EditedGuy);
this.globalEvents.OnSaveButtonClicked -= SaveData();
}
So you remove the event handler after dealing with it. This assumes that the handler will be added back next time you go into edit mode.
You could define a private eventhandler delegate variable in your class and assign it in your constructor:
private SaveButtonClickedHandler _handler;
Assign the handler in your constructor:
_handler = (s,e) => SaveData();
this.globalEvents.OnSaveButtonClicked += _handler;
Dispose:
this.globalEvents.OnSaveButtonClicked -= _handler;
"SaveButtonClickedHandler" is pseudo-code/placeholder for whatever the name of the delegate should be.
Hasanain
You'll have to put in a proper event handler method that calls SaveData() and register/unregister that. Otherwise you try to unregister another "new" anonymous method instead of the original one you've registered, which you, because it is anonymous, cannot actually access anymore.
public void SaveButtonClicked(object sender, EventArgs e)
{
SaveData();
}
this.globalEvents.OnSaveButtonClicked += SaveButtonClicked;
this.globalEvents.OnSaveButtonClicked -= SaveButtonClicked;

C# attach event handling with additional parameters

PREFACE
I have a Windows form Button that exposes the event OnClick (object sender, EventArgs e).
In my application I can handle this by using the classic event handling technique of C#:
// Button Creation
Button button = new Button();
button.Click += MyEventHandler;
Then Windows Form ask me for an handler with the following signature:
public void MyEventHandler(object sender, EventArgs e) { }
Suppose that I would like to use lambda expression to do this I can use the syntax:
button.Click += (sender, args) =>
{
// execute the code
};
The drawback of this is that I can't unsubscribe from a Lambda expression.
QUESTION
What I would like to do is to have an utility class that will allow me to handle any Click event plus using an additional Action as a parameter. So I would like to write something like this:
button.Click += MyUtility.Click(()
=> {
// the custom code the Click event will execute
})
Can I do it in somehow?
You can assign the lambda expression to a local variable or field.
For example:
EventHandler handler = (sender, args) =>
{
// execute the code
};
button.Click += handler;
button.Click -= handler;
If you want to unsubscribe inside the handler, you'll need to assign handler to null, then to the lambda, to avoid definite assignment issues.

Categories

Resources