There are tons of questions on StackOverflow asking how to hide Form1 and show Form2. And, usually, a few different answers crop up:
1)
// Program.cs
Application.Run(new Form1());
// Form1.cs
Form2 form2 = new Form2();
form2.Show();
this.Hide();
2)
// Program.cs
Form1 form1 = new Form1();
Form2 form2 = new Form2();
form1.Show();
form2.Show();
Application.Run();
...etc..
I'm not looking for a simple disposable solution like #1. I'm looking for best form management practices. An application with 5-8 forms, opening and closing one another frequently - what's the best way to manage these forms?
My idea was to make each form a (lazy?) Singleton and bury them in a FormsManager
class of some sort (like solution #2 but ++). And then individual forms might call something like FormsManager.GetForm<WelcomeDialog>()
.
But I was wondering what people with more experience used. Again, these solutions shouldn't be quick hacks. They should be design-oriented, maybe architectural, and long-term solutions.
Edits:
This is a pretty generic question (so the requirements are pretty open) for anybody who might have the same trouble. Specific to my situation though, I don't need multiple forms shown at startup. Also, I have no MDI forms. I may have a few modal forms, but they are mostly non-modal.
Click on the tab for the second form (the subForm) in your design and double click on the button control to display the Click event procedure. Press F5 to build and run the application. Click on the button in the main form to display the sub form. Now, when you press the button in the sub form, the form will be hidden.
Open a new form and close the current, without hiding and closing the application. Form2 nextForm = new Form2();<br /> this. Hide();<br /> nextForm.
In order to totally close a C# application, including the hidden forms, you can use the following command in the event code of the “Exit” control: Application. Exit(); Application.
I'm answering in a general manner here.
I don't think a singleton pattern would fit well with form management. Generally, you want to pass some context parameter to the form, and you might want to open multiple instances of the same form. So a singleton doesn't fit well IMO.
I think form management should be simple.
For instance, if you want to display a modal form from another form, I would write something really straightforward:
private void button1_Click(object sender, EventArgs e)
{
using (ModalForm1 frm = new ModalForm1(myParam))
{
frm.ShowDialog();
if (frm.MyResultProperty == ...)
{
// Do some job here
}
}
}
Of course you could write some interface/generics syntax to avoid a little code duplication in case you want to display a lot of modal forms:
public interface IFormResult<T>
{
T Result { get; set; }
}
public class ModalForm1 : Form, IFormResult<string>
{
public ModalForm1()
{
InitializeComponent();
this.Result = "My result";
}
public string Result { get; set; }
}
private void button1_Click(object sender, EventArgs e)
{
string res = ShowModalForm<ModalForm1, string>();
}
private static T2 ShowModalForm<T1, T2>()
where T1 : Form, IFormResult<T2>, new()
{
using (T1 form = new T1())
{
form.ShowDialog();
return form.Result;
}
}
But honestly, I feel like it's a bit overingeneered.
Second point: if your form doesn't exactly follows this specific behavior (ShowDialog()
then a Result
property is set), then you must write another Interface...etc.
If this type of syntax (generics, interfaces...etc.) doesn't reduce the number of lines of code written OR the complexity OR the maintainability (and obviously we can't say it's really the case here), then it's pretty useless IMO.
Edit:
Form management really depends on your use case.
FormManager
concept (or better: think about how to improve the user experience by reducing the number for possible opened forms)Generally, the form that is used to start the application (i.e. the form that stops the program when closed, which is the form that is a parameter of Application.Run()
) is responsible of other forms. You have one main form, and multiples child forms. If your case is really different, then there is probably something smarter to write, but it'll depend on your case. I don't think one can provide a general good answer to the general problematic of form management.
Honestly, if you want something really maintainable, try to reduce (as much as possible) the number of forms that can be shown at the same time. Multiple displayed modeless forms at the same time doesn't offer a good user experience in most cases, and form lifetime management can be problematic if forms are dependent on each other.
In anything other than the most straightforward scenario -- a single main form running for the lifetime of the application, with short-lived child forms -- it is recommended to create a class that inherits from ApplicationContext
. It isn't that complicated:
class FormManager : ApplicationContext {
//When each form closes, close the application if no other open forms
private void onFormClosed(object sender, EventArgs e) {
if (Application.OpenForms.Count == 0) {
ExitThread();
}
}
//Any form which might be the last open form in the application should be created with this
public T CreateForm<T>() where T : Form, new() {
var ret = new T();
ret.FormClosed += onFormClosed;
return ret;
}
//I'm using Lazy here, because an exception is thrown if any Forms have been
//created before calling Application.SetCompatibleTextRenderingDefault(false)
//in the Program class
private static Lazy<FormManager> _current = new Lazy<FormManager>();
public static FormManager Current => _current.Value;
//Startup forms should be created and shown in the constructor
public FormManager() {
var f1 = CreateForm<Form1>();
f1.Show();
var f2 = CreateForm<Form2>();
f2.ShowDialog();
}
}
and Application.Run
in Program.cs
can use the static instance of FormManager
:
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(FormManager.Current);
}
}
During the lifetime of the application, new forms should be created via CreateForm
, in order to register the onFormClosed
method with the FormClosed
event:
var f3 = FormManager.Current.CreateForm<Form3>();
f3.Show();
var f4 = FormManager.Current.CreateForm<Form4>();
f4.ShowDialog();
If you prefer new Form3();
over calls to FormManager.CreateForm
, you can create a RegisterForm
method on FormManager
:
public void RegisterForm(Form frm) {
frm.FormClosed += onFormClosed;
}
and call RegisterForm
on each new Form
:
var f3 = new Form3();
FormManager.Current.RegisterForm(f3);
var f4 = new Form4();
FormManager.Current.RegisterForm(f4);
(NB. If all your forms inherit from some base class, then instead of manually calling RegisterForm
for each new instance, you could call it in the base class constructor.)
Note that Application.OpenForms
only returns those forms that are currently visible. If the application shouldn't exit as long as there are still hidden forms open, then FormManager
will have to use some collection to keep track of all the forms. That collection will determine whether to quit the application or not.
class FormManager : ApplicationContext {
private List<Form> forms = new List<Form>();
private void onFormClosed(object sender, EventArgs e) {
forms.Remove((Form)sender);
if (!forms.Any()) {
ExitThread();
}
}
public void RegisterForm(Form frm) {
frm.FormClosed += onFormClosed;
forms.Add(frm);
}
public T CreateForm<T>() where T : Form, new() {
var ret = new T();
RegisterForm(ret);
return ret;
}
private static Lazy<FormManager> _current = new Lazy<FormManager>();
public static FormManager Current => _current.Value;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With