My understanding is that any event handlers wired up in C# need to be unwired as such.
Object myObject = new Object();
myObject.Event += EventHandler; //Wired
myObject.Event -= EventHandler; //Unwired
But do you need to unwire the following code? and if so, how?
Object myObject = new Object();
myObject.Event += (object sender, EventArgs e) => { }; //Wired
myObject.Event -= ????? //Unwire? How?
My assumption is no?
Yes, you need to (*) and you need to do it like this:
Object myObject = new Object();
EventHandler handler = (object sender, EventArgs e) => { };
myObject.Event += handler; //Wired
myObject.Event -= handler; //Unwired
See here for an explanation.
(*)
You don't need to do it because of garbage collection. You need to do it, if you don't want the event to call your handler any more.
UPDATE:
To clarify a bit:
The only reason, why you want to unwire an event handler is that the object defining the event handler can be garbage collected.
Think about the following example:
You have a class PowerSource with an event BlackOut.
You have a class LightBulb that will be on, as long as there is power. It has a method ConnectToPowerSource. This method subscribes to the BlackOut event of the supplied PowerSource.
You have a collection that contains the light bulbs
Now, simply removing a light bulb from the list will not make it get garbage collected, because the PowerSource is still holding a reference to the LightBulb instance in its BlackOut event. Only after unregistering the LightBulb from the BlackOut event will make the LightBulb get garbage collected.
Yes, you do have to. Because an event is a strong reference, your event handler will continue to be invoked.
You can remove it as follows:
EventHandler handler = (s,e) => { DoSomething(); }
myObject.Event += handler;
myObject.Event -= handler;
Object myObject = new Object();
EventHandler h = (object sender, EventArgs e) => { }; //Wired
myObject.Event += h;
myObject.Event -= h;
Or, to unwire in the handler:
Object myObject = new Object();
EventHandler h = null; //need to declare h to use it in the following line
//compiler/resharper will complain about modified closure
h = (object sender, EventArgs e) => { myObject.Event-=h; };
myObject.Event += h;
Related
I have been tinkering with Events to gain a better understanding of their use in very general situations. I'm surprised to find the following, so I'm probably heading in the wrong direction...the essence of what I'm doing is changing a button to a random color when it is clicked:
Windows Form
public Form1()
{
ColorChanges KK = new ColorChanges();
KK.ColorEventHandler += handle_ColorChanges;
button1.Click += delegate { KK.ChangeColor(button1); };
}
Event Class
class ColorChanges
{
*... properties & constructor*
public void ChangeColor(object sender)
{
*... randomly assign color to ColorEventArgs*
}
protected virtual void onColorEvent(object sender, ColorEventArgs e)
{
EventHandler<ColorEventArgs> ceh = ColorEventHandler;
{
if (ceh != null)
{
ceh(sender, e)
}
}
}
public event EventHandler<ColorEventArgs> ColorEventHandler;
}
Custom Event Args
public class ColorEventArgs : EventArgs
{
public Color xColor { get; set; }
}
Event Handler
public void handle_ColorChanges(object sender, ColorEventArgs e)
{
if (sender is Button)
{
var ButtonSender = (Button)sender;
ButtonSender.BackColor = e.xColor;
}
}
So the edited questions are:
Is use of the EventHandler(TEventArgs) Delegate useful? MS documentation indicates that syntax like
button1.Click += new EventHandler<AutoRndColorEventArgs>(handle_ColorChanges);
is correct, but that will not reach my code to randomly select a color and an error
"No overload for 'handle_ColorChanges' matches delegate >'System.EventHandler' "
so something like
button1.Click += new EventHandler<AutoRndColorEventArgs>(KK.ChangeColor(button1));
or
button1.Click += new EventHandler(KK.ChangeColor(button1));
Error says that a method is required and if I use
"No overload for 'handle_ColorChanges' matches delegate
'System.EventHandler'"
Lambda expressions help thanks for the supporting answers
button1.Click += (sender,args) => KK.ChangeColor(s);
But that doesn't allow un-assignment and that will be required later...
An anonymous delegate has the same problem
button1.Click += delegate(object sender, EventArgs e)
{ KK.ChangeColor(sender); };
The crux of the problem is that my color methods or their delegates do not match the button delegate signature (object, event). I don't care about the button args and want to use my own HOW?
Is the use of the delegate correct?
Yep, what you are doing is assigning an anonymous delegate as your event handler. This is perfectly valid, however, the catch here is you can't unassign the event handler because you have no reference to it. You could keep a reference to it and do it that way (if required)
var clickHandler = delegate { ... };
button1.Click += clickHandler;
...
button1.Click -= clickHandler
If you need access to the parameters of the event handler you will need to add those into the signature e.g.
button1.Click += delegate (object sender, EventArgs args) { ... }
The new EventHandler(SomeHandlerMethod) construct is the long way of doing things, it's synonymous to += SomeHandlerMethod. Your code currently doesn't work because you are trying to actually call the handler inside the constructor when the constructor expects a reference to the method
+= new EventHandler<ColorEventArgs>(KK.ChangeColor);
Is there a better structure for this?
Yeah, you can do it using even less code
button1.Click += (s, args) => KK.ChangeColor(button1);
This is incorrect:
button1.Click += new EventHandler<AutoRndColorEventArgs>(KK.ChangeColor(button1));
Instead of KK.ChangeColor(button1), you just need to specify the event handler method name as you did in here:
KK.ColorEventHandler += handle_ColorChanges;
The event handler method signature should match with the EventHandler delegate.If you want to just call a method in event handler, you can use lambda statement like this:
button1.Click += (s,e) => KK.ChangeColor(s);
Or:
button1.Click += delegate(object s, EventArgs e) { KK.ChangeColor(s); };
By doing this you are creating an anonymous method and attach it to your Click event.
If I have reference to event ThrowEvent, method X() and subscribe like that :
ThrowEvent += X;
ThrowEvent += X;
ThrowEvent += X;
What happend when I try to make
ThrowEvent -= X;
Will it remove the first or the last added method ?
You can easily test it yourself:
private static void A(Object sender, EventArgs e) {
Console.Out.Write('A');
}
private static void B(Object sender, EventArgs e) {
Console.Out.Write('B');
}
...
EventHandler eh = null;
eh += A;
eh += A;
eh += B;
eh += A;
eh -= A;
eh(null, EventArgs.Empty);
The output is "AAB", so we can conclude, that the last delegete is removed
In the context of event subscription, two delegates are the same if:
1. They point to the same static method,
or:
2. They point to the same instance method of the same class instance.
So, in your question, X must meet this condition. It doesn't matter which one of these X are removed. The point is, after X was subscribed 3 times, this method is called 3 times, when event is raised. After X is unsubscribed once, it is called 2 times, when event is raised.
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.
I've set an eventhandler to an event like this:
frm.FormClosed += (sender, args) =>
{
if (this.myGrid.Enabled)
{
this.myGrid.Select();
}
};
frm.Show();
I want to hang out the eventhandler after the form was closed.
Can you help me?
I want to hang out the eventhandler after the form was closed.
I assume you want to remove it.
Not necessary, don't waste time on it. When the Form is closed (and Disposed), the eventhandler will be collected too. It is a member of the same Form, that follows from the word this in the code.
If you still want to remove it, you will need a copy:
FormClosedEventhandler closeHandler; // class member
closeHandler = (sender, args) =>
{
if (this.myGrid.Enabled)
{
this.myGrid.Select();
}
};
frm.FormClosed += closeHandler ; // OnLoad
...
frm.FormClosed -= closeHandler ; // OnClose
If you mean how to remove you event handler from the event, then you won't be able to use an anonymous delegate but you can create a method with the same parameters and same code and then:
private void EventHandler(object sender, FormClosedEventArgs e)
{
if (this.myGrid.Enabled)
{
this.myGrid.Select();
}
}
frm.FormClosed += EventHandler; // Attach the event handler
frm.FormClosed -= EventHandler; // Remove the event handler
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;