I have a UserControl-derived control the displays some information fetched from a web server. I'm currently in the process of making the initialization of the control asyncronous, to improve responsiveness.
In my Load event handler, I'm creating a CancellationTokenSource, and using the associated Token in the various async calls.
I now want to ensure that if the user closes the form before the async operation completes, the operation will be cancelled. In other words, I want to call Cancel on the token.
I'm trying to figure out where to do this. If there was an Unload event that I could trap, then that would be perfect - but there isn't. In fact, I can't find any event that looks suitable.
I could trap the close event for the containing Form, but I really wanted to keep everything local to my UserControl.
Suggestions?
I suggest the Control::HandleDestroyed event. It is raised, when the underlying HWnd is destroyed (which usually happens, when the parent form is closed). To handle it in your own UserControl, you should override OnHandleDestroyed.
You have full access to the Control's properties at this moment, because it is not yet disposed of.
Another solution
protected override void OnParentChanged(EventArgs e)
{
base.OnParentChanged(e);
if (parentForm != null)
{
parentForm.Closing -= parentForm_Closing;
}
parentForm = FindForm();
if (parentForm != null)
parentForm.Closing += parentForm_Closing;
}
void parentForm_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
parentForm.Closing -= parentForm_Closing;
parentForm = null;
//closing code
}
Why not just use the Disposed event?
When a form is closing, it will call Dispose on itself and all child controls will be disposed recursively as well.
Try this:
UserControl.Dispose();
Related
if a window with usercontrol is getting closed, then i should call a method in the usercontrol i.e. nothing but when usercontrol is disposed. How do i do that?
If you want to know when the garbage collector collects the UserControl use this:
~UserControl1()
{
//...
}
If you want to know when the UserControl is unloaded from its parent, use Unloaded event on the userControl
note: unlike a Window, a UserControl can't get closed.
As contols do not have a Closing/Closed event it's not that simple as overriding Dispose. Dispose may not be called under some circumstances.
You might override WndProc of the control and catch the WM_CLOSE event.
For future reference for others..
I disposed my user control that was loaded by my main window like this:
<UserControl x:Class="MyApp.Class" ... Unloaded="UserControl_Unloaded"></UserControl>
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
{
((MyVM)this.DataContext).Dispose();
}
Unloaded is an event available on any View.
I have a timer that I created using the Form designer (that makes it a member of the main form window right? ) I stop the timer in a different method in this class. Currently I have it working by passing it into the other method and assiging to t, a private member of this class. I realize that it COULD be null when this happens, but its not when I debug it and its driving me nuts.
System.NullReferenceException was unhandled by user code
Message=Object reference not set to an instance of an object.
private void domDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
// do a doc scan here then remove the handler
var form = Form.ActiveForm as MainWindow;
form.pagesToVisit = docScan(b.Document, this.domain);
if (t != null) { t.Start(); }
// here i need to stop a timer that is a member of the main form
b.DocumentCompleted -= domDocumentCompleted;
}
Note that your code is inside an event. The event may be getting called in a separate thread from the one the main form operates under. See How to: Make Thread-Safe Calls to Windows Forms Controls
for how to get around this.
Also, it's not good practice to remove an event from inside the event.
I have program that opens subwindows inside of it (mdi.parent). I have made component that is in one window under it, however, i want that that window never actually disposed after its created because i want to keep only one instance of it.
This can be made with code:
// This prevents this window disposing from window close button, we want always show one and only
// one instance of this window.
FormClosing += (o, e) =>
{
Hide();
e.Cancel = true;
};
However, after this there is problem, closing program requires pressing close button press twice. First press closes subwindow and second terminates program. How this can be get around?
I am working with Winforms.
As Habib said, you can call Application.Exit, but:
The Form.Closed and Form.Closing events are not raised when the
Application.Exit method is called to exit your application
If this is important to you, you can do something like this (MDI parent code):
private Boolean terminating;
protected override void OnClosing(CancelEventArgs e)
{
if (!terminating)
{
terminating = true;
Close();
}
base.OnClosing(e);
}
Call Application.Exit() in the form close event.
Application.Exit - MSDN
Informs all message pumps that they must terminate, and then closes
all application windows after the messages have been processed.
The code inside of your FormClosing event handler method is a bit too terse. It does its job of preventing the user from closing the form, but as you've also noticed, it prevents you from closing the form programmatically as well.
This is easily solved by testing the value of the CloseReason property of the FormClosingEventArgs that are passed in each time the event is raised.
These will tell you the reason why the form is attempting to close. If the value is CloseReason.UserClosing, then you want to set e.Cancel to true and hide the form. If the value is something else, then you want to allow the form to continue closing.
// This prevents this window disposing when its close button is clicked by the
// user; we want always show one and only one instance of this window.
// But we still want to be able to close the form programmatically.
FormClosing += (o, e) =>
{
if (e.CloseReason == CloseReason.UserClosing)
{
Hide();
e.Cancel = true;
}
};
Use This
Form[] ch = this.MdiChildren;
foreach (Form chfrm in ch)
chfrm.Close();
You can use Application.Exit if there is no processing happening when the application is closed. Otherwise, you can check Application.OpenForms collection in MDI parent's closing event and close all the other forms that are open.
I am creating a window object globally and display it only when it is necessary, most of the time window will be in invisible mode. I have some work to do whenever the window is visible. can any one please tell me which message should i override to put the code which is supposed to execute when a window.show method is called?
IsVisibleChanged should do what you want.
private void Window_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == true)
{
//Do what you need here
}
}
Look at the Window class documentation and also the Window lifetime cycle. Now out of this, we can conclude that you (probably) need the IsVisibleChanged event.
I need to change a certain DataGridView's property (a DataSourceUpdateMode for one of its binding) only when ALL of its initial data bindings are completed.
I tried subscribing to the "DataBindingComplete" event, but it's fired too many times (one or more time for each binding associated to the control); what I need is a more global "AllDataBindingsComplete" event, fired when the control is ready to be displayed to the user.
As a temporary workaround, I'm using the MouseDown event (I've assumed that when the user is able to click the control, it means that the control is displayed... :) and the events I'm playing with - SelectionChanged - are fired after the MouseDown):
protected override void OnMouseDown(MouseEventArgs e)
{
Binding selectedItemsBinding = this.DataBindings["SelectedItems"];
if (selectedItemsBinding != null)
{
selectedItemsBinding.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;
}
base.OnMouseDown(e);
}
It works, but it smells like an ugly hack A LOT (and it's called too many times, only one time is enough for my needs).
Is there a better way?
(yes, I'm trying to adopt MVVM in a Windows Forms project, and I've added a bindable "SelectedItems" property to the DataGridView...)
What I've done at the Windows Forms form level, and may be improvised down to just the control(s) you want, is to subclass the Windows Forms baseclass into my own. Then, in its constructor, attach an extra event call to the Load() event.
So when everything else is completely loaded, only THEN will it hit my custom method (of the subclass). Since it is the bottom of the call-stack chain being attached to the event queue, I know it's last and everything else is done... Here's a snippet of the concept.
public class MyForm : Form
{
public MyForm()
{
this.Load += AfterEverythingElseLoaded;
}
private void AfterEverythingElseLoaded(object sender, EventArgs e)
{
// Do my own things here...
}
}
This concept can be applied to the Init() function too if that's more appropriate for your control... Let everything else within it get initialized(), then do you the "AfterInitialized()" function.