2012-02-27 19 views
12

Ci sono tonnellate di domande su StackOverflow chiedendo come nascondere e mostrare Form1 Form2. E, di solito, un paio di risposte diverse affiorano:Le migliori pratiche per le applicazioni multiforme per mostrare e nascondere i moduli?

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

Io non sto cercando una soluzione semplice usa e getta come # 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 forme?

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

Ma mi chiedevo che cosa le persone con più esperienza usati. Ancora una volta, queste soluzioni non dovrebbero essere hack veloci. Dovrebbero essere orientato al design, forse architettonico e soluzioni a lungo termine.

Modifiche:

Questa è una domanda abbastanza generica (quindi i requisiti sono abbastanza aperto) per chi potrebbe 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 forme modali, ma sono per lo più non modali.

+0

Quali sono i requisiti? Vuoi più moduli da mostrare all'avvio? Una volta avviata l'applicazione, qual è la forma mostra/nascondi la strategia desiderata (moduli modali, più moduli, moduli MDI ...)? – ken2k

+0

Quindi, non ho bisogno di più moduli visualizzati all'avvio. Una volta avviata l'applicazione, la forma mostra/nascondi strategia è semplicemente più moduli (non-MDI). Ci possono essere una o due forme modali. – Jason

risposta

5

sto rispondendo in modo generale qui.

non credo che un pattern Singleton 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.

Credo che la gestione forma dovrebbe essere semplice.

Per esempio, se si desidera visualizzare un form modale da un'altra forma, vorrei scrivere qualcosa di davvero semplice:

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

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

Naturalmente si potrebbe scrivere una sintassi interfaccia/farmaci generici per evitare un po 'di duplicazione di codice in caso in cui si desidera visualizzare un sacco di forme modali:

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

Ma, onestamente, mi sento come se fosse un po 'overingeneered.

Secondo punto: se il modulo non segue esattamente questo comportamento specifico (ShowDialog() quindi è impostata una proprietà Result), è necessario scrivere un'altra interfaccia ... ecc.

Se questo tipo di sintassi (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 proprio il caso qui) , quindi è IMO piuttosto inutile.


Edit:

gestione Modulo dipende dal vostro caso d'uso.

  • Se si dispone di dire 20 moduli che possono essere visualizzati allo stesso tempo, allora si dovrebbe pensare di un concetto FormManager (o meglio: pensare a come migliorare l'esperienza dell'utente, riducendo il numero di possibili forme aperte)
  • Se si dispone di qualcosa di relativamente semplice (2-3 moduli modeless allo stesso tempo + 3-4 forme modali possibili), non scriverei codice complesso per gestire tali moduli.

Generalmente, il modulo che viene utilizzato per avviare l'applicazione (cioè la forma che arresta il programma quando è chiuso, che è la forma che è un parametro di Application.Run()) è responsabile di altre forme. Si dispone di in un modulo principale e si moltiplicano i moduli 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ù possibile) 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.

+0

Forse ho sbagliato le responsabilità del modulo, pensando che ci dovrebbe essere un controller centrale. Invece, dovrebbero gestire se stessi? Stavo pensando, tramite Reflection, a scoprire tutte le classi di Form e ad aggiungerle a una typemap Dictionary ea chiamare Get() o qualcosa del genere.Il problema della manutenibilità che sto cercando di risolvere è scrivere un codice per spaghetti, in cui devo saltare da una classe all'altra per ricordare quale forma apre la forma. – Jason

+0

@Jason Vedere la risposta modificata – ken2k

+0

Nota che WinForms ha una classe base incorporata per il problema generale della gestione della form: ['ApplicationContext'] (https://msdn.microsoft.com/en-us/library/system.windows .forms.applicationcontext (v = vs.110) .aspx) –

3

Io uso questo trucco, diciamo form1 è la principale forma:

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 qualsiasi forma chiusa (tranne form1), form1 riapparirà di nuovo

Aggiornamento

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

In questi casi non è necessario nascondere il modulo precedente

+0

Hmm, questo sembra un po 'difficile da mantenere, specialmente con 5 forme ciascuna chiamata diverse forme l'una dall'altra. – Jason

+0

No, non è necessario eseguire questa operazione in tutti gli altri moduli, altri moduli potrebbero inviare messaggi, dialogresult, dati in mainform che possono gestire i moduli di destra, a meno di "Showdialog": vedere aggiornamento –

+0

Non capisco cosa intendi. Sembra che il tuo codice riapra il modulo 1 nascosto una volta che Form2, Form3 o qualsiasi modulo viene chiuso. Ma questo è limitante. Form1 potrebbe dover aprire Form5. Form5 potrebbe dover aprire Form3. Form3 potrebbe dover aprire Form7. Non credo che la tua soluzione sia molto estensibile. – Jason

1

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

Questo dovrebbe dare un buon inizio.

6

In uno scenario diverso da quello più diretto: un singolo modulo principale in esecuzione per la durata dell'applicazione, con moduli figlio di breve durata, si consiglia di creare una classe che eredita 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<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(); 
    } 
} 

e Application.Run in Program.cs possono utilizzare l'istanza statica di 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); 
    } 
} 

Durante la durata dell'applicazione, nuove forme dovrebbero essere create tramite CreateForm , per registrare il metodo onFormClosed con l'evento FormClosed:

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

Se si preferisce new Form3(); sopra le chiamate verso FormManager.CreateForm, è possibile creare un metodo RegisterForm su FormManager:

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

e chiamare RegisterForm su ogni nuovo Form:

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

(NB. Se tutti i moduli ereditare da una classe base, quindi chiamando invece di manualmente RegisterForm per ogni nuova istanza, si potrebbe chiamare nel costruttore della classe di base.)


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

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

Non lo sapevo affatto, grazie! – Jason

+1

@ Jason Ho ampliato la mia risposta. –

0
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 formano quindi creare un'altra forma come da2

+0

Aggiungi una spiegazione con la risposta per come questa risposta aiuta l'OP nel risolvere il problema attuale –

Problemi correlati