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 feststellen und das Nachrichtenfeld schließen, nachdem wir beispielsweise 5 Sekunden vergangen sind. Schließen Sie das Nachrichtenfeld automatisch.
Es gibt benutzerdefinierte MessageBox (die von Form geerbt wurden) oder andere Reporterformulare, aber es wäre interessant, kein Formular zu benötigen.
Anregungen oder Beispiele dazu?
Aktualisiert:
Für WPF
Nachrichtenbox in C # automatisch schließen
Benutzerdefinierte MessageBox (mithilfe der Formularvererbung)
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
Scrollbare MessageBox
Eine scrollbare MessageBox in C #
Exception Reporter
/programming/49224/good-crash-reporting-library-in-c-sharp
http://www.codeproject.com/Articles/6895/A-Reusable-Flexible-Error-Reporting-Framework
Lösung:
Vielleicht denke ich, dass die folgenden Antworten eine gute Lösung sind, ohne ein Formular zu verwenden.
https://stackoverflow.com/a/14522902/206730
https://stackoverflow.com/a/14522952/206730
quelle
MessageBox
Antworten:
Versuchen Sie den folgenden Ansatz:
AutoClosingMessageBox.Show("Text", "Caption", 1000);
Wo die
AutoClosingMessageBox
Klasse 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 erhalten 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
Schaltflächen überprüft und festgestellt, dass der Ansatz beim Senden derWM_CLOSE
Nachricht überhaupt nicht funktioniert.Ich werde einen Fix im Kontext der separaten AutoclosingMessageBox- Bibliothek bereitstellen . Diese Bibliothek enthält einen neu gestalteten Ansatz und kann meiner Meinung nach für jemanden nützlich sein.
Es ist auch über das NuGet-Paket erhältlich :
Versionshinweise (v1.0.0.2):
- Neue Show (IWin32Owner) -API zur Unterstützung der gängigsten Szenarien (im Kontext von # 1 );
- Neue Factory () - API zur vollständigen Kontrolle der Anzeige von MessageBox;
quelle
#32770
Wert als Klassennamen verwendenSystem.Windows.Forms.MessageBoxButtons.YesNo
YesNo
Knöpfen überprüft - Sie sind absolut korrekt - es funktioniert nicht. Ich werde im Kontext der separaten AutoclosingMessageBox- Bibliothek einen Fix bereitstellen . Das enthält den neu gestalteten Ansatz und kann meiner Meinung nach nützlich sein. Vielen Dank!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 Nachrichtenfeld gehört, das Feld ebenfalls schließt.
Für Windows Forms-Steuerelemente muss auf denselben Thread zugegriffen werden, mit dem sie erstellt wurden. Mit
TaskScheduler.FromCurrentSynchronizationContext()
wird sichergestellt, 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 in einem Thread aus einem Thread-Pool (z. B. einem Timer-Rückruf) oder einem Task-Pool (z. B. in einer Task, die mitTaskFactory.StartNew
oderTask.Run
mit Standardparametern erstellt wurde) ausgeführt wird.quelle
Task
undTaskScheduler
stammen aus dem NamespaceSystem.Threading.Tasks
in mscorlib.dll, sodass keine zusätzlichen Assemblyreferenzen erforderlich sind.w.SentToBack()
direkt vorher anrufenMessageBox.Show()
, wird das Dialogfeld immer noch über dem Hauptformular angezeigt. Getestet unter .NET 4.5 und 4.7.1.AppActivate!
Wenn es Ihnen nichts ausmacht, Ihre Referenzen ein wenig zu verwirren, können Sie
Microsoft.Visualbasic,
diesen sehr kurzen Weg einschließen und verwenden.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(" "); }
Jetzt geh und wasche deine Hände!
quelle
Die System.Windows.MessageBox.Show () -Methode weist eine Überladung auf, bei der ein Eigentümerfenster als erster Parameter verwendet wird. Wenn wir ein unsichtbares Eigentümerfenster erstellen, das wir nach einer bestimmten Zeit schließen, wird auch das untergeordnete Meldungsfeld geschlossen.
So weit, ist es gut. Aber wie schließen wir ein Fenster, wenn der UI-Thread durch das Meldungsfeld blockiert wird und auf UI-Steuerelemente nicht über einen Worker-Thread zugegriffen werden kann? Die Antwort lautet: Durch Senden einer WM_CLOSE-Fensternachricht an das Eigentümerfenster-Handle:
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); }
quelle
System.Windows
undSystem.Windows.Forms
ich habe einige Zeit gebraucht, um das herauszufinden. Sie müssen die folgenden:System
,System.Runtime.InteropServices
,System.Threading.Tasks
,System.Windows
,System.Windows.Interop
,System.Windows.Media
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); }
quelle
RogerB bei CodeProject hat eine der cleversten Lösungen für diese Antwort, und das hat er bereits im Jahr 2004 getan, und es ist immer noch großartig.
Grundsätzlich gehen Sie hier zu seinem Projekt und laden die CS-Datei herunter . Für den Fall, dass dieser Link jemals stirbt, habe ich hier einen Backup- Kern . Fügen Sie die CS-Datei zu Ihrem Projekt hinzu oder kopieren Sie den Code irgendwo, wenn Sie dies lieber tun möchten.
Dann müssten Sie nur noch wechseln
DialogResult result = MessageBox.Show("Text","Title", MessageBoxButtons.CHOICE)
zu
DialogResult result = MessageBoxEx.Show("Text","Title", MessageBoxButtons.CHOICE, timer_ms)
Und du kannst loslegen.
quelle
HIER ist ein Codeprojekt-Projekt verfügbar , das diese Funktionalität bietet.
Nach vielen Threads hier auf SO und anderen Boards kann dies nicht mit der normalen MessageBox durchgeführt 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 auf die Schaltfläche OK hört (nur 1 Möglichkeit), emulieren Sie mit dem OnTick-Ereignis eine ESC-Presse mit
SendKeys.Send("{ESC}");
und stoppen Sie dann den Timer.quelle
Der Code von DMitryG "Rückgabewert des Basiswerts
MessageBox
abrufen" weist einen Fehler auf, sodass das timerResult nie korrekt zurückgegeben wird (MessageBox.Show
Aufruf wird nachOnTimerElapsed
Abschluss zurückgegeben). 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); }
quelle
Die Vb.net-Bibliothek bietet hierfür eine einfache Lösung mit Interaktionsklasse:
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); }
quelle
In user32.dll gibt es eine undokumentierte API mit dem Namen MessageBoxTimeout (), für die jedoch Windows XP oder höher erforderlich ist.
quelle
Verwenden Sie
EndDialog
statt zu sendenWM_CLOSE
:[DllImport("user32.dll")] public static extern int EndDialog(IntPtr hDlg, IntPtr nResult);
quelle
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);
quelle