Закройте MessageBox через несколько секунд
У меня есть приложение Windows Forms VS2010 С#, где я показываю MessageBox для отображения сообщения.
У меня хорошая кнопка, но если они уйдут, я хочу тайм-аут и закрою окно сообщения, если скажет 5 секунд, автоматически закройте окно сообщения.
Есть специальные MessageBox (которые унаследованы от Form) или другие репортерные формы, но было бы интересно не использовать форму.
Любые предложения или образцы об этом?
Обновлено:
Для WPF
Автоматически закрывать окно сообщений на С#
Пользовательский 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
http://medmondson2011.wordpress.com/2010/04/07/easy-to-use-custom-c-message-box-with-a-configurable-checkbox/
Прокручиваемый MessageBox
Прокручиваемый MessageBox на С#
Репортер исключений
https://stackoverflow.com/info/49224/good-crash-reporting-library-in-c-sharp
http://www.codeproject.com/Articles/6895/A-Reusable-Flexible-Error-Reporting-Framework
Решение:
Возможно, я думаю, что следующие ответы являются хорошим решением, без использования формы.
qaru.site/info/97513/...
qaru.site/info/97513/...
Ответы
Ответ 1
Попробуйте использовать следующий подход:
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
кнопками YesNo
и обнаружил, что подход с отправкой сообщения WM_CLOSE
вообще не работает.
Я предоставлю исправление в контексте отдельной библиотеки AutoclosingMessageBox. Эта библиотека содержит переработанный подход и, я считаю, может быть полезна кому-то.
Он также доступен через пакет NuGet:
Install-Package AutoClosingMessageBox
Примечания к выпуску (v1.0.0.2):
- API New Show (IWin32Owner) для поддержки большинства популярных сценариев (в контексте # 1);
- API New Factory(), чтобы обеспечить полный контроль над показом MessageBox;
Ответ 2
Решение, которое работает в 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
с параметрами по умолчанию).
Ответ 3
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(" ");
}
Теперь помывайте руки!
Ответ 4
Метод System.Windows.MessageBox.Show() имеет перегрузку, которая занимает окно владельца в качестве первого параметра. Если мы создадим окно невидимого владельца, которое мы затем закроем по истечении заданного времени, оно также закроет окно дочернего сообщения.
Window owner = CreateAutoCloseWindow(dialogTimeout);
MessageBoxResult result = MessageBox.Show(owner, ...
Пока все хорошо. Но как закрыть окно, если поток пользовательского интерфейса заблокирован блоком сообщений, а элементы управления пользовательского интерфейса недоступны из рабочего потока? Ответ - отправка сообщения окна 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;
}
И вот импорт метода SendMessage Windows API:
static class NativeMethods
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
}
Ответ 5
Вы можете попробовать следующее:
[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);
}
Ответ 6
У RogerB над CodeProject есть одно из самых сложных решений для этого ответа, и он сделал это еще в '04, и он все еще bangin '
В принципе, вы идете сюда в свой проект и загружаете CS файл. В случае, если ссылка когда - либо умирает, у меня есть резервная суть здесь. Добавьте файл CS в свой проект или скопируйте/вставьте код где-нибудь, если вы предпочтете это сделать.
Тогда все, что вам нужно сделать, это переключить
DialogResult result = MessageBox.Show("Text","Title", MessageBoxButtons.CHOICE)
в
DialogResult result = MessageBoxEx.Show("Text","Title", MessageBoxButtons.CHOICE, timer_ms)
И тебе хорошо идти.
Ответ 7
Существует проект кодекса проекта ЗДЕСЬ, который обеспечивает эту функциональность.
После многих потоков здесь на SO и других платах это невозможно сделать с помощью обычного MessageBox.
Edit:
У меня есть идея, которая немного эммма да..
Используйте таймер и начинайте, когда появляется MessageBox.
Если ваш MessageBox только прослушивает кнопку OK (только 1 возможность), используйте OnTick-Event для эмуляции ESC-Press с помощью SendKeys.Send("{ESC}");
и затем остановки таймера.
Ответ 8
Код DMitryG "получить возвращаемое значение базового MessageBox
" имеет ошибку, так что timerResult никогда не будет правильно возвращен (MessageBox.Show
возвращает OnTimerElapsed
завершения 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);
}
Ответ 9
Библиотека 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);
}
Ответ 10
В user32.dll есть недокументированный API с именем MessageBoxTimeout(), но для него требуется Windows XP или более поздняя версия.
Ответ 11
используйте EndDialog
вместо отправки WM_CLOSE
:
[DllImport("user32.dll")]
public static extern int EndDialog(IntPtr hDlg, IntPtr nResult);
Ответ 12
Я сделал это так
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);