Best practices for multi-form applications to show and hide forms? [closed]

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;
}

Leave a Comment