The first time I open one of my child forms from the main form it opens normally. If I close the first instance of the child form and then reopen it however I get a crash the first time I try to call CreateGraphics() outside of the OnPaint() method. The exception I get is: "Cannot access a disposed object. Object name: 'MyControlClass'."
I've set breakpoints to monitor what's going on. Dispose() is called as expected the first time I close the form. When I start the form the second time MyControlClass's constructor is called, and the Dispose method isn't called prior to the exception. At the point of the exception this is still valid. Because of that I'm wondering if somehow it's actually the static component of MyControlClass that ended up being disposed; not the instance object.
I am creating a new copy of the form each time the button to show it is called. MyChildForm is a member held by my mt parentform and is also used to prevent multiple copies of the form from being opened at once.
ShowMyForm()
{
myChildForm = new myChildForm Form();
myChildForm.FormClosed += myChildFormFormClosed;
myChildForm.Show();
}
private void myChildFormFormClosed(object sender, FormClosedEventArgs e)
{
myChildForm = null;
}
The line of code that crashes: MyControlClass inherits from MyControlClassBase, which in turn inherits from MyControlClassBaseBase. This line of code is triggered by a mouse event in MyControlClassBase and is in MyControlClassBaseBase. The code after this would take a cached image of MyControl, display it using the newly created Graphics object, and then draw an overlay based on the mouse cursor position.
Graphics g = CreateGraphics();
PS Since I'm sure someone will ask: The rube goldberg in question is due to the utter fail that is fake 'transparency' in winforms in any but the most trivial cases and the fact that MyControlClass takes too long to paint to keep up with the mouse cursor; but that's a separate question entirely.
After a form is closed, it is disposed - meaning that it exists just to read fields.
If you want to show the same form again, create another instance or just hide it instead of closing.
MyForm f = new MyForm();
f.Show();
// After closed, it will be disposed.
So we need to do the same steps to show it again:
f = new MyForm();
f.Show();
Now you will get a brand new and identical form.
But to hide it when closed, you might need this code:
private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
Hide();
}
}
Note that it will not work with modal forms. (ShowDialog();)
(Thanks to Sorax) This will also not work with MDI children.
Related
I tried opening a second form using a button on my main form, but when I close the second window, I can't open it again.
I added the following code to my main form:
settings_window secondForm = new settings_window();
private void settings_button_Click(object sender, EventArgs e)
{
secondForm.Show();
}
But when I try to open the second form named settings_window the second time, I get the following error: System.ObjectDisposedException.
I found the following code to fix this but I don't know where to place it:
private void settings_window_FormClosing(object sender, FormClosingEventArgs e)
{
this.Hide();
e.Cancel = true; // Do not close the form.
}
You can avoid storing references of Forms and use a simple generic method that shows a Form when an instance of it already exists or creates a new one (and shows it) when none has been created before:
private void ShowForm<T>() where T : Form, new()
{
T? f = Application.OpenForms.OfType<T>().SingleOrDefault();
if (f is null) {
f = new T();
f.FormClosing += F_FormClosing;
}
f.Show();
BeginInvoke(new Action(()=> f.WindowState = FormWindowState.Normal));
void F_FormClosing(object? sender, FormClosingEventArgs e)
{
e.Cancel = true;
(sender as Form)?.Hide();
}
}
When you need it, call as ShowForm<SomeFormClass>(), e.g.,
ShowForm<settings_window>()
Note:
This code uses a local method to subscribe to the FormClosing event.
You can use a standard method, in case this syntax is not available.
BeginInvoke() is used to defer the FormWindowState.Normal assignment. This is used only in the case you minimize a Form, then right-click on its icon in the TaskBar and select Close Windows from the Menu. Without deferring this assignment, the minimized Form wouldn't show up again.
When the starting Form closes, all other Forms close as well
This code supposes nullable is enabled (e.g., see object? sender). If nullable is disabled or you're targeting .NET Framework, remove it (e.g., change in object sender)
Is secondForm a private field of the main form class?
It should work then.
Alternative solution is to show it as as modal - ShowDialog()
Also if you want to save some data in your second form, just use some data initialization from constructor, then saving to first/parent form.
I think you need to create a new instance of the form each time you want to open it. It will create a new instance of the settings_window form each time the button is clicked.
private void settings_button_Click(object sender, EventArgs e)
{
// Create a new instance of the form
settings_window secondForm = new settings_window();
secondForm.Show();
}
Your code shows a class that you have named settings_window, which gives us a hint about what its intended use might be. In general, for a form that behaves "like" a settings window, that you can call multiple times using the same instance, you can declare a member variable using this pattern:
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
// Provide some means, like a menu or button, to show 'secondForm'
settingsMenu.Click += onClickSettingsMenu;
// Dispose the settings_form when the MainForm does.
Disposed += (sender, e) => secondForm.Dispose();
}
// By instantiating it here, its public default or persisted
// properties are immediately available, for example even
// while the main form constructs and loads the initial view.
settings_window secondForm = new settings_window();
private void onClickSettingsMenu(object? sender, EventArgs e)
{
if(DialogResult.OK.Equals(secondForm.ShowDialog()))
{
// Apply actions using the properties of secondForm
}
}
}
This is suitable for any form when you want to:
Repeatedly show and hide the form (e.g. a "Settings" form where the user can change the options multiple times).
Retrieve the default or the persisted properties of the form from the outset even if it's never been shown.
Use any of the form's public properties (e.g. GameLevel, SortType etc.) at any given moment while the app is running, even if the form isn't currently visible.
Display the form modally meaning that "no input (keyboard or mouse click) can occur except to objects on the modal form".
The reason it works is that calling ShowDialog (unlike calling Show) intentionally does not dispose the window handle, and this is to support of this very kind of scenario. The app is then responsible for disposing the resource when the app closes.
The Microsoft documentation for ShowDialog explains how this works.
Unlike non-modal forms, the Close method is not called by the .NET Framework when the user clicks the close form button of a dialog box or sets the value of the DialogResult property. Instead the form is hidden and can be shown again without creating a new instance of the dialog box. Because a form displayed as a dialog box is hidden instead of closed, you must call the Dispose method of the form when the form is no longer needed by your application.
I'd like to have a welcoming form, named the StartForm. This closes, then opens my MenuForm.
Current problem is: First one stays open, doesn't close to show the second one.
I tried several things, like simply showing and hiding them. I now try Application.run and then hide it. Unfortunately it stays open. And doesnt Application.run the second form.
static void Main()
{
Form StartForm = new Main();
MForm MenuForm = new MForm();
Application.Run(StartForm);
//
Task.Delay(500).ContinueWith((t) =>
{
StartForm.Hide();
Application.Run(MenuForm);
});
}
I expected this to Close the first form after waiting the delay, but it stays open.
Calling Application.Run() causes a new application message loop to begin running on the current thread. If a subsequent call to Application.Run() is made while the first application is running an InvalidOperationException is thrown according to the docs. However, this is not actually possible in your case as your call to Application.Run() is being invoked on the UI thread, which blocks the execution of any following code until it exits.
Instead, what we need is to bootstrap the application by using Application.Run(new Main()) and then inside of the Main form we will handle the displaying and hiding behaviour.
Here is how I would suggest achieving this:
Firstly, in your Main method, replace everything with Application.Run(new Main());.
Then inside of your Main form create a method like the following:
private void Main_Shown(Object sender, EventArgs e)
{
Task.WaitAll(Task.Delay(500));
this.Hide();
var menuForm = new MForm();
menuForm.Closed += (obj, args) => { this.Show(); };
menuForm.Show();
}
And lastly, go into your Main.Designer.cs file and make sure you subscribe the Shown event to Main_Shown method you just created in the InitializeComponent method like so:
this.Shown += new System.EventHandler(this.Main_Shown);
Explanation of what is going on here:
According to the docs the Form.Shown Event gets fired the first time a form is shown to the user. This is perfect for this use case, so we 'hook' into it and respond to it by awaiting a delay of 500 milliseconds.
Afterwards we hide the current (Main) form, create the new (MForm) form and show it. But the more important detail is that we subscribe to that form's Closed event so that when it is closed by the user, the Main form shows itself again.
use timer to Open Second form in Firstform
and Hide Firstform after show Secondform.
like this.
Main
static void Main()
{
Form StartForm = new Main();
Application.Run(StartForm);
}
FirstForm
set timer with interval 5000 and Enable it.
private void timer1_Tick(object sender, EventArgs e)
{
MForm MenuForm = new MForm();
this.Hide();
MenuForm.Show();
timer1.Stop();
}
I am developing a winforms application with the code below to open new form :
using (Form1 f = new Form1(textBoxJobCardNo.Text, tf, tfh))
{
// System.GC.Collect();
f.FormBorderStyle = FormBorderStyle.None;
f.Left = sc[1].Bounds.Left;
f.Top = sc[1].Bounds.Top;
f.Height = sc[1].Bounds.Height;
f.Width = sc[1].Bounds.Width;
f.StartPosition = FormStartPosition.Manual;
labelerror.Visible = false;
textBoxJobCardNo.Clear();
f.ShowDialog();
}
I am using ShowDialog() to open new form. In place of ShowDialog() I want to use Show() without removing using statement because the using statement helps me to free memory after form close. If I use Show() then it will not hold on after Show() and moves out of using scope, which will close the form.
When i am using using my ram usage is constant when i am trying to do it with show without using it increase 2 mb on every form open.
Can't i hold control in using like show dialog with show.
If you don't show the form modally (using ShowDialog()), by default, it'll be disposed on closing. This is what the MSDN says:
When a form is closed, all resources created within the object are closed and the form is disposed. You can prevent the closing of a form at run time by handling the Closing event and setting the Cancel property of the CancelEventArgs passed as a parameter to your event handler. If the form you are closing is the startup form of your application, your application ends.
The two conditions when a form is not disposed on Close is when (1) it is part of a multiple-document interface (MDI) application, and the form is not visible; and (2) you have displayed the form using ShowDialog. In these cases, you will need to call Dispose manually to mark all of the form's controls for garbage collection.
So there's no need to call Dispose() (or use a using block) if the form is not modal and not MDI, it'll be freed when closed.
That said, this is an implementation detail that might change (although at this point in time, it's unlikely that it will), and it should be safe to call Dispose() multiple times if you want.
Put this line of code in the FormClosed Eventhandler of the form
this.Dispose();
aka:
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
this.Dispose();
}
The using block does nothing more than call dispose() on the resource you specify after the block ended. If you handle Disposing your Resources yourself you dont have to use the using block and your Resources are freed the way you specify, in this example after the Form has been closed.
Edit:
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
this.Dispose();
}
Form.Show() should be called without a using block because it will return immediately after showing the form, so that the user can access the new form and all other forms that are present. In a using block the newly shown form would be destroyed immediately after creation because of the automatic Dispose() call at the end of the block.
Form.ShowDialog() returns much later: after the Modal form is closed, so this will work fine in a using block.
I have a windows forms question:
Program.cs:
Application.Run(new frmStart());
frmStart: on btnLoad_Click,
frmLoad formLoad = new frmLoad();
formLoad.Show();
this.Hide(); // if I do a this.Close(); after it shuts down and doesn't get to show the form
frmLoad: on btnCancel_Click:
Application.Exit();
// or this.Close();
// or even: base.Close();
The form disappears but the program doesn't end, I still have to press the blue "Stop Debugging" to make it stop.
I have been looking... I know it is possible to make the program really stop, and not just freeze when you close the second form, even if you don't keep the first form on the screen, but can't remember and can't figure out how.
Ack, -1 on Application.ExitThread!
The issue is that you haven't closed the main form. The simplest way is to hook onto the 2nd form's Closed event and have it close the main form. For example the code to open the 2nd form changes to:
var newForm = new frmLoad();
newForm.FormClosed += (closedSender, closedE) => Close();
newForm.Show();
Hide();
This essentially sets up so that when the frmLoad form closes, the main form calls it's Close() method. I used a Lambda expression for the event handler, but you can just as easily create a private method accepting an (object sender, EventArgs e) and point .FormClosed at it.
*Edit: Sorry, missed that you only want to close on certain state. In which case on your frmLoad, create a public property such as:
public bool UserCancelled
{
get;
private set;
}
where the Cancel button sets this to True before closing the form. Your event handler in the main form changes to:
var newForm = new frmLoad();
newForm.FormClosed += (closedSender, closedE) =>
{
if (newForm.UserCancelled)
Close();
};
newForm.Show();
Hide();
In frmStart add:
public static frmStart Current;
Then in the constructor add:
Current = this;
Then in frmLoad: on btnCancel_Click:
frmStart.Current.Close();
You really should call Close() on both. That's the only clean way as otherwise the first form never is told to close down and doesn't clean up.
You may know that it's safe to do this, but someone else working on the code later may add code in the OnClose in the first form that they need called. They will say not nice things about you when they finally figure out why their code is not called.
If you close both, then your app will exit.
Please use the Application.ExitThread() method, the method exits the message loop on the current thread and closes all windows on the thread.
http://msdn.microsoft.com/en-us/library/system.windows.forms.application.exitthread
I vote yuck on all the answers. Override Form Closing in your primary form and close the secondary form first.
What I need is such an event handler in my window class.
void someEventHandler(object sender, RoutedEventArgs e)
{
MyNewWindow mnw = new MyNewWindow();
mnw.Owner = Window.GetWindow(this);
mnw.ShowDialog();
this.Close();
}
Window.GetWindow(this) returns the parent window of the current window.
I had thought when the owner of the new window is the parent window of the current one, it would wait for the parent; and not the current one. But it did not work that way. Current window waits for the execution of the new and closes only after.
If I use Show() instead of ShowDialog() for some reason the window is not shown at all.
Probably I need some delegate methods but I am not sure where to start.
Edit: I guess I need to improve the question for future references:
The new window should be a dialog to the parent window. If I use Show() the parent window becomes accesible and I dont want that. If I use ShowDialog() it becomes a dialog to the current window, meaning that the current window does not close until the new window is closed, and I dont want that either.
Closing a window causes any windows that it owns to be closed.
If you just want the owner window to not be visible, try this;
void someEventHandler(object sender, RoutedEventArgs e)
{
MyNewWindow mnw = new MyNewWindow();
mnw.Owner = this;
this.Hide(); // not required if using the child events below
mnw.ShowDialog();
}
You'll likely want to hook up some event in the parent window that acts accordingly when you close the child window depending on your requirements.
EDIT
You could perhaps control the hiding of the (multiple parents) from the child;
void OnLoad(object sender, RoutedEventArgs e)
{
this.Owner.Hide();
}
void Closed(object sender, RoutedEventArgs e)
{
this.Owner.Show();
}
If I understand what you're trying to do you want to close the current window and replace it with a MyNewWindow that is a child of the window that was the original window's parent (probably the app's main window).
To do that you should be using:
mnw.Owner = this.Owner;
instead of calling GetWindow, which gives you back the current Window instance. The purpose of GetWindow is to obtain the Window which contains some other UIElement, like a Button farther down the tree. When passing in a Window instance, you just get back what you passed in.
Calling ShowDialog() blocks. That means, the method returns only when mnw is closed and only then is the original window closed.
If you change that to Show(), mnw is closed as soon as you call Close() on the original window. because Window.GetWindow(this) returns this. Thus, you set the owner to this and when you close this, mnw gets closed too. That leaves us with:
MyNewWindow mnw = new MyNewWindow();
mnw.Owner = this.Owner; // may not be necessary
mnw.Show();
this.Close();
This code seems to work for me.