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

Jas*_*son 16 c# architecture design-patterns winforms

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

1)

// Program.cs
Application.Run(new Form1());
// Form1.cs
Form2 form2 = new Form2();
form2.Show();
this.Hide();
Run Code Online (Sandbox Code Playgroud)

2)

// Program.cs
Form1 form1 = new Form1();
Form2 form2 = new Form2();
form1.Show();
form2.Show();
Application.Run();
Run Code Online (Sandbox Code Playgroud)

...etc..

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

我的想法是让每个形式成为一个(懒惰的?)Singleton并将它们埋在某种FormsManager类中(比如解决方案#2但是++).然后个别形式可能会称之为FormsManager.GetForm<WelcomeDialog>().

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

编辑:

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

Zev*_*itz 10

除了最直接的场景之外的任何事情 - 在应用程序的生命周期中运行的单个主窗体,使用短期子窗体 - 建议创建一个继承自的类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();
    }
}
Run Code Online (Sandbox Code Playgroud)

并且Application.RunProgram.cs可以使用的静态实例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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在应用程序的生命周期中,应该通过以下方式创建新表单:CreateForm为了向事件注册onFormClosed方法FormClosed:

var f3 = FormManager.Current.CreateForm<Form3>();
f3.Show();
var f4 = FormManager.Current.CreateForm<Form4>();
f4.ShowDialog();
Run Code Online (Sandbox Code Playgroud)

如果您更喜欢new Form3();通话FormManager.CreateForm,则可以创建一个RegisterForm方法FormManager:

public void RegisterForm(Form frm) {
    frm.FormClosed += onFormClosed;
}
Run Code Online (Sandbox Code Playgroud)

并呼吁RegisterForm每个新的Form:

var f3 = new Form3();
FormManager.Current.RegisterForm(f3);
var f4 = new Form4();
FormManager.Current.RegisterForm(f4);
Run Code Online (Sandbox Code Playgroud)

(注意:如果所有表单都从某个基类继承,那么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;
}
Run Code Online (Sandbox Code Playgroud)


ken*_*n2k 6

我在这里以一般方式回答.

我认为单例模式不适合表单管理.通常,您希望将一些上下文参数传递给表单,并且您可能希望打开同一表单的多个实例.因此单身人士不适合IMO.

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

例如,如果你想从另一个表单中显示一个模态表单,我会写一些非常简单的东西:

private void button1_Click(object sender, EventArgs e)
{
    using (ModalForm1 frm = new ModalForm1(myParam))
    {
        frm.ShowDialog();

        if (frm.MyResultProperty == ...)
        {
            // Do some job here
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

但说实话,我觉得它有点过分了.

第二点:如果您的表单不完全遵循此特定行为(ShowDialog()然后设置Result属性),那么您必须编写另一个接口...等.

如果这种类型的语法(泛型,接口等)不减少写入的代码行数或复杂性或可维护性(显然我们不能说这是真的如此),那么它很漂亮无用的IMO.


编辑:

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

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

通常,用于启动应用程序的表单(即关闭时停止程序的表单,即作为参数的表单Application.Run())负责其他表单.您有一个主要表单,并且多个子表单.如果你的情况真的不同,那么写一些东西可能更聪明,但这取决于你的情况.我不认为可以为表格管理的一般问题提供一般的良好答案.

老实说,如果你想要一些真正可维护的东西,尽量减少(尽可能多)可以同时显示的表格数量.在大多数情况下,多个显示的无模式窗体不能提供良好的用户体验,并且如果窗体彼此依赖,则窗体寿命管理可能是有问题的.