Подтвердить что ты не робот

Рекомендации по многоформатным приложениям для отображения и скрытия форм?

В 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>().

Но мне было интересно, какие люди используют больше опыта. Опять же, эти решения не должны быть быстрыми хаками. Они должны быть ориентированными на дизайн, возможно архитектурными и долгосрочными решениями.

редактирует:

Это довольно общий вопрос (так что требования довольно открыты) для всех, у кого могут быть такие же проблемы. Конкретно для моей ситуации, однако, мне не нужно несколько форм, показанных при запуске. Кроме того, у меня нет форм MDI. Я могу иметь несколько модальных форм, но они в основном немодальные.

4b9b3361

Ответ 1

Я отвечаю в общем виде здесь.

Я не думаю, что одноэлементный шаблон хорошо подойдет для управления формами. Как правило, вы хотите передать некоторый контекстный параметр в форму, и вам может понадобиться открыть несколько экземпляров одной и той же формы. Таким образом, синглтон не подходит хорошо ИМО.

Я считаю, что управление формами должно быть простым.

Например, если вы хотите отобразить модальную форму из другой формы, я бы написал что-то очень просто:

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), тогда вы должны написать другой интерфейс... и т.д.

Если этот тип синтаксиса (generics, interfaces... и т.д.) не уменьшает количество строк написанного кода или сложность ИЛИ ремонтопригодность (и, очевидно, мы не можем сказать, что это действительно так) то это довольно бесполезная ИМО.


Edit:

Управление форматами действительно зависит от вашего использования.

  • Если вы скажете 20 форм, которые могут отображаться одновременно, тогда вы должны подумать о концепции FormManager (или лучше: подумайте о том, как улучшить пользовательский интерфейс, уменьшив число возможных открываемых форм)
  • Если у вас есть что-то относительно простое (2-3 моделирования в одно и то же время + 3-4 возможных модальных формы), я бы не написал сложный код для управления этими формами.

Как правило, форма, которая используется для запуска приложения (т.е. форма, которая останавливает программу при закрытии, которая является формой, являющейся параметром Application.Run()), несет ответственность за другие формы. У вас есть одна основная форма и несколько дочерних форм. Если ваш случай действительно отличается, то, вероятно, что-то умнее писать, но это будет зависеть от вашего дела. Я не думаю, что можно дать общий хороший ответ на общую проблему управления формами.

Честно говоря, если вы хотите что-то действительно поддерживаемое, попробуйте уменьшить (насколько это возможно) количество форм, которые могут быть показаны одновременно. Несколько отображаемых немодальных форм одновременно не обеспечивают хорошего пользовательского опыта в большинстве случаев, а управление жизненным циклом формы может быть проблематичным, если формы зависят друг от друга.

Ответ 2

В чем-то другом, кроме самого простого сценария - единственной основной форме, запущенной для жизни приложения, с короткоживущими дочерними формами - рекомендуется создать класс, который наследует от 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();
    }
}

и Application.Run в Program.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);
    }
}

В течение срока службы приложения новые формы должны создаваться с помощью CreateForm, чтобы зарегистрировать метод onFormClosed с событием FormClosed:

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

Если вы предпочитаете new Form3(); по вызовам FormManager.CreateForm, вы можете создать метод RegisterForm на FormManager:

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

и вызовите RegisterForm для каждого нового Form:

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

(NB. Если все ваши формы наследуются от какого-то базового класса, то вместо ручного вызова 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;
}

Ответ 3

Я использую этот трюк, скажем, форма1 является основной формой:

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

В этом случае не нужно скрывать предыдущую форму

Ответ 4

В зависимости от размера вашего приложения, скажем, посмотрите библиотеку Microsoft Enterprise и, в частности, блок CAB.

Это должно дать вам хорошее начало.

Ответ 5

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