it-swarm.com.de

Schließen Sie eine MessageBox nach einigen Sekunden

Ich habe eine Windows Forms-Anwendung VS2010 C #, in der ich eine MessageBox zum Anzeigen einer Nachricht anzeige.

Ich habe eine OK-Schaltfläche, aber wenn sie weggehen, möchte ich eine Zeitüberschreitung und das Meldungsfeld schließen, nachdem wir sagen, 5 Sekunden, das Meldungsfeld automatisch schließen.

Es gibt benutzerdefinierte MessageBox-Formulare (die von Form geerbt wurden) oder andere Reporter-Formulare, aber es wäre interessant, kein Formular zu benötigen.

Anregungen oder Muster dazu?

Aktualisiert:

Für WPF
Nachrichtenbox in C # automatisch schließen

Benutzerdefinierte MessageBox (mithilfe von Form übernehmen)
http://www.codeproject.com/Articles/17253/A-Custom-Message-Box

http://www.codeproject.com/Articles/327212/Custom-Message-Box-in-VC

http://tutplusplus.blogspot.com.es/2010/07/c-tutorial-create-your-own-custom.html

http://medmondson2011.wordpress.com/2010/04/07/easy-to-use-custom-c-message-box-with-a-configurable-checkbox/

Scrollbare MessageBox
Eine scrollbare MessageBox in C #

Ausnahmereporter
https://stackoverflow.com/questions/49224/good-crash-reporting-library-in-c-sharp

http://www.codeproject.com/Articles/6895/A-Reusable-Flexible-Error-Reporting-Framework

Lösung:

Vielleicht halte ich die folgenden Antworten für eine gute Lösung, ohne ein Formular zu verwenden.

https://stackoverflow.com/a/14522902/2067
https://stackoverflow.com/a/14522952/2067

68
Kiquenet

Versuchen Sie den folgenden Ansatz:

AutoClosingMessageBox.Show("Text", "Caption", 1000);

Wobei die Klasse AutoClosingMessageBox wie folgt implementiert wurde:

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);
        using(_timeoutTimer)
            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("#32770", _caption); // lpClassName is #32770 for MessageBox
        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);
}

Update: Wenn Sie den Rückgabewert der zugrunde liegenden MessageBox abrufen möchten, wenn der Benutzer vor dem Timeout etwas auswählt, können Sie die folgende Version dieses Codes verwenden:

var userResult = AutoClosingMessageBox.Show("Yes or No?", "Caption", 1000, MessageBoxButtons.YesNo);
if(userResult == System.Windows.Forms.DialogResult.Yes) { 
    // do something
}
...
public class AutoClosingMessageBox {
    System.Threading.Timer _timeoutTimer;
    string _caption;
    DialogResult _result;
    DialogResult _timerResult;
    AutoClosingMessageBox(string text, string caption, int timeout, MessageBoxButtons buttons = MessageBoxButtons.OK, DialogResult timerResult = DialogResult.None) {
        _caption = caption;
        _timeoutTimer = new System.Threading.Timer(OnTimerElapsed,
            null, timeout, System.Threading.Timeout.Infinite);
        _timerResult = timerResult;
        using(_timeoutTimer)
            _result = MessageBox.Show(text, caption, buttons);
    }
    public static DialogResult Show(string text, string caption, int timeout, MessageBoxButtons buttons = MessageBoxButtons.OK, DialogResult timerResult = DialogResult.None) {
        return new AutoClosingMessageBox(text, caption, timeout, buttons, timerResult)._result;
    }
    void OnTimerElapsed(object state) {
        IntPtr mbWnd = FindWindow("#32770", _caption); // lpClassName is #32770 for MessageBox
        if(mbWnd != IntPtr.Zero)
            SendMessage(mbWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
        _timeoutTimer.Dispose();
        _result = _timerResult;
    }
    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);
}

Noch ein Update

Ich habe den Fall von @ Jack mit YesNo überprüft und festgestellt, dass die Vorgehensweise beim Senden des WM_CLOSE Nachricht funktioniert überhaupt nicht.
Ich werde fix im Kontext der separaten AutoclosingMessageBox Bibliothek bereitstellen. Diese Bibliothek enthält einen überarbeiteten Ansatz und kann, glaube ich, für jemanden nützlich sein.
Erhältlich auch über NuGet-Paket :

Install-Package AutoClosingMessageBox

Versionshinweise (v1.0.0.2):
- Neue Show (IWin32Owner) API zur Unterstützung der beliebtesten Szenarien (im Kontext von # 1 );
- Neue Factory () -API für die vollständige Kontrolle der MessageBox-Anzeige;

113
DmitryG

Eine Lösung, die in WinForms funktioniert:

var w = new Form() { Size = new Size(0, 0) };
Task.Delay(TimeSpan.FromSeconds(10))
    .ContinueWith((t) => w.Close(), TaskScheduler.FromCurrentSynchronizationContext());

MessageBox.Show(w, message, caption);

Basierend auf dem Effekt, dass das Schließen des Formulars, dem das Meldungsfeld gehört, das Feld ebenfalls schließt.

Für Windows Forms-Steuerelemente muss der Zugriff auf denselben Thread erfolgen, der sie erstellt hat. Die Verwendung von TaskScheduler.FromCurrentSynchronizationContext() stellt sicher, dass der obige Beispielcode auf dem UI-Thread oder einem vom Benutzer erstellten Thread ausgeführt wird. Das Beispiel funktioniert nicht richtig, wenn der Code für einen Thread aus einem Thread-Pool (z. B. einen Timer-Rückruf) oder einen Task-Pool (z. B. für einen mit TaskFactory.StartNew Oder Task.Run Standardmäßig erstellten Task) ausgeführt wird Parameter).

21
BSharp

AppActivate!

Wenn es Ihnen nichts ausmacht, Ihre Referenzen ein wenig zu verwischen, können Sie Microsoft.Visualbasic, und benutze diesen sehr kurzen Weg.

Zeigen Sie die MessageBox an

    (new System.Threading.Thread(CloseIt)).Start();
    MessageBox.Show("HI");

CloseIt-Funktion:

public void CloseIt()
{
    System.Threading.Thread.Sleep(2000);
    Microsoft.VisualBasic.Interaction.AppActivate( 
         System.Diagnostics.Process.GetCurrentProcess().Id);
    System.Windows.Forms.SendKeys.SendWait(" ");
}

Waschen Sie sich jetzt die Hände!

15
FastAl

Die System.Windows.MessageBox.Show () -Methode weist eine Überladung auf, die ein Eigentümerfenster als ersten Parameter verwendet. Wenn wir ein unsichtbares Besitzerfenster erstellen, das wir nach einer bestimmten Zeit schließen, wird auch das untergeordnete Meldungsfeld geschlossen.

Window owner = CreateAutoCloseWindow(dialogTimeout);
MessageBoxResult result = MessageBox.Show(owner, ...

So weit, ist es gut. Aber wie schließen wir ein Fenster, wenn der Benutzeroberflächenthread durch das Meldungsfeld blockiert ist und über einen Arbeitsthread nicht auf Benutzeroberflächensteuerelemente zugegriffen werden kann? Die Antwort lautet - durch Senden einer WM_CLOSE-Fensternachricht an das Fensterhandle des Besitzers:

Window CreateAutoCloseWindow(TimeSpan timeout)
{
    Window window = new Window()
    {
        WindowStyle = WindowStyle.None,
        WindowState = System.Windows.WindowState.Maximized,
        Background =  System.Windows.Media.Brushes.Transparent, 
        AllowsTransparency = true,
        ShowInTaskbar = false,
        ShowActivated = true,
        Topmost = true
    };

    window.Show();

    IntPtr handle = new WindowInteropHelper(window).Handle;

    Task.Delay((int)timeout.TotalMilliseconds).ContinueWith(
        t => NativeMethods.SendMessage(handle, 0x10 /*WM_CLOSE*/, IntPtr.Zero, IntPtr.Zero));

    return window;
}

Und hier ist der Import für die SendMessage Windows API Methode:

static class NativeMethods
{
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
}
11
Esge

Sie könnten dies versuchen:

[DllImport("user32.dll", EntryPoint="FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);

[DllImport("user32.Dll")]
static extern int PostMessage(IntPtr hWnd, UInt32 msg, int wParam, int lParam);

private const UInt32 WM_CLOSE = 0x0010;

public void ShowAutoClosingMessageBox(string message, string caption)
{
    var timer = new System.Timers.Timer(5000) { AutoReset = false };
    timer.Elapsed += delegate
    {
        IntPtr hWnd = FindWindowByCaption(IntPtr.Zero, caption);
        if (hWnd.ToInt32() != 0) PostMessage(hWnd, WM_CLOSE, 0, 0);
    };
    timer.Enabled = true;
    MessageBox.Show(message, caption);
}
10
Jens Granlund

RogerB drüben bei CodeProject hat eine der schlauesten Lösungen für diese Antwort.

Grundsätzlich können Sie gehen Sie hier zu seinem Projekt und laden Sie die CS-Datei herunter . Für den Fall, dass dieser Link jemals stirbt, habe ich ein Backup Gist hier. Fügen Sie die CS-Datei zu Ihrem Projekt hinzu, oder kopieren Sie den Code, oder fügen Sie ihn irgendwo ein, wenn Sie dies lieber tun möchten.

Dann brauchen Sie nur noch zu wechseln

DialogResult result = MessageBox.Show("Text","Title", MessageBoxButtons.CHOICE)

zu

DialogResult result = MessageBoxEx.Show("Text","Title", MessageBoxButtons.CHOICE, timer_ms)

Und du bist gut zu gehen.

4

DMitryGs Code "Hole den Rückgabewert des zugrundeliegenden MessageBox" hat einen Fehler, so dass das timerResult nie richtig zurückgegeben wird (MessageBox.Show call returniert NACH Abschluss von OnTimerElapsed). Mein Fix ist unten:

public class TimedMessageBox {
    System.Threading.Timer _timeoutTimer;
    string _caption;
    DialogResult _result;
    DialogResult _timerResult;
    bool timedOut = false;

    TimedMessageBox(string text, string caption, int timeout, MessageBoxButtons buttons = MessageBoxButtons.OK, DialogResult timerResult = DialogResult.None)
    {
        _caption = caption;
        _timeoutTimer = new System.Threading.Timer(OnTimerElapsed,
            null, timeout, System.Threading.Timeout.Infinite);
        _timerResult = timerResult;
        using(_timeoutTimer)
            _result = MessageBox.Show(text, caption, buttons);
        if (timedOut) _result = _timerResult;
    }

    public static DialogResult Show(string text, string caption, int timeout, MessageBoxButtons buttons = MessageBoxButtons.OK, DialogResult timerResult = DialogResult.None) {
        return new TimedMessageBox(text, caption, timeout, buttons, timerResult)._result;
    }

    void OnTimerElapsed(object state) {
        IntPtr mbWnd = FindWindow("#32770", _caption); // lpClassName is #32770 for MessageBox
        if(mbWnd != IntPtr.Zero)
            SendMessage(mbWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
        _timeoutTimer.Dispose();
        timedOut = true;
    }

    const int WM_CLOSE = 0x0010;
    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
    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);
}
2
OnWeb

Es gibt ein Codeprojekt, das verfügbar ist HIER , das diese Funktionalität bietet.

Nach vielen Threads hier auf SO und anderen Boards kann dies nicht mit der normalen MessageBox gemacht werden.

Bearbeiten:

Ich habe eine Idee, die ein bisschen ehmmm ist ja ..

Verwenden Sie einen Timer und starten Sie, wenn die MessageBox angezeigt wird. Wenn Ihre MessageBox nur den OK-Button abhört (nur 1 Möglichkeit), emulieren Sie mit dem OnTick-Event ein ESC-Press mit SendKeys.Send("{ESC}"); und stoppen Sie dann den Timer.

2
jAC

Vb.net Bibliothek hat eine einfache Lösung mit Interaktionsklasse für diese:

void MsgPopup(string text, string title, int secs = 3)
{
    dynamic intr = Microsoft.VisualBasic.Interaction.CreateObject("WScript.Shell");
    intr.Popup(text, secs, title);
}

bool MsgPopupYesNo(string text, string title, int secs = 3)
{
    dynamic intr = Microsoft.VisualBasic.Interaction.CreateObject("WScript.Shell");
    int answer = intr.Popup(text, secs, title, (int)Microsoft.VisualBasic.Constants.vbYesNo + (int)Microsoft.VisualBasic.Constants.vbQuestion);
    return (answer == 6);
}
1
Cenk

benutze EndDialog anstatt WM_CLOSE zu senden:

[DllImport("user32.dll")]
public static extern int EndDialog(IntPtr hDlg, IntPtr nResult);
0
Kaven Wu

Ich habe es so gemacht

var owner = new Form { TopMost = true };
Task.Delay(30000).ContinueWith(t => {
owner.Invoke(new Action(()=>
{
      if (!owner.IsDisposed)
      {
          owner.Close();
      }
   }));
});
var dialogRes =  MessageBox.Show(owner, msg, "Info", MessageBoxButtons.YesNo, MessageBoxIcon.Information);
0
santipianis

Es gibt eine undokumentierte API in user32.dll mit dem Namen MessageBoxTimeout (), für die jedoch Windows XP oder höher) erforderlich ist.

0
Gammadyne