How to prevent attaching an event handler multiple times to the button? - c#

I am in the phase of testing the project and I am dealing with a problem with attaching an event handler multiple times to the button.
I have a class with the _btnSelecteProj field, which I assign the button reference via the parameter in the InitProjects method.
Public Void InitProjects (Button btnSelectProject)
{
        _btnSelecteProj = btnSelectProject;
        _btnSelecteProj.MouseClick += BtnSelectProj_MouseClick;
}
This method is in the runtime called several times and therefore the event handler is assigned multiple times, which I need to prevent.
I realize that this a wrong design. However, it is not possible to refactor the whole project, because I am in a testing phase. I struggle with this problem in several places in the code.
I tried to prevent this problem this way:
_btnSelectedProj.MouseClick -= BtnSelectProj_MouseClick;
_btnSelectedProj.MouseClick += BtnSelectProj_MouseClick;
But it doesn't work.
I appreciate any advice.

In your event you can implement the add/remove operations as explicit methods and check you event handler for null in add.

For example you can check if the value of _btnSelecteProj is changed
public void InitProjects(Button btnSelectProject)
{
if (_btnSelecteProj != null)
{
if (_btnSelecteProj.Equals(btnSelectProject))
return;
_btnSelecteProj.MouseClick -= BtnSelecteProj_MouseClick;
}
_btnSelecteProj = btnSelectProject;
_btnSelecteProj.MouseClick += BtnSelecteProj_MouseClick;
}

I had to add method ClearEventHandlers() and remove event handler proper way.
public void ClearEventHandlers()
{
_btnSelecteProj.MouseClick -= BtnSelectProj_MouseClick;
}
This method runs at the end of the object's life-time.
NOTE: I'm not sure whether this question/answer is useful to someone else...

Related

Unsign all event listeners, C# code example

I have a C# class which introduce a new custom event type, and allows users add or remove listeners to it. Also I implement a method which revoves all event listeners during dispatch;
public event EventHandler DataCommited;
private void DetatchListeners()
{
if (DataCommited != null)
{
foreach (EventHandler eh in DataCommited.GetInvocationList())
{
DataCommited -= eh;
}
}
}
It is possible to implement a method which will be taking DataCommited event as an argument. So, I can unsign a set of events using one method. I tried a lot ways implementing it, but unfortunately failed to do it. I wonder if it is actually possible and how. Thank you!!!
It is possible to implement a method which will be taking DataCommited event as an argument.
Well, not really. You can take an EventInfo, but that's all. It's important to understand that this statement:
public event EventHandler DataCommited;
actually creates two things:
An event, which code in other classes can subscribe to and unsubscribe from
A field of type EventHandler, which you can use to call the handlers, or get each one individually.
A simpler implementation of your current code would simply be this:
public event EventHandler DataCommited;
private void DetatchListeners()
{
DataCommitted = null;
}
Unsubscribing from a field-like event just changes the value of the field, after all.
However, if you have an EventInfo, you don't know how that event is implement. It may be backed directly by a field - it might not be... there's no general way of asking an event for its current handlers, or setting a new list of handlers. All you can do directly with an event is subscribe and unsubscribe.
If you only use field-like events, you could use reflection to find the name of the field and set the value to null. You can't do it in general though.
See my article on delegates and events for more information.

How to check event handler assigned or not

my previos question is how to clear event handles in c#
Now i need to know that how to check any event handler already assigned?
If the event is in the same class where you will do the check, you can compare to null. But if this is not the case, you should ask yourself why do you care about the inside workings of a class. I mean it is the job of the class which contains the event to care about its subscribers not the opposite. But if you really want this information, the event containing class can expose a property for the outside world - like HasEventHandlers.
Unless I misunderstand the question, a simple check for null should be sufficient. You always need to check for a null in the event handler anyway before calling any event handlers.
In quick watch window I found the btnSubmit click handler with the following expression:
(((System.Web.UI.Control)(btnSubmit)).Events.head.handler).Method
An old solution presented by Jon Skeet where you explicitly implemented event handling would be the best way to address this.
Here is how:
private EventHandler m_myEvent;
public event EventHandler OnEvent
{
add
{
// First try to remove the handler, then re-add it
m_myEvent -= value;
m_myEvent += value;
}
remove
{
m_myEvent -= value;
}
}
In the unlikely scenario that you have multicast delegates, you could experience odd behaviors.

Why would a 'public event EventHandler cccc' be null?

Why would a 'public event EventHandler cccc' be null?
I have a class that's
public class Builder
{
public event EventHandler StartedWorking;
public Builder()
{
// Constructor does some stuff
}
public void Start()
{
StartedWorking(this, eventargobject); //StartedWorking is null --
}
}
This seems straightforward and something I do all the time? Am I missing something obvious or is there something that could cause this?
EDIT:
Does this mean that if I fire an event that is not subscribed to in a client class I have to check that it is not null?
EDIT-2:
I guess I'd never had events that were never not subscribed to and hence never ran into this --
You learn something new every day
Sorry about the seemingly stupid question....
The event handler will be null unless somebody has subscribed to the event. As soon as a delegate is subscribed to the event, it will no longer be null.
This is why it's always suggested to use the following form for raising events:
public void Start()
{
var handler = this.StartedWorking;
if (handler != null)
{
handler(this, eventArgObject);
}
}
This protects you from a null exception if there has been no subscribers.
As others have already said, it's null because there are no subscribers.
To answer your edit: Yes, you should always check an event for null before triggering it. However, if you just do a plain if(StartedWorking != null){...} you risk a race condition, because it's possible for a subscriber to unsubscribe after the null check but before you trigger the event. Because of this, you should always use this pattern when checking events for null:
protected void OnStartedWorking()
{
EventHandler localEvent = StartedWorking
if(localEvent != null)
{
localEvent(this, EventArgs.Empty);
}
}
This prevents the race condition by taking a copy of the event first so the subscribe list is fixed at the point of copying.
There's more infomration about publishing events on MSDN: How to Publish Events that Conform to .NET Framework Guidelines
(This works because in .net the MultiCastDelegate class in imutable, so any attempt to change the subscriber list on the event won't effect the copy you have made)
If you haven't hooked any event subscribers up to your StartedWorking event, then it will be null. That is how .NET events works.
This article, among other things, demonstrates that you should check for null before invoking an event. This other question and answer demonstrates how you can create events in a way that avoids the null check (basically by adding an empty handler always).
Don't you need to assign a function ?
StartedWorking += new EventHandler(afunction);
void afunction(object sender, EventArgs e)
{
DoSomething();
}

Subscribe event to event in C#?

Is it possible in C# to connect one event to another so emitting first event will emit second one? The only way i can see is to create a stub function that will emit second event and connect the first event to this function. Compiler do not want to connect event to event or event to anonymouse function / lambda that calls another event:
class Ui { public event EventHandler OnClick; }
class Logic { public event EventHandler OnExit; }
var ui = new Ui();
var logic = new Logic();
ui.OnClick += logic.OnExit; // Not working.
ui.OnClick += ( a, b ) => logic.OnExit; // Not working either :(.
Maybe it's some decorator available or some black magic that allows to chain events without stub functions?
You cannot do this, because you generally cannot do anything to an event from outside the object which owns it except for adding and removing handlers. In particular, you cannot list the existing registered handlers, and you cannot raise it. In your case, "copying" the event is essentially the same thing in disguise, and would allow you to circumvent this restriction; therefore, it's not allowed.
See this recent answer of mine for a more in-depth explanation of why things are the way they are - I just don't feel like retyping it all here.
For your particular case, if you own both classes, the workaround is to make them cooperate specifically - make Ui be aware of the associated Logic instance, and add event handlers to Logic.OnClick in Ui.OnClick.add implementation. Of course, this introduces coupling; you can reduce it to some extent by using more generic interfaces, but you can't get rid of it entirely.
As a side note, OnClick is not a good name for a .NET event. Common naming guide says that it should be simply Click (and OnClick should be the name of a protected virtual method that raises it).
You could do this by hiding the underlying event for Logic and then controlling calls to Add/Remove which require a UI parameter.
public class UI {
public EventHandler OnClick;
}
public class Logic {
private event EventHandler _onExit;
public void AddOnExit(UI ui, EventHandler e) {
ui.OnClick += e;
_onExit += e;
}
public void RemoveOnExit(UI ui, EventHandler e) {
ui.OnClick -= e;
_onExit -= e;
}
}

Is this dangerous? About events

Maybe a stupid question, but..
In my code I use the following construction at several places.
void MyFunction()
{
DoSomething(myClass.myProperty)
myClass.PropertyChanged += (s,e ) => {
if (e.PropertyName == "myProperty") {
DoSomething(myClass.myProperty);
}
}
}
So I want to do something initially, and also do the same when the property changes in the future.
Now the thing is, MyFunction() gets called several time during my program's execution. Will the delegate I assign to PropertyChanged be added evertime it passes through this method? (consuming more memory every iteration and slowing down the program) Or is the compiler/runtime smart enough to understand I should be only added the first time..? And if so, how does this work?
The compiler can't know your intent ... it will faithfully attach the event handler on each call to MyFunction().
Look at it this way - they compiler can't know that the reference variables (e.g. myclass) in your function refer to the same instances of objects you've attached handlers to previously. Nor can it know that you haven't detached the handler somewhere else between calls. It can't make assumptions like this.
You, however, could restructure your code so that the handler is only attached once. Since other consumers may subscribe to the PropertyChanged event, you need to keep some independent state around to know whether you have subscribed or not. For example:
if( !haveSubscribedToPropertyChanged ) {
myClass.PropertyChanged += (s,e ) => {
if (e.PropertyName == "myProperty") {
DoSomething(myClass.myProperty);
}
}
haveSubscribedToPropertyChanged = true;
}
Many of the other answers suggest that you should check whether the PropertyChanged event is null to prevent adding several listeners. A problem with that solution is that PropertyChanged can be non-null if other parts of the code listens to the same event but for another property, like this.
void AnotherFunction()
{
myClass.PropertyChanged += (s,e) => {
if (e.PropertyName == "anotherProperty") {
DoSomethingElse(myClass.anotherProperty);
}
}
}
A better solution, in my opinion, would be to maintain a boolean flag which is initially false, and only set to true when MyFunction is called. Then, check this flag whether you need to add the event handler.

Categories

Resources