У меня есть приложение Windows Forms VS2010 C #, где я показываю MessageBox для отображения сообщения.
У меня есть кнопка "ОК", но если они уйдут, я хочу, чтобы тайм-аут и закрыть окно сообщения, скажем, через 5 секунд, автоматически закрыть окно сообщения.
Существуют настраиваемые MessageBox (унаследованные от Form) или формы другого репортера, но было бы интересно, не обязательно Form.
Есть предложения или образцы по этому поводу?
Обновлено:
Для WPF
автоматическое закрытие окна сообщений в C #
Пользовательский MessageBox (с использованием наследования формы)
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
Прокручиваемое окно сообщений
Прокручиваемое окно сообщений в C #
Репортер исключений
/programming/49224/good-crash-reporting-library-in-c-sharp
http://www.codeproject.com/Articles/6895/A-Reusable-F flexible-Error-Reporting-Framework
Решение:
Возможно, я думаю, что следующие ответы - хорошее решение без использования формы.
https://stackoverflow.com/a/14522902/206730
https://stackoverflow.com/a/14522952/206730
источник
MessageBox
Ответы:
Попробуйте следующий подход:
AutoClosingMessageBox.Show("Text", "Caption", 1000);
Где
AutoClosingMessageBox
класс реализован следующим образом: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); }
Обновление: если вы хотите получить возвращаемое значение базового MessageBox, когда пользователь что-то выбирает до истечения времени ожидания, вы можете использовать следующую версию этого кода:
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); }
Еще одно обновление
Я проверил футляр
YesNo
@Jack с кнопками и обнаружил, что подход с отправкойWM_CLOSE
сообщения вообще не работает.Я предоставлю исправление в контексте отдельной библиотеки AutoclosingMessageBox . Эта библиотека содержит переработанный подход и, я считаю, может быть кому-то полезна.
Он также доступен через пакет NuGet :
Примечания к выпуску (v1.0.0.2):
- Новый API Show (IWin32Owner) для поддержки наиболее популярных сценариев (в контексте №1 );
- Новый Factory () API для обеспечения полного контроля над показом MessageBox;
источник
#32770
значение в качестве имени классаSystem.Windows.Forms.MessageBoxButtons.YesNo
YesNo
кнопками - вы абсолютно правы - не работает. Я предоставлю исправление в контексте отдельной библиотеки AutoclosingMessageBox . Он содержит переработанный подход и, я считаю, может быть полезен. Благодаря!Решение, работающее в WinForms:
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);
Основываясь на эффекте, заключающемся в том, что закрытие формы, которая владеет окном сообщения, также закроет окно.
Для элементов управления Windows Forms требуется доступ к ним в том же потоке, в котором они были созданы. Использование
TaskScheduler.FromCurrentSynchronizationContext()
гарантирует, что при условии, что приведенный выше пример кода выполняется в потоке пользовательского интерфейса или потоке, созданном пользователем. Пример не будет работать правильно, если код выполняется в потоке из пула потоков (например, обратный вызов таймера) или пула задач (например, в задаче, созданной с параметрами по умолчаниюTaskFactory.StartNew
илиTask.Run
с ними).источник
Task
иTaskScheduler
находятся из пространства именSystem.Threading.Tasks
в mscorlib.dll, поэтому дополнительные ссылки на сборку не требуются.w.SentToBack()
передMessageBox.Show()
, диалоговое окно все еще показано на верхней части главной формы. Проверено на .NET 4.5 и 4.7.1.AppActivate!
Если вы не против немного запутать свои ссылки, вы можете включить
Microsoft.Visualbasic,
и использовать этот очень короткий способ.Отображение MessageBox
(new System.Threading.Thread(CloseIt)).Start(); MessageBox.Show("HI");
CloseIt Функция:
public void CloseIt() { System.Threading.Thread.Sleep(2000); Microsoft.VisualBasic.Interaction.AppActivate( System.Diagnostics.Process.GetCurrentProcess().Id); System.Windows.Forms.SendKeys.SendWait(" "); }
А теперь вымойте руки!
источник
Метод System.Windows.MessageBox.Show () имеет перегрузку, которая принимает окно владельца в качестве первого параметра. Если мы создадим невидимое окно-владелец, которое затем закроем через определенное время, его дочернее окно сообщения также закроется.
Все идет нормально. Но как закрыть окно, если поток пользовательского интерфейса заблокирован окном сообщения, а элементы управления пользовательского интерфейса не могут быть доступны из рабочего потока? Ответ - отправив сообщение Windows WM_CLOSE дескриптору окна владельца:
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; }
А вот импорт для метода Windows API SendMessage:
static class NativeMethods { [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); }
источник
System.Windows
иSystem.Windows.Forms
мне потребовалось время, чтобы разобраться. Вам потребуется следующее:System
,System.Runtime.InteropServices
,System.Threading.Tasks
,System.Windows
,System.Windows.Interop
,System.Windows.Media
Вы можете попробовать это:
[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); }
источник
У RogerB из CodeProject есть одно из лучших решений для этого ответа, и он сделал это еще в '04, и это все еще работает
По сути, вы заходите сюда, в его проект и загружаете файл CS . В случае, если ссылка когда - либо умирает, у меня есть резервная суть здесь. Добавьте CS-файл в свой проект или скопируйте / вставьте код куда-нибудь, если хотите.
Тогда все, что вам нужно сделать, это переключить
DialogResult result = MessageBox.Show("Text","Title", MessageBoxButtons.CHOICE)
к
DialogResult result = MessageBoxEx.Show("Text","Title", MessageBoxButtons.CHOICE, timer_ms)
И тебе хорошо идти.
источник
ЗДЕСЬ доступен проект codeproject, который обеспечивает эту функциональность.
Следуя многим темам здесь, на SO и других досках, это невозможно сделать с помощью обычного MessageBox.
Редактировать:
У меня есть идея, немного эммм да ..
Используйте таймер и начните, когда появится MessageBox. Если ваш MessageBox слушает только кнопку OK (только одна возможность), тогда используйте OnTick-Event для эмуляции ESC-Press с помощью
SendKeys.Send("{ESC}");
и затем остановите таймер.источник
Код DMitryG «получить возвращаемое значение базового
MessageBox
» имеет ошибку, поэтому timerResult никогда не возвращается правильно (MessageBox.Show
вызов возвращается ПОСЛЕOnTimerElapsed
завершения). Мое исправление ниже: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); }
источник
У библиотеки Vb.net есть простое решение, использующее для этого класс взаимодействия:
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); }
источник
В user32.dll есть недокументированный API с именем MessageBoxTimeout (), но для него требуется Windows XP или более поздняя версия.
источник
использовать
EndDialog
вместо отправкиWM_CLOSE
:[DllImport("user32.dll")] public static extern int EndDialog(IntPtr hDlg, IntPtr nResult);
источник
Я сделал это вот так
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);
источник