I have an application with one main form. In the form I have one object objC of class C. The form gets from objC my control and puts it into panel. The form interacts with objC through methods calling and subscribing to objC’s events.
When I try to close the form by clicking on [X] button or by calling this.Close(), form isn’t closing. It calls handler of FormClosing. In the handler, I call objC.Dispose(). I checked, there are no exceptions generated. In objC.Dispose() I unsubscribe from all form’s event handlers. And I removed my control from panel with this code:
splitContainerMain.Panel2.SuspendLayout();
{
splitContainerMain.Panel2.Controls.Clear();
}
splitContainerMain.Panel2.ResumeLayout();
But it just won’t close. I can try to close as many times as I will, FormClosing event will be repeated, but FormClose will be never generated.
This bug is not reproduced when I don't create a control and add it to the panel. What have I done wrong?
There are few explanations for this. But one, you might have a Validating event handler that is canceling the validation. This will also cancel OnFormClosing. Fix:
void Form1_FormClosing(object sender, FormClosingEventArgs e) {
e.Cancel = false;
}
Btw, there is no point in calling Suspend/ResumeLayout, there is no layout done at form closing time. And calling Controls.Clear() does not actually dispose the controls. Rather nasty behavior that trips up a lot of programmers. Best thing to do is to do nothing, parent controls automatically dispose their children controls. And there is no point in unsubscribing events either, the form object and the objC object only reference each other, no other references exist. The garbage collector knows how to handle that.
This question seems to be releated to : Cleaning objects off a form, Where and When?
which provides two good alternatives for dealing with your object.
Either dispose of it by implementing the Form's Dispose method or
Simply ensure that the control is added to the System.ComponentModel.IContainer components property of the form. This will ensure that it's dispose method will be called when the form is disposed.
Related
We created a new form that we're showing via ShowDialog and added a "Cancel" button to it. Here's how we're opening the form from its parent:
// _modalForm is a class-level instance of our ModalForm class
var result = _modalForm.ShowDialog(this);
MessageBox.Show(result.ToString());
Here's the cancel button's Click event handler in ModalForm.
private void btnCancel_Click(object sender, EventArgs e)
{
Close();
}
In our FormClosing event, we have this code (based on this answer).
private void ModalForm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
Hide();
_parentForm.RefreshData();
}
Surprisingly, when we click the "Cancel" button (or use the "X" button at the top of the form), the FormClosing event is raised twice. Both times the CloseReason is UserClosing.
I double checked to make sure InitializeComponent isn't call twice and that we only subscribe to the event once. btnCancel is not set at the CancelButton property for the form. It also doesn't have DialogResult set in the designer. When I check the return value of ShowDialog though, it is set to DialogResult.Cancel.
Changing btnCancel_Click to just be DialogResult = DialogResult.Cancel instead of Close() and doing nothing except _parentForm.Refresh() in the FormClosing event fixes the issue of the event getting raised twice.
Does anyone know why in this particular scenario the FormClosing event gets raised twice?
That's because hiding a modal form will cause it to close with DialogResult.Cancel as dialog result. So if you call this.Hide() in FormClosing event, the event will be raised again.
Imagine if it didn't close the form, your application had been blocked by a hidden modal form!
Note: The answer describes about the reason of raising the event twice. But as described here and others mentioned, For modal forms (which you showed using ShowDialog), the Dispose method will not be called and the form exists after closing and you can use its properties to get some data or you can show it again. So you don't need to call hide method.
For more information take a look at: Do I need to Dispose a Form after the Form got Closed?
The workaround you mention is unnecessary, because modal dialogs are not disposed when closed. They are designed to keep their data after being closed (since it is required by the caller of the modal dialog), and to be reused as needed.
Just let it close properly. It causes no harm to a modal dialog :)
Note that this also means that unlike normal forms, you must dispose of modal dialogs manually if they are not persistent. In your scenario, this most likely means you'd want to dispose of the dialog when the parent form is disposed (this happens automatically if you added the dialog as a component, but needs to be done manually if you create it yourself).
(using vb.net)
Hi,
In a class, I have a public shared event.
In my form, I have a usercontrol which I "AddHandler" to the shared event. But to avoid memory leaks (since the shared event never gets disposed as long as the application runs) how can I tell my usercontrol to RemoveHandler?
I tried to use the "Disposed" event, but it doesn't always seams like the usercontrol is disposed right away. I want to RemoveHandler as soon as the parent form closes.
What is best practice?
EventHandlerList might be what you need - I use it to remove Click events from Buttons when some of my other controls have updated
http://msdn.microsoft.com/en-us/library/system.componentmodel.eventhandlerlist.removehandler.aspx
I have a program which allows the editing of product information. I noticed that it was not releasing memory after closing the editing forms. After some research I stumbled upon this question which mentions that the problem may be that it is hanging on to event subscriptions.
That made sense to me because this form has about 100+ controls on it, many of which are custom with custom events which are subscribed to by their parent controls. This creates a pretty large hierarchy of event subscriptions. So I looked for a way to release these and found this which allows you to unsubscribe from that event.
The problem is, I have a ton of subscriptions. Do I really have to manually unsubscribe from each event one by one on form close, or is there at least a way to release them in one fell swoop, or loop through them?
Remember this: The object on the LEFT of the += keeps alive the object containing the method on the RIGHT of the +=. That is, the object that raises the event keeps alive the object that handles the event, even if the object (such as a form) that handles the event is disposed.
So the thing you need to ensure is that all the event-raisers have gone away.
However, if all the event-raisers happen to be controls in the same Form class that subscribes to those events, you will not need to manually unhook all the event handlers when the form is closed.
This is because the controls that raise the events to which to form has subscribed have the same lifetime as the form itself.
You only need to worry if you subscribe to events raised by an object that has a longer lifetime than the object that is subscribing. Then the subscribing object (the form) would need to unsubscribe when it (the form) is disposed.
It depends on how long your form and its events will be living.
However, you can loop through your controls within the form, releasing the events.
If you remove a nonexisting event accidentally - don't worry, it won't throw an exception.
For example, this is how to get rid of all your TextBox.KeyDown-Events:
private void frm_FormClosed(object sender, FormClosedEventArgs e)
{
foreach (Control tb in this.Controls)
{
if (tb is TextBox)
{
TextBox tb1 = (TextBox)tb;
tb1.KeyDown -= TextBox_KeyDown;
}
}
Okay, make an example here:
I have UserControl A, UserControl B, UserControl C and one Windows Form.
This Windows Form is only started with UserControl A.
UserControl C has [Next] and [Back] buttons.
Say, UserControl A is declared with an event handler. One of function in UserControl A will actually raise the event call to execute one function at UserControl C.
So, at UserControl C, I have to add with
"UserControlA.OneFunction += this.UserControlC_Function;"
If I click Next button at UserControl C, it will dispose the UserControl A and add new UserControl B to the Windows Form. But I never remove this event handler manually.
One of the function in UserControl A is the caller (where event is declared).
One of the function in UserControl C is the listener.
So, these are my questions:
Should I manually remove the handler before UserControl A disposed?
Will this User Control A dispose automatically remove the handler that declared previously?
Should I add this somewhere?
"UserControlA.OneFunction -= this.UserControlC_Function;"
By convention, we don't. And since no event should be invoked after disposal, there is no need to do so unless the control in question is behaving weirdly.
No. At least there isn't such code as seen from reflector.
You don't need to remove the handlers in this case because neither the form nor its buttons are referenced by code external to the form, and the entire object graph will therefore be garbage collected.
The answer to this post does a really good job explaining when you need to manually remove an event handler and when it is not necessary.
Do I need to remove event subscriptions from objects before they are orphaned?
If the form is released (assuming no other objects has a reference to the objects in question) there's little risk of not removing the event handler, however it's a good idea always to remove the event handler before the object listening can no longer be reach (ie all variables referencing the object i sout of scope) not doing so can create a memory leak.
This is not the case in your situation (if I get what you are describing, code would make it more clear)
The problem would be if you attach a delegate referencing object C to an event on object A and then looses access to C (e.g. assigning a new value to the variable). C would then hang around until A is garbage collected
If the memory lifetime of an event publisher is not limited relative to the useful lifetime of an event subscriber, failure to unsubscribe an event will likely cause a memory leak. Were it not for the unfortunate hassle of doing so, there wouldn't be any reason for an event subscriber that was being disposed not to unsubscribe from all events, and for an event publisher that was being disposed not to nullify all event subscriptions. Since neither C# nor VB provides any convenient means of doing those things, however, one has to balance the hassle of proper subscription handling with the fact that in many situations one can get away skimping on it.
Here is my situation:
I instantiated a c# usercontrol on a main thread, but it is not added to the form.
//global declaration
usercontrol1 datacontrol;
constructor()
{
.......
datacontrol = new usercontrol1();
........
}
I then have an asyhcnronous background thread that fires an event that is handled by the instantiated datacontrol. The event handler has a:
if(InvokeRequired){BeginInvoke(..);}
This should prevent any cross-threaded calls from being made. However when this gets called InvokeRequired is false so the handler is not invoked on the correct thread. So in the handler when I attemped a this.labelname.text ="blah" a cross-thread exception is thrown.
However if I add the control to a panel on the mainform, and remove it, then allow the background thread to fire the event. The handler enters but this time 'InvokeRequired' is set to true so it properly invokes itself in the mainthreads context avoiding the exception.
Can someone explain to me why the act of adding it to a panel then removing it fixes the error?
There is no onLoad events for the form so everything should be properly instantiated without it being drawn.
thanks!
stephanie
This is probably because the handle for the control has not yet been created. If you reference dataform.Handle in your constructor, it should create the handle and set the thread ID appropriately, so InvokeRequired will return true later.
You can also force the creation of a handle with CreateControl, but only if the control is visible.
When you add a Control (or Form) to a parent, it sets of the Creating of WindowHandles. Apparently it is also needed to initialize the Execution context for the InvokeRequired logic.
So, don't assume that a created but never-shown Control or Form behaves 'normally'.