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.
}
CodePudding user response:
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();
}
CodePudding user response:
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.
CodePudding user response:
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 theFormWindowState.Normal
assignment. This is used only in the case you minimize a Form, then right-click on its icon in the TaskBar and selectClose 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., seeobject? sender
). If nullable is disabled or you're targeting .NET Framework, remove it (e.g., change inobject sender
)
CodePudding user response:
Your code is showing the specific example of a "settings" form and one of the easier ways to implement this is to have a member field of your main form: SettingsForm _settings = new SettingsForm()
. Doing it this way means that the app always has access to its member properties and you can show it repeatedly without consequence. You will need to finally dispose the SettingsForm
when the app terminates.
For a minimal example, here is a main form with a settings menu:
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
sort();
settingsMenu.Click = onClickSettingsMenu;
// Dispose the SettingsForm when the MainForm does.
Disposed = (sender, e) => _settings.Dispose();
}
SettingsForm _settings = new SettingsForm();
.
.
.
}
The settings menu click is handled as follows:
public partial class MainForm : Form
{
.
.
.
private void onClickSettingsMenu(object? sender, EventArgs e)
{
if(DialogResult.OK.Equals(_settings.ShowDialog(this)))
{
sort();
}
}
private void sort()
{
switch (_settings.SortType)
{
case SortType.None:
textBoxMultiline.Text =
string.Join(
Environment.NewLine,
_testData);
break;
case SortType.Alphanumeric:
textBoxMultiline.Text =
string.Join(
Environment.NewLine,
_testData.OrderBy(_=>_));
break;
}
textBoxMultiline.Select(0,0);
}
Where
List<string> _testData = new List<string>
{
"occasion",
"twin",
"intention",
"arrow",
"draw",
"forest",
"please",
"shell",
"momentum",
"coalition",
};
Settings Form
public enum SortType
{
None,
Alphanumeric,
}
public partial class SettingsForm : Form
{
public SettingsForm()
{
InitializeComponent();
foreach (RadioButton radioButton in groupSort.Controls)
{
radioButton.CheckedChanged = (sender, e) =>
{
SortType = (SortType)Enum.Parse(
typeof(SortType),
((RadioButton)sender).Text);
};
}
buttonApply.Click = (sender, e) => DialogResult = DialogResult.OK;
AcceptButton = buttonApply;
StartPosition = FormStartPosition.CenterParent;
FormBorderStyle = FormBorderStyle.FixedToolWindow;
}
public SortType SortType { get; set; }
}
The Microsoft documentation for ShowDialog explains how this works.
When a form is displayed as a modal dialog box, clicking the Close button (the button with an X at the upper-right corner of the form) causes the form to be hidden and the DialogResult property to be set to DialogResult.Cancel. 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.