Calling an event handler in C# - 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.

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 to remove the event handler after it had fired?

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;
}

In which code file and where exactly in the file should I assign event handlers to controls?

Exactly where I must write this code?
button1.MouseHover += Common_MouseHover;
The problem is I want to declare one event handler and point each button at it. This is the common handler:
private void Common_MouseHover(object sender, EventArgs e)
{
Button btn = sender as Button;
if (btn != null)
btn.Image = pic
}
But I dont know where i must write:
button1.MouseHover += Common_MouseHover;
utton2.MouseHover += Common_MouseHover;
.. etc
In designer? Where exactly?
Either in the designer or the Constructor of your Form or if you are creating dynamic Buttons at the time of creation.
public Form1()
{
InitializeComponent();
button1.MouseHover += new EventHandler(Common_MouseHover);
button2.MouseHover += new EventHandler(Common_MouseHover);
}
If in the Property Editor.
Technically, it depends when you want to assign it. For example, you might not want the event to be handled by that method until certain conditions are met: then you assign the event handler once the conditions are true.
Bear in mind that assigning event handlers can cause memory leaks if you aren't careful. For example, you might have a timer that continually assigns the same event handler to the event. You need to check if the event already has the event handler, or if it's null, or whatever you need to prevent duplication. You might also want to remove event handlers dynamically using the -= operator.
For the example given, the form's constructor seems right, and is the most common place for assigning event handlers manually.
Call following function in your form Constructor after InitializeComponent(); -
private void init()
{
foreach (Control ctrl in this.Controls)
{
if (ctrl is Button)
{
(ctrl as Button).MouseHover += new EventHandler(Common_MouseHover);
}
}
}
Call it like this -
// Form Constructor
public Form3()
{
InitializeComponent();
Init();
}

DataRow Delete without firing CurrentChanged Event

Well, I have the following problem:
I need to delete a row from a bindingsource without fireing the CurrentChanged event. Deletion is working without any problems. But it raises the CurrentChanged event instantly, which leads to my code matching to the CurrentChanged event is being executed. That leads to a problem.
Is there any way to achieve a similar effect like .Delete() without raising the event?
Deleting a row will always raise the event, if there are any subscribers.
If the event code is under your control, you could set a flag which you check in the BindingSource_CurrentChanged event handler:
private void DeleteRow()
{
this.justDeletedRow = true;
this.bindingSource.DeleteRow(...);
}
protected void BindingSource_CurrentChanged(object sender ...)
{
if (this.justDeletedRow)
{
this.justDeletedRow = false;
return;
}
// Process changes otherwise..
}
If the code isn't under your control - if you're binding to a component, say - then you can unbind the handler while performing the operation:
private void DeleteRow()
{
this.bindingSource.CurrentChanged -= this.component.BindingSource_CurrentChanged;
this.bindingSource.DeleteRow(...);
this.bindingSource.CurrentChanged += this.component.BindingSource_CurrentChanged;
}

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;

Categories

Resources