web-dev-qa-db-fra.com

Meilleures pratiques pour les applications multiformes pour afficher et masquer les formulaires?

Il y a des tonnes de questions sur StackOverflow demandant comment masquer Form1 et afficher Form2. Et, généralement, quelques réponses différentes surgissent:

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

Je ne cherche pas une solution jetable simple comme # 1. Je recherche les meilleures pratiques de gestion de formulaires. Une application avec 5-8 formulaires, s'ouvrant et se fermant fréquemment - quelle est la meilleure façon de gérer ces formulaires?

Mon idée était de faire de chaque forme un singleton (paresseux?) Et de les enterrer dans une classe FormsManager (comme la solution # 2 mais ++). Et puis les formulaires individuels peuvent appeler quelque chose comme FormsManager.GetForm<WelcomeDialog>().

Mais je me demandais ce que les gens avec plus d'expérience utilisaient. Encore une fois, ces solutions ne devraient pas être des hacks rapides. Ils devraient être orientés vers la conception, peut-être architecturaux et solutions à long terme.

Modifications:

C'est une question assez générique (donc les exigences sont assez ouvertes) pour quiconque pourrait avoir le même problème. Cependant, spécifique à ma situation, je n'ai pas besoin de plusieurs formulaires affichés au démarrage. De plus, je n'ai pas de formulaires MDI. J'ai peut-être quelques formulaires modaux, mais ils sont pour la plupart non modaux.

17
Jason

Je réponds de manière générale ici.

Je ne pense pas qu'un modèle singleton conviendrait bien à la gestion des formulaires. En règle générale, vous souhaitez transmettre certains paramètres de contexte au formulaire et vous pouvez ouvrir plusieurs instances du même formulaire. Un singleton ne convient donc pas bien à l'OMI.

Je pense que la gestion des formulaires devrait être simple.

Par exemple, si vous souhaitez afficher un formulaire modal à partir d'un autre formulaire, j'écrirais quelque chose de très simple:

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

        if (frm.MyResultProperty == ...)
        {
            // Do some job here
        }
    }
}

Bien sûr, vous pouvez écrire une syntaxe d'interface/générique pour éviter une petite duplication de code au cas où vous voudriez afficher beaucoup de formes modales:

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

Mais honnêtement, j'ai l'impression que c'est un peu exagéré.

Deuxième point: si votre formulaire ne suit pas exactement ce comportement spécifique (ShowDialog() alors une propriété Result est définie), alors vous devez écrire une autre interface ... etc.

Si ce type de syntaxe (génériques, interfaces ... etc.) Ne réduit pas le nombre de lignes de code écrites OR la complexité OR la maintenabilité) (et évidemment nous ne pouvons pas dire que c'est vraiment le cas ici), alors c'est assez inutile l'OMI.


Éditer:

La gestion des formulaires dépend vraiment de votre cas d'utilisation.

  • Si vous avez dit 20 formulaires qui peuvent être affichés en même temps, alors vous devriez penser à un concept FormManager (ou mieux: pensez à comment améliorer l'expérience utilisateur en réduisant le nombre de formulaires ouverts possibles)
  • Si vous avez quelque chose de relativement simple (2-3 formulaires non modaux en même temps + 3-4 formulaires modaux possibles), je n'écrirais pas de code complexe pour gérer ces formulaires.

Généralement, le formulaire qui est utilisé pour démarrer l'application (c'est-à-dire le formulaire qui arrête le programme lorsqu'il est fermé, qui est le formulaire qui est un paramètre de Application.Run()) est responsable d'autres formulaires. Vous avez n formulaire principal et plusieurs formulaires enfants. Si votre cas est vraiment différent, alors il y a probablement quelque chose de plus intelligent à écrire, mais cela dépendra de votre cas. Je ne pense pas que l'on puisse apporter une bonne réponse générale à la problématique générale de la gestion des formulaires.

Honnêtement, si vous voulez quelque chose de vraiment maintenable, essayez de réduire (autant que possible) le nombre de formulaires qui peuvent être affichés en même temps. Plusieurs formulaires non modaux affichés en même temps n'offrent pas une bonne expérience utilisateur dans la plupart des cas, et la gestion de la durée de vie des formulaires peut être problématique si les formulaires dépendent les uns des autres.

6
ken2k

Dans tout autre scénario que le plus simple - un seul formulaire principal exécuté pendant toute la durée de vie de l'application, avec des formulaires enfants de courte durée - il est recommandé de créer une classe qui hérite de ApplicationContext . Ce n'est pas si compliqué:

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

et Application.Run dans Program.cs peut utiliser l'instance statique de 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);
    }
}

Pendant la durée de vie de l'application, de nouveaux formulaires doivent être créés via CreateForm, afin d'enregistrer la méthode onFormClosed avec l'événement FormClosed:

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

Si vous préférez new Form3(); aux appels à FormManager.CreateForm, Vous pouvez créer une méthode RegisterForm sur FormManager:

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

et appelez RegisterForm sur chaque nouveau Form:

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

(NB. Si tous vos formulaires héritent d'une classe de base, alors au lieu d'appeler manuellement RegisterForm pour chaque nouvelle instance, vous pouvez l'appeler dans le constructeur de la classe de base.)


Notez que Application.OpenForms Renvoie uniquement les formulaires qui sont actuellement visibles. Si l'application ne doit pas quitter tant qu'il reste des formulaires masqués ouverts, alors FormManager devra utiliser une collection pour garder une trace de tous les formulaires. Cette collection déterminera s'il faut quitter l'application ou non.

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;
}
12
Zev Spitz

J'utilise cette astuce. Disons que form1 est la forme principale:

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

Par conséquent; lorsque vous ouvrez un formulaire fermé (sauf form1), form1 réapparaîtra.

Mise à jour

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

Dans ce cas, il n'est pas nécessaire de masquer le formulaire précédent.

3
Amen Ayach

En fonction de la taille de votre application, je vous dirais de jeter un œil à la bibliothèque Microsoft Enterprise et plus particulièrement au bloc CAB.

Cela devrait vous donner un bon départ.

1
Steoates