2011-10-28 12 views
14

Lascia che ti dia lo sfondo.Forza per chiudere MessageBox a livello di programmazione

Abbiamo un'applicazione (di medie dimensioni) che utilizza MessageBox.Show (....) in vari punti (in centinaia).

Queste finestre di messaggio fanno parte di worklfow e vengono utilizzate per informare, avvertire o prendere input da un utente. L'applicazione dovrebbe disconnettersi automaticamente dopo un determinato periodo di tempo se non ci sono attività. Abbiamo il requisito che durante la disconnessione dell'applicazione, solo per pulire i dati della sessione, per cancellare le viste e nascondersi in modo che al prossimo avvio, non dovrà eseguire il processo di avvio che è costoso in termini di tempo.

Tutto funziona correttamente ma in uno scenario in cui è presente una finestra di messaggio sullo schermo e l'utente ha lasciato la macchina senza rispondere alla finestra di messaggio e quindi a causa di nessuna attività per disconnettere l'applicazione. Il problema è che la casella Messaggio non scomparirà.

Come posso chiudere la finestra di messaggio aperta, se presente, mentre nasconde l'applicazione?

Grazie.

+0

Forse inviare tasto Invio o ESC? :) – Reniuz

+0

Ho pensato che 'MessageBox.Show (...)' è modale, quindi come può il programma inviare una chiave? Stai usando discussioni/attività? – Fischermaen

+0

Grazie per le risposte. Giusto per chiarire usando la casella msg personalizzata non è un'opzione in quanto la rilavorazione è abbastanza grande. Anche l'invio del tasto ESC non è corretto perché solo l'applicazione attiva riceverà il comando. Sto usando l'approccio di FIndWindow dove sto ricevendo Msgbox handle passando id e msg caption. Dopo aver ottenuto il gestore, sto chiudendo utilizzando la seguente API win32, ad es. SendMessage (nuovo HandleRef (null, msgbxcHandler), WM_CLOSE, IntPtr.Zero, IntPtr.Zero); SendMessage (nuovo HandleRef (null, msgbxcHandler), WM_NCDESTROY, IntPtr.Zero, IntPtr.Zero); Finora funziona bene. – NYK

risposta

1

Prendendo come un presupposto che è possibile modificare il codice che sta chiamando il metodo MessageBox.Show(), mi sento di raccomandare non usa MessageBox. Invece, basta usare il proprio modulo personalizzato, chiamando ShowDialog() su di esso per fare fondamentalmente la stessa cosa della classe MessageBox. Quindi, l'utente ha l'istanza del modulo stesso ed è possibile chiamare Close() in tale istanza per chiuderla.

Un buon esempio è here.

+0

Ouch. E vediamo molte volte quanto sia orribile quando le persone reimplementano le finestre dei messaggi. 'msg' mostra che puoi avere una vera casella di messaggio e chiuderla dopo un ritardo ... – Joey

+0

@ Joey: sì, hai ragione ... era solo un'idea, non la migliore probabilmente. Dici che _msg mostra che puoi avere una vera casella di messaggio e chiuderla dopo un ritardo_ ... cosa o chi è "msg"? Scusa, non capisco, chiedo scusa – Marco

3

Prima una domanda: se le caselle di messaggi vengono utilizzate come parte del flusso di lavoro, la finestra di messaggio non verrà chiusa in modo programmatico perché il flusso cambierà/continuerà?

penso che avete tre opzioni

  1. Crea la tua versione della classe MessageBox che si apre una finestra di dialogo che si presenta come un messagebox con funzionalità aggiuntive in modo che chiusa automaticamente dopo un certo periodo di tempo.

  2. Implementare qualcosa di simile in C# per chiudere le finestre di messaggio in modo programmato. http://www.codeproject.com/KB/dialog/AutoCloseMessageBox.aspx

  3. Eliminare le finestre di messaggio dall'interrompere il flusso di lavoro. Questa è probabilmente la soluzione migliore dal momento che il suono della chiusura di una finestra di messaggio causerà la continuazione/il cambiamento del flusso di lavoro e forse causerà la visualizzazione di un'altra casella di messaggio che potrebbe non essere desiderabile. Ma ovviamente risolvere il problema alla radice potrebbe essere il migliore, ma non è sempre il più facile.

1 e 2 avrebbe bisogno di essere fatto da un thread separato, quindi sarà necessario pensare alle implicazioni di che come mostra la messagebox sarà il blocco.

+0

+1 per il punto 3.! Anche a me, non avrei la buona sensazione di "confermare" semplicemente una finestra di messaggio aperta senza nemmeno sapere se l'utente l'avesse notato ... – MartinStettner

1

penso che il modo più pulito sarebbe quello di implementare voi propria forma finestra di messaggio come

class MyMessageBox : Form { 
    private MyMessageBox currentForm; // The currently active message box 

    public static Show(....) { // same as MessageBox.Show 
    // ... 
    } 

    public static Show(...) { // define additional overloads 
    } 

    public static CloseCurrent() { 
    if (currentForm != null) 
     currentForm.Close(); 
    } 

    // ... 
} 

In alcuni dei miei progetti più grandi, ho trovato questo approccio utile anche per altri scopi (come la registrazione automatica dei messaggi di errore ecc.)

La seconda idea è di utilizzare GetTopWindow() (o forse qualche altra funzione WIN32) per ottenere la finestra corrente di livello superiore dell'applicazione e inviare un messaggio WM_CLOSE ad esso.

+0

e se l'utente premesse la scheda alt o un'altra finestra di messaggio sopra la casella dei messaggi desiderata . in quello scenario credo che chiuderà quello indesiderato. – Vivekh

0

Crea il tuo controllo per questo e implementa il comportamento che ti piace avere lì. Come opzione potrebbe esserci un timer per chiudere questo MessageBox.

2

Heres mio esempio con SendKeys - Testato e funzionante:

diciamo che abbiamo BackgroundWorker e il pulsante in forma. Dopo che il pulsante era click - avvia lavoratore e mostra la finestra del messaggio. Negli addetti al lavoro, l'evento DoWork dorme per 5 secondi e quindi invia la chiave di invio - casella di messaggistica chiusa.

private void button1_Click(object sender, EventArgs e) 
{ 
    backgroundWorker1.RunWorkerAsync(); 
    MessageBox.Show("Close this message!"); 
} 

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    Thread.Sleep(5000); 
    SendKeys.SendWait("{Enter}");//or Esc 
} 
+1

Voglio solo sottolineare che per la soluzione è molto importante verificare se c'è davvero una finestra di messaggio aperta: un tasto di invio inviato a qualsiasi altra parte dell'interfaccia utente potrebbe avere conseguenze indesiderate ... – MartinStettner

+0

Sì, il tuo punto è buono. La soluzione rapida in modo tale che la chiave di invio non sia ovviamente una buona chiave, meglio usare l'interfaccia utente di esc - in dovrebbe rappresentare la cancellazione. – Reniuz

+1

Questa soluzione funziona solo se l'utente non cambia lo stato attivo su un'altra app. Non puoi essere sicuro che l'app abbia ancora lo stato attivo. E Windows impedisce all'applicazione di focalizzarsi a livello di programmazione da un'altra app. – Alex

6

Questo link sui forum MSDN mostra come chiudere una finestra di messaggio utilizzando FindWindow e l'invio di un messaggio WM_CLOSE. Anche se è stata posta la domanda per .NET/WindowsCE, potrebbe risolvere il problema, vale la pena dare un'occhiata

+2

Penso che sia considerata una buona pratica aggiungere almeno un po 'di informazioni aggiuntive su come la pagina collegata potrebbe risolvere un problema (invece di pubblicare semplicemente il link). Spero non ti dispiaccia che ho aggiunto una breve descrizione. +1 in ogni caso, questa potrebbe essere una risorsa utile. – MartinStettner

+0

Bene martin, hai assolutamente ragione. a causa della complessità temporale non ho potuto aggiungere queste informazioni. comunque grazie per il montaggio. :) – Bravo

7

Ecco un pezzo di codice basato su UIAutomation (un'API interessante ma non ancora molto utilizzata) che tenta di chiudere tutti i modali finestre (compresa quella aperta con MessageBox) del processo in corso:

/// <summary> 
    /// Attempt to close modal windows if there are any. 
    /// </summary> 
    public static void CloseModalWindows() 
    { 
     // get the main window 
     AutomationElement root = AutomationElement.FromHandle(Process.GetCurrentProcess().MainWindowHandle); 
     if (root == null) 
      return; 

     // it should implement the Window pattern 
     object pattern; 
     if (!root.TryGetCurrentPattern(WindowPattern.Pattern, out pattern)) 
      return; 

     WindowPattern window = (WindowPattern)pattern; 
     if (window.Current.WindowInteractionState != WindowInteractionState.ReadyForUserInteraction) 
     { 
      // get sub windows 
      foreach (AutomationElement element in root.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window))) 
      { 
       // hmmm... is it really a window? 
       if (element.TryGetCurrentPattern(WindowPattern.Pattern, out pattern)) 
       { 
        // if it's ready, try to close it 
        WindowPattern childWindow = (WindowPattern)pattern; 
        if (childWindow.Current.WindowInteractionState == WindowInteractionState.ReadyForUserInteraction) 
        { 
         childWindow.Close(); 
        } 
       } 
      } 
     } 
    } 

ad esempio, se si dispone di un'applicazione WinForms che si apre un MessageBox quando si preme qualche button1, sarà comunque in grado di chiudere l'applicazione utilizzando il menu "Chiudi finestra" di Windows (fare clic con il tasto destro sulla barra delle applicazioni):

private void button1_Click(object sender, EventArgs e) 
    { 
     MessageBox.Show("Don't click me. I want to be closed automatically!"); 
    } 

    protected override void WndProc(ref System.Windows.Forms.Message m) 
    { 
     const int WM_SYSCOMMAND = 0x0112; 
     const int SC_CLOSE = 0xF060; 

     if (m.Msg == WM_SYSCOMMAND) // this is sent even if a modal MessageBox is shown 
     { 
      if ((int)m.WParam == SC_CLOSE) 
      { 
       CloseModalWindows(); 
       Close(); 
      } 
     } 
     base.WndProc(ref m); 
    } 

Si potrebbe usare CloseModalWindows da qualche altra parte nel codice, ovviamente, questo è solo un esempio.

+0

Non è UIAutomation per WPF? La domanda era per WinForms –

+0

@ErikFunkenbusch - Non so cosa intendi con "per WPF". UIAutomation supporta qualsiasi tipo di applicazione. –

+0

Lo spazio dei nomi System.Windows.Automation dice che è per WPF. http://msdn.microsoft.com/en-us/library/ms747327(v=vs.110).aspx "Microsoft UI Automation è il nuovo framework di accessibilità per Microsoft Windows, disponibile su tutti i sistemi operativi che supportano Windows Presentation Foundation (WPF)." –

1

Ho usato .net 2 e due approcci con lo stesso trucco.

Aprire il MessageBox da stub-Form con MessageBox.Show(this,"message")

quando il modulo non è visibile o non ha davvero UI.

  1. Mantenere il gestore di moduli e chiudere con:

    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); 
    

    o

  2. tenendo il modulo come parametro di classe e l'utilizzo di FormX.Close().

Poiché il modulo è il proprietario del MessageBox, la chiusura chiude il MessageBox.

1

Fare riferimento alla DmitryG post in "Close a MessageBox after several seconds"

chiusura automatica MessageBox dopo timeout raggiungere

using System.Runtime.InteropServices; 
using System.Threading; 
using System.Windows.Forms; 

    public class AutoClosingMessageBox 
    { 
     System.Threading.Timer _timeoutTimer; 
     string _caption; 
     AutoClosingMessageBox(string text, string caption, int timeout) 
     { 
      _caption = caption; 
      _timeoutTimer = new System.Threading.Timer(OnTimerElapsed, 
       null, timeout, System.Threading.Timeout.Infinite); 
      MessageBox.Show(text, caption); 
     } 
     public static void Show(string text, string caption, int timeout) 
     { 
      new AutoClosingMessageBox(text, caption, timeout); 
     } 
     void OnTimerElapsed(object state) 
     { 
      IntPtr mbWnd = FindWindow(null, _caption); 
      if (mbWnd != IntPtr.Zero) 
       SendMessage(mbWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); 
      _timeoutTimer.Dispose(); 
     } 
     const int WM_CLOSE = 0x0010; 
     [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)] 
     static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 
     [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)] 
     static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); 
    } 

e chiamare tramite

AutoClosingMessageBox.Show("Content", "Title", TimeOut); 
Problemi correlati