多表单应用程序显示和隐藏表单的最佳实践

本文关键字:表单 最佳 应用程序 显示 隐藏 | 更新日期: 2023-09-27 18:26:04

StackOverflow上有很多问题询问如何隐藏Form1和显示Form2。通常,会出现一些不同的答案:

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..

我不是在寻找像#1那样简单的一次性解决方案。我正在寻找最佳的表单管理实践。一个有5-8个表单的应用程序,经常打开和关闭彼此-管理这些表单的最佳方式是什么?

我的想法是让每个表单都成为一个(懒惰的?)Singleton,并将它们埋在某种类型的FormsManager类中(类似于解决方案#2,但++)。然后单个表单可能会调用类似FormsManager.GetForm<WelcomeDialog>()的东西。

但我想知道有更多经验的人会用什么。同样,这些解决方案不应该是快速破解。它们应该是面向设计的,可能是architectural长期解决方案

编辑:

对于任何可能有同样问题的人来说,这是一个非常通用的问题(因此需求非常开放)。但具体到我的情况,我不需要在启动时显示多个表单。此外,我没有MDI表格。我可能有一些模态形式,但它们大多是非模态的。

多表单应用程序显示和隐藏表单的最佳实践

在最简单的场景之外的任何其他场景中——一个在应用程序生命周期内运行的主窗体,带有短暂的子窗体——建议创建一个从ApplicationContext继承的类。没有那么复杂:

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

CCD_ 6中的CCD_ 5可以使用CCD_

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

在应用程序的生命周期内,应通过CreateForm创建新表单,以便使用FormClosed事件注册onFormClosed方法:

var f3 = FormManager.Current.CreateForm<Form3>();
f3.Show();
var f4 = FormManager.Current.CreateForm<Form4>();
f4.ShowDialog();

如果您更喜欢new Form3();而不是对FormManager.CreateForm的调用,则可以在FormManager:上创建RegisterForm方法

public void RegisterForm(Form frm) {
    frm.FormClosed += onFormClosed;
}

并在每个新的CCD_ 16:上调用CCD_

var f3 = new Form3();
FormManager.Current.RegisterForm(f3);
var f4 = new Form4();
FormManager.Current.RegisterForm(f4);

(注意。如果所有表单都继承自某个基类,那么您可以在基类构造函数中调用它,而不是为每个新实例手动调用RegisterForm。)


请注意,Application.OpenForms只返回那些当前可见的表单。如果只要仍有隐藏的表单打开,应用程序就不应该退出,那么FormManager将不得不使用一些集合来跟踪所有表单。该集合将决定是否退出应用程序。

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

我在这里以一般的方式回答。

我认为单例模式不太适合表单管理。一般来说,你想把一些上下文参数传递给表单,你可能想打开同一表单的多个实例

我认为表单管理应该很简单。

例如,如果你想显示另一种形式的模态形式,我会写一些非常简单的东西:

private void button1_Click(object sender, EventArgs e)
{
    using (ModalForm1 frm = new ModalForm1(myParam))
    {
        frm.ShowDialog();
        if (frm.MyResultProperty == ...)
        {
            // Do some job here
        }
    }
}

当然,如果你想显示很多模态形式,你可以编写一些接口/泛型语法来避免一些代码重复:

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

但老实说,我觉得这有点过头了。

第二点:如果您的表单没有完全遵循此特定行为(ShowDialog(),则设置了Result属性),那么您必须编写另一个Interface。。。等

如果这种类型的语法(泛型、接口等)不能减少编写的代码行数、复杂性或可维护性(显然我们不能说这里真的是这样),那么它就毫无用处了


编辑:

表单管理实际上取决于您的用例。

  • 如果你有20个可以同时显示的表单,那么你应该考虑FormManager的概念(或者更好:考虑如何通过减少可能打开的表单的数量来改善用户体验)
  • 如果你有一些相对简单的表单(同时有2-3个非模态表单+3-4个可能的模态表单),我不会写复杂的代码来管理这些表单

通常,用于启动应用程序的表单(即关闭程序时停止程序的表单,即Application.Run()的参数表单)负责其他表单。您有一个主窗体和多个子窗体。如果你的情况真的不同,那么可能有更聪明的东西可以写,但这取决于你的情况。对于表单管理的普遍问题,我不认为有人能给出一个普遍的好答案。

老实说,如果你想要真正可维护的东西,试着(尽可能多地)减少可以同时显示的表单的数量。在大多数情况下,同时显示多个非模态表单并不能提供良好的用户体验,如果表单相互依赖,则表单生命周期管理可能会出现问题。

我使用这个技巧。假设form1是主要形式:

private void button1_Click(object sender, EventArgs e)
{
     LoadForm(new Form2());
}
private void LoadForm(Form frm)
{
    frm.FormClosed += new FormClosedEventHandler(frm_FormClosed);
    this.Hide();
    // Here you can set a bunch of properties, apply skins, save logs...
    // before you show any form
    frm.Show();
}
void frm_FormClosed(object sender, FormClosedEventArgs e)
{
    this.Show();
}

因此;打开任何关闭的窗体(除窗体1外)时,窗体1将再次出现。

更新

using (Form2 frm = new Form2())
{
    if (frm.ShowDialog() = DialogResult.ok)
    {
        //Do some things...
    }
}

在这种情况下,没有必要隐藏以前的表单。

根据应用程序的大小,Id表示要查看Microsoft Enterprise库,特别是CAB块。

这应该会给你一个良好的开端。

public partial class Form1 : Form
{
    private static Form1 inst;
    public static Form1 GetForm
    {
        get
        {
            if (inst == null || inst.IsDisposed)
            {
                inst = new Form1();
            }
            return inst;
        }
    }
    public Form1()
    {
        InitializeComponent();
        inst = this;
    }
    private void button1_Click(object sender, EventArgs e)
    {
        Form2.GetForm.Show();
        this.Hide();
    }
}

public partial class Form2 : Form
{
    private static Form2 inst;
    public static Form2 GetForm
    {
        get
        {
            if (inst == null || inst.IsDisposed)
                inst = new Form2();
            return inst;
        }
    }
    public Form2()
    {
        InitializeComponent();
    }
    private void button1_Click(object sender, EventArgs e)
    {
        Form1.GetForm.Show();
        this.Hide();
    }
    private void Form2_FormClosed(object sender, FormClosedEventArgs e)
    {
        Form1.GetForm.Show();
    }
}

如果您有两个以上的表单,则创建另一个表单作为From2