Le migliori pratiche per le applicazioni multiforms per mostrare e hide i moduli?

Ci sono tonnellate di domande su StackOverflow che chiedono come hide Form1 e mostrare Form2. E, di solito, emergono alcune risposte diverse:

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

Non sto cercando una soluzione semplice usa e getta come la n. 1. Sto cercando le migliori pratiche di gestione dei moduli. Un’applicazione con 5-8 moduli, che si aprono e si chiudono a vicenda frequentemente: qual è il modo migliore per gestire queste forms?

La mia idea era di rendere ogni forma (pigra?) Singleton e seppellirle in una class FormsManager di qualche tipo (come la soluzione n. 2 ma ++). E poi i singoli moduli potrebbero chiamare qualcosa come FormsManager.GetForm() .

Ma mi stavo chiedendo cosa usassero le persone con più esperienza. Ancora una volta, queste soluzioni non dovrebbero essere hack veloci. Dovrebbero essere soluzioni orientate al design , forse architettoniche e a lungo termine .

modifiche:

Questa è una domanda piuttosto generica (quindi i requisiti sono abbastanza aperti) per chiunque possa avere lo stesso problema. Tuttavia, specifico per la mia situazione, non ho bisogno di più moduli mostrati all’avvio. Inoltre, non ho moduli MDI. Potrei avere alcune forms modali, ma sono per lo più non modali.

Sto rispondendo in modo generale qui.

Non credo che un modello singolo si adatterebbe bene con la gestione dei moduli. In genere, si desidera passare alcuni parametri di contesto al modulo e si potrebbe voler aprire più istanze dello stesso modulo. Quindi un singleton non si adatta bene all’IMO.

Penso che la gestione della forma dovrebbe essere semplice.

Ad esempio, se vuoi visualizzare un modulo modale da un altro modulo, scriverei qualcosa di molto semplice:

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

Ovviamente è ansible scrivere una syntax di interfaccia / generici per evitare una piccola duplicazione del codice nel caso in cui si desideri visualizzare molte forms modali:

 public interface IFormResult { T Result { get; set; } } public class ModalForm1 : Form, IFormResult { public ModalForm1() { InitializeComponent(); this.Result = "My result"; } public string Result { get; set; } } private void button1_Click(object sender, EventArgs e) { string res = ShowModalForm(); } private static T2 ShowModalForm() where T1 : Form, IFormResult, new() { using (T1 form = new T1()) { form.ShowDialog(); return form.Result; } } 

Ma sinceramente, mi sento un po ‘sopraffatto.

Secondo punto: se il tuo modulo non segue esattamente questo comportamento specifico ( ShowDialog() allora viene impostata una proprietà Result ), allora devi scrivere un’altra interfaccia … ecc.

Se questo tipo di syntax (generici, interfacce, ecc.) Non riduce il numero di righe di codice scritte O la complessità O la manutenibilità (e ovviamente non possiamo dire che sia davvero il caso qui), allora è carino IMO inutile.


Modificare:

La gestione dei moduli dipende molto dal tuo caso d’uso.

  • Se dici 20 moduli che possono essere visualizzati allo stesso tempo, dovresti pensare a un concetto di FormManager (o meglio: pensa a come migliorare l’esperienza utente riducendo il numero di possibili moduli aperti)
  • Se hai qualcosa di relativamente semplice (2-3 moduli modeless allo stesso tempo + 3-4 forms modali possibili), non scriverei codice complesso per gestire quelle forms.

In genere, il modulo utilizzato per avviare l’applicazione (ovvero il modulo che interrompe il programma quando è chiuso, ovvero il modulo che è un parametro di Application.Run() ) è responsabile di altre forms. Hai una forma principale e moltiplica le forms figlio . Se il tuo caso è davvero diverso, probabilmente c’è qualcosa di più intelligente da scrivere, ma dipenderà dal tuo caso. Non credo che si possa fornire una buona risposta generale alla problematica generale della gestione della forma.

Onestamente, se vuoi qualcosa di veramente mantenibile, prova a ridurre (il più ansible) il numero di moduli che possono essere mostrati allo stesso tempo. La presenza di più moduli non modali visualizzati allo stesso tempo non offre una buona esperienza utente nella maggior parte dei casi e la gestione a vita può essere problematica se i moduli dipendono l’uno dall’altro.

In uno scenario diverso da quello più diretto: un singolo modulo principale in esecuzione per tutta la durata dell’applicazione, con moduli figlio di breve durata, si consiglia di creare una class che erediti da ApplicationContext . Non è così complicato:

 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() 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 _current = new Lazy(); public static FormManager Current => _current.Value; //Startup forms should be created and shown in the constructor public FormManager() { var f1 = CreateForm(); f1.Show(); var f2 = CreateForm(); f2.ShowDialog(); } } 

e Application.Run in Program.cs può utilizzare l’istanza statica di FormManager :

 static class Program { ///  /// The main entry point for the application. ///  [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(FormManager.Current); } } 

Durante la vita dell’applicazione, è necessario creare nuove forms tramite CreateForm , in modo da registrare il metodo onFormClosed con l’evento FormClosed :

 var f3 = FormManager.Current.CreateForm(); f3.Show(); var f4 = FormManager.Current.CreateForm(); f4.ShowDialog(); 

Se preferisci new Form3(); tramite le chiamate a FormManager.CreateForm , è ansible creare un metodo RegisterForm su FormManager :

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

e chiama RegisterForm su ogni nuovo Form :

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

(NB Se tutte le tue forms ereditano da qualche class base, invece di chiamare manualmente RegisterForm per ogni nuova istanza, puoi chiamarla nel costruttore della class base.)


Notare che Application.OpenForms restituisce solo quei moduli che sono attualmente visibili. Se l’applicazione non dovesse uscire fintanto che ci sono ancora moduli nascosti aperti, allora FormManager dovrà usare qualche raccolta per tenere traccia di tutti i moduli. Tale raccolta determinerà se chiudere l’applicazione o meno.

 class FormManager : ApplicationContext { private List
forms = new List
(); 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() where T : Form, new() { var ret = new T(); RegisterForm(ret); return ret; } private static Lazy _current = new Lazy(); public static FormManager Current => _current.Value; }

Io uso questo trucco, diciamo che form1 è la forma 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(); } 

così quando si chiude qualsiasi forma (tranne form1), form1 riappare nuovamente

Aggiornare

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

In questi casi non è necessario hide la forma precedente

A seconda delle dimensioni della tua applicazione, Id dice di dare un’occhiata alla libreria di Microsoft Enterprise e in particolare al blocco CAB.

Questo dovrebbe darti un buon inizio.

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

se si dispone di più di due moduli, quindi creare un altro modulo come From2