When using the FormClosing event, why does the code e.Cancel = true; work, but new CancelEventArgs().Cancel = true; does not work?
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
new CancelEventArgs().Cancel = true;
}
The event is raised by the Winforms plumbing code. The only way it can see that the custom event handler wants to alter the default behavior is through the e object. Creating a new CancelEventArgs object has no side-effects that the plumbing can detect.
There's something else wrong, events are raised for the benefit of external code, letting it know what's going on and giving it an option to alter behavior. There is no external code here, the event handler is actually part of the same class that raises the event. In other words, the form is listening to its own events. There's a much better way to deal with that, you override the method that raises the event. Like this:
protected override void OnFormClosing(FormClosingEventArgs e) {
e.Cancel = true;
base.OnFormClosing(e);
}
Now external code can override the default behavior, events are raised after the OnXxxx method runs. And you have a choice, if you do not want the external code to override the behavior, simply swap the two statements.
I think the code is doing exactly what it says; what's missing is a literal reading of it.
When you assign a new value to e.Cancel, you are modifying the e that is provided as a parameter to the function. After the event handler function finishes, this FormClosingEventArgs instance, including any changes made to it from within the event handler, will be available to whatever code invoked the event handler. In this case, that's almost certainly the Winforms code written by Microsoft.
On the flip side, when you inside that event handler create a new instance of the type FormClosingEventArgs and do something to it, there is nothing to provide that information back to the caller; you'd need something explicit for that. Since the caller is looking at the value of the parameter it passed in once the event handler completes, you'd need to somehow replace the content of e as seen by the caller with the newly created instance. In other cases such a result might be provided as a return value.
In general, the result of new T(), for some type T, is an instance of type T. You can thus work with the result of the expression new T() as you would a non-null variable of type T. In your particular case, you're assigning a value to a property on type T (specifically, the instance of that type thus created). (There is the special case where the constructor fails, but let's not go there for now; for simple types, that would pretty much mean that you are in such dire straits that your program is unlikely to be able to continue running in any case.)
What's important here is that if you don't assign the result of the expression new T() itself anywhere, the newly created instance is thrown away (technically, becomes inaccessible) once the statement completes. Then at some later point, the .NET garbage collector kicks in and actually reclaims the memory that was allocated. It isn't really any different from allocating a variable in one function, calling that function from another function and trying to access the variable thus allocated from the second function without doing anything to transfer the variable from the first function to the second, except here only one function is involved.
Doing something like your second line of code in the event handler would be rather unusual, but can in principle be valid if invoking the constructor has some side effect that you're looking to make use of, such as triggering lazy loading.
This code surely work just check it
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
if (PreClosingConfirmation() == System.Windows.Forms.DialogResult.Yes)
{
Dispose(true);
Application.Exit();
}
else
{
e.Cancel = true;
}
}
private DialogResult PreClosingConfirmation()
{
DialogResult res = System.Windows.Forms.MessageBox.Show(" Do you want to quit? ", "Quit...", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
return res;
}
Happy Coding
It's because the CancelEventArgs is an object reference passed to the event handler in your code. The behind the scenes code raises FormClosing with a CancelEventArgs object and all the event handlers receive that same object. Once all the handlers have had their turn, the behind the scenes code checks the CancelEventArgs object it sent to see if its Cancel property is set to true. If it is, it doesn't do anything, and the FormClose chain of events stops. If Cancel is false (CancelEventArgs default value), meaning it hasn't been set to Cancel the chain of events, the behind the scenes code proceeds along, and then raises the FormClosed event.
You can read more at Form.FormClosing Event on MSDN.
In Forms all the -ing events are typically followed by an -ed event. The -ing events usually have CancelEventArgs, which can have its Cancel property set to true to stop the -ed event from happening.
Related
As opposed to the FormClosing event, which can be fired multiple times in a slow-to-respond app with a user jumping on the close button repeatedly.
Is it possible for the FormClosed event to fire multiple times?
Eg, is the following try/catch necessary?
private void MyForm_FormClosed(object sender, FormClosedEventArgs e)
{
// Dispose the CancellationTokenSource
// Is it possible for this to be fire more than once!?
try
{
cts.Dispose();
cts = null;
}
catch { }
}
Calling cts.Dispose(); by itself would be absolutely fine, setting it to null is redundant and doesn't help the garbage collector, and try catch is overkill.
A CancellationTokenSource.Dispose just takes care of a timer and a handle to a kernel event (ManualResetEvent), both are cleaned up in a thread safe manner and both implementation will handle being called twice and are fault tolerant.
Depending on how you instantiated the CancellationTokenSource you could also use the null conditional operator for good measure.
Example
private void MyForm_FormClosed(object sender, FormClosedEventArgs e)
=> cts?.Dispose();
It's worth noting CancellationTokenSource checks if it has been previously disposed anyway.
protected virtual void Dispose(bool disposing)
{
if (disposing && !_disposed)
{
...
As to whether a Form OnClose event can be called twice, I haven't deviled into the code, yet my assumption is that it will work on the message pump, and unless you call the event yourself it will only be called once. Even if otherwise, you are covered anyway
Well, it is always possible to add the same event multiple times to the same handler, in which case it would be called multiple times!
But outside of, this I can't find any documentation which guarantees that it will only be called once, but looking at the WInform lifecycle https://learn.microsoft.com/en-us/dotnet/desktop/winforms/order-of-events-in-windows-forms?view=netframeworkdesktop-4.8
I would say that you can be pretty sure that it should be only called once.
However, I would still employ safe coding practices and keep the try catch, or use the null conditional operator.
If you don't catch an exception which, such as this, is harmless, and/or have (or later add) a global exception handler then it will catch and handle such things which could be totally unnecessary and clog up your logs files with what is effectively junk. (I have seen this too often)
Let's assume that inside a Windows Form, I start a long-running task like this:
ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateDailyTasksChart));
Then, this function runs for a while, but I close the window before it finishes.
private void UpdateDailyTasksChart (object param)
{
//Call Data Access Layer, go get MySQL Data. But I close the Form.
UICallback(chartData); //The Form is closed when this is executed.
}
And now here's what UICallback does:
private delegate void UICallbackDel(object chartData);
private void UICallback (object chartData)
{
if (InvokeRequired)
{
this.Invoke(new UICallbackDel(UICallback), chartData);
}
else
{
aButtonOnMyForm.Visible = false; //But the Form has been closed!
}
}
Curiously, this code doesn't crash.
I placed breakpoints in the Form_Closed event and it does execute. I haven't checked if the Form still exists by, for example, declaring it with a class variable. But my guess is that it does.
So the question is: the GC will only collect the Form when my thread finishes? Or what does it happen?
There are two points here:
If you explicitly dispose of the form you may expect to raise an ObjectDisposedException when the work item delegate executes.
Your work item delegate holds a reference to the form through this, InvokeRequired (which in turn references this) and aButtonOnMyForm. Therefore, your form is ineligible for garbage collection as long as the ThreadPool thread has not completed execution. That's why the code doesn't crash. (Note: this is always implicitly accessible regardless.)
Another thing to derive from this is that it is generally wise to unregister external event handlers from a form before that form closes, lest you create memory leaks. The big gotcha there is if you register a lambda expression or anonymous delegate, you need to save off a reference to it in order to unregister later. Simply copying and pasting the same code would not work.
You may want to take a look at this post.
Basically, you may or may not get an ObjectDisposedException depending on what the GC is doing at the time you are trying to Invoke on the form. You can try and catch it but it cannot be tested reliably, so that is more of a hack than anything.
The garbage collector doesn't care whether an object is “closed”, or “disposed” or anything like that. It only cares whether the object is still accessible. And in your case, the form is still accessible by using this (implicitly or explicitly). That's why your application doesn't crash.
Of course, if an object is closed or disposed or something like that, it's within its rights to throw ObjectDisposedException, or similar for any method you call on it. But it certainly doesn't have to do that.
I've just encountered a bug in the program I'm writing where an exception was thrown stating an "object reference must be set to an instance of an object". Upon investigation, I found that this exception was thrown when trying to fire an event BUT the event didn't have any delegate methods added to it.
I wanted to check that my understanding was correct that as a developer you shouldn't fire events without first checking that the event doesn't equal null? For example:
if (this.MyEventThatIWantToFire != null)
{
this.MyEventThatIWantToFire();
}
Thanks in advance for the advice/thoughts.
The pattern you've shown is broken in a multi-threaded environment. MyEventThatIWantToFire could become null after the test but before the invocation. Here's a safer approach:
EventHandler handler = MyEventThatIWantToFire;
if (handler != null)
{
handler(...);
}
Note that unless you use some sort of memory barrier, there's no guarantee you'll see the latest set of subscribers, even ignoring the obvious race condition.
But yes, unless you know that it will be non-null, you need to perform a check or use a helper method to do the check for you.
One way of making sure there's always a subscriber is to add a no-op subscriber yourself, e.g.
public event EventHandler MyEventThatIWantToFire = delegate {};
Of course, events don't have to be implemented with simple delegate fields. For example, you could have an event backed by a List<EventHandler> instead, using an empty list to start with. That would be quite unusual though.
An event itself is never null, because the event itself is just a pair of methods (add/remove). See my article about events and delegates for more details.
You shouldn't do it that way - if someone where to remove a listener between the if and the call to the event, you'd get an exception. It is good practise to use it with a local variable:
protected void RaiseEvent()
{
var handler = Event;
if(handler != null)
handler(this, EventArgs.Empty);
}
Of course, this has the disadvantage of maybe calling a listener that already was removed between the creation of the local variable and the call to the handler.
Yes, you should check it for null but the way you're doing it leads to a race condition. It's possible the event may end up being null anyway if someone unsubscribes the last delegate in between your "if" and your event raise. The accepted way to do this is like:
var temp = this.MyEventThatIWantToFire;
if (temp != null) {
this.temp(this, EventArgs.Empty);
}
or alternatively, ensure there's always at least one delegate in the invocation list by declaring your event like this:
public event EventHandler MyEventThatIWantToFire = delegate {};
Make sense?
Yes, your understanding is correct. It's up to you to check the delegate's value before calling it.
Most delegates - which events are - are "multi-cast" delegates. This means that a delegate can manage a list of one or more methods that it will call when activated.
When the delegate evaluates to a null value, there are no registered methods; therefore, there is nothing to call. If it evaluates to something other than a null value, there is at least one method registered. Calling the delegate is an instruction to call all methods it has registered. The methods are called in no guaranteed order.
Using the addition-assignment operator (+=) or addition operator (+) adds a method to the delegate's list of methods. Using the subtraction-assignment operator (-=) or subtraction operator (-) removes a method from the delegate's list of methods.
Also note that the called methods are executed from the caller's thread. This is important if you are using events to update your user interface controls. In this situation, use of your control's InvokeRequired property lets you handle cross-threading calls (using Invoke() or BeginInvoke()) gracefully.
Yes in C# if an event has no delegates, it will be null, so you must check
Yes, you must check whether event is not null. Mostly you encounter protected method that does the check and invokes event, or even constructs event args. Something like this:
public event EventHandler Foo;
protected void OnFoo() {
if (Foo == null)
return;
Foo(this, new EventArgs());
}
This approach also alows your derived classes to invoke (or prevent invoking) the event.
Ok,
So I have a method which fires when someone clicks on our Icon in a silverlight application, seen below:
private void Logo_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
ShowInfo(true);
DispatcherTimer autoCloseTimer = new DispatcherTimer();
autoCloseTimer.Interval = new TimeSpan(0, 0, 10);
autoCloseTimer.Tick +=new EventHandler((timerSender,args) =>
{
autoCloseTimer.Stop();
ShowInfo(false);
});
autoCloseTimer.Start();
}
Whats meant to happen is that the method ShowInfo() opens up a box with the company info in and the dispatch timer auto closes it after said timespan. And this all works...
But what I'm not sure about is because the dispatch timer is a local var, after the Logo_MouseLeftButtonUp method finishes, what is there to keep the dispatch timer referenced and not availible for GC collection before the anonymous method is fired?
Is it the reference to the ShowInfo() method in the anonymous method?
Just feels like some thing I should understand deeper as I can imagine with using events etc it can be very easy to create a leak with something like this.
Hope this all makes sense!
Andy.
The DispatcherTimer registers itself with the Dispatcher by calling the internal Dispatcher.AddTimer method when you call Start.
Since it also unregisters itself by calling Dispatcher.RemoveTimer when you call Stop, you won't leak memory.
The Timer keeps the anonymous method alive in its Tick event, which also keeps the variables in the method alive through the closure.
One of the more obscure causes of memory leaks in .NET is event handlers. An event handler is a reference to an object, and keeps the object in scope. When you're done with an event handler, it needs to be dropped. If the event handler is used only once, it can deregister itself, but if it gets used more than once, there will need to be some other object that knows when it's no longer useful, and drops the event handler.
I have the following function.
What it does is, given a Control (most likely a windows form) i want to have all of the controls contained that "obey" the Rules ( a function screening the controls i want ) subscribe to an event (lets say KeyDown).
The question is: how do i unsubscribe? Or more importantly, do i need to?
Since i will be using this one on the Load event of forms on the form itself will i really need to unsubscribe if the form closes?
(after some light reading and a little understanding of the GC i suspect i don't need to unsubscribe but I'm not sure)
//an example of using the function
private void Form1_Load(object sender, EventArgs e)
{
MyEventHandler.CreateKeyDownEventHandlers(this);
}
//the function
public static void CreateEventHandlers(Control Ctrl)
{
foreach (Control c in Ctrl.Controls)
{
//bool Rules(Control) a function that determines to what controls'
//events to apply the handler
if ( Rules(c) )
{
c.KeyDown += (s, e) =>
{
// do something
};
}
//a control might be a groupbox so we want their contained
//controls also
if (c.Controls != null)
{
if (c.Controls.Count > 0)
{
CreateEventHandlers(c);
}
}
}
}
This is my first try with events, delegates, anonymous functions and lambdas so if i did something really stupid tell me.
First, I think you cannot unsubscribe an anonymous function unless it's assigned to a handler variable and that variable is addded to and then removed from the event.
Whether you need to unsubscribe: Think about the object lifetimes. You create anonymous functions in a static method and attach the to controls of which I assume you control the lifetimes.
When you dispose one of these controls, they will no longer be referencing the anonymous functions and the GC can kill them (the anonymous functions), so you don't need to unsubscribe.
If the situation was reversed and something that was created in the static method referenced the controls, as if a control delegate was added to an event in the static context, then the GC couldn't take care of the controls until the reference to them was removed, which wouldn't happen if it was done in the static method.
If you are creating the Form once, and these handlers also once at the beginning, then you don't really need to clean anything.
If you create it multiple times though (e.g. you create the form many times when the user clicks on a button), then you need to be careful. And here the answer depends on what exactly is in the handlers:
c.KeyDown += (s, e) =>
{
// do something
};
In general assigning a delegate to an event can cause a dependency cycle from GC's point of view, e.g. imagine that a Form contains control A, and registers to an event on A. Then the form cannot be disposed until A is disposed, and A cannot be disposed until the form is disposed (because it references the form indirectly through the callback). If you only create the form together with control A then its ok (GC will get rid of both at the same time), but when you create controls A dynamically then you can end-up with memory leak.
You can unsubscribe from an event by using
yourobject.Yourevent-= YourSubscribedFunction;
This will unsubscribe this function from the event.
About the second part of your question:
You do not need to unsubscribe if the object containing the event is destroyed.
I am not sure what happens if the subscribing object is disposed but my tests indicate that the function is still called, eventhough the object no longer exists.
ClassA a = new ClassA();
using (ClassB b = new ClassB()) // implements IDisposable
{
b.SubscribeToFoo(a); // b subscribes to FooEvent of ClassA
a.DoFoo(); // a executes FooEvent
}
GC.Collect(); // Run Garbage Collector just to be sure
a.DoFoo(); // a executes FooEvent
The subscribed method of ClassB is called, eventhough b is disposed.