几秒钟后关闭一个MessageBox

时间:2013-01-25 13:14:23

标签: c# winforms messagebox

我有一个Windows窗体应用程序VS2010 C#,其中我显示一个MessageBox以显示消息。

我有一个好的按钮,但是如果他们走开了,我想超时并在让我们说5秒后关闭消息框,自动关闭消息框。

有自定义MessageBox(继承自Form)或其他记者表单,但有趣的是没有必要使用Form。

有关它的任何建议或样品吗?

已更新:

对于WPF
Automatically close messagebox in 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

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

可滚动的MessageBox
A Scrollable MessageBox in C#

例外记者
https://stackoverflow.com/questions/49224/good-crash-reporting-library-in-c-sharp

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

解决方案:

也许我认为以下答案是很好的解决方案,而不使用表格。

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

14 个答案:

答案 0 :(得分:111)

尝试以下方法:

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 package

获得
Install-Package AutoClosingMessageBox

发行说明(v1.0.0.2):
- 新节目(IWin32Owner)API,以支持最流行的场景(在 #1)的背景;
- 新的Factory()API,提供对MessageBox显示的完全控制;

答案 1 :(得分:20)

适用于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窗体控件要求必须在创建它们的同一线程上访问它们。使用TaskScheduler.FromCurrentSynchronizationContext()将确保假定上面的示例代码在UI线程或用户创建的线程上执行。如果代码在线程池(例如计时器回调)或任务池(例如,使用TaskFactory.StartNew创建的任务或使用默认参数创建的Task.Run上)执行,则该示例将无法正常运行

答案 2 :(得分:15)

则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(" ");
}

现在去洗手!

答案 3 :(得分:11)

System.Windows.MessageBox.Show()方法有一个重载,它将所有者Window作为第一个参数。如果我们创建一个不可见的所有者窗口,然后我们在指定的时间后关闭它,那么它的子消息框也会关闭。

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

到目前为止一切顺利。但是,如果UI线程被消息框阻止,并且无法从工作线程访问UI控件,我们如何关闭窗口?答案是 - 通过向所有者窗口句柄发送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);
}

答案 4 :(得分:10)

你可以试试这个:

[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);
}

答案 5 :(得分:4)

CodeProject上的{p> RogerB对这个答案有一个最简单的解决方案,他在2004年做到了这一点,它仍然是bangin'

基本上,你是go here to his project and download the CS file。如果链接死了,我在这里有一个备份gist。将CS文件添加到项目中,或者如果您愿意,可以将代码复制/粘贴到某个位置。

然后,你所要做的就是切换

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

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

你很高兴。

答案 6 :(得分:2)

有一个可提供这种功能的代码项目项目HERE

在SO和其他主板上有许多线程,这不能用普通的MessageBox完成。

编辑:

我有一个想法,有点嗯..是的。

使用计时器并在MessageBox出现时启动。 如果您的MessageBox仅侦听OK按钮(仅1种可能性),则使用OnTick-Event模拟带有SendKeys.Send("{ESC}");的ESC-Press,然后停止计时器。

答案 7 :(得分:2)

DMitryG的代码“获取底层MessageBox的返回值”有一个错误,因此从未实际正确返回timerResult(MessageBox.Show调用返回AFTER 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);
}

答案 8 :(得分:1)

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);
}

答案 9 :(得分:0)

user32.dll中有一个名为MessageBoxTimeout()的未记录的API,但它需要Windows XP或更高版本。

答案 10 :(得分:0)

使用EndDialog而不是发送WM_CLOSE

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

答案 11 :(得分:0)

我是这样做的

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);

答案 12 :(得分:0)

如果有人想要 c++ 中的 AutoClosingMessageBox,我已经实现了等效的代码,这里是 gists

的链接
static intptr_t MessageBoxHookProc(int nCode, intptr_t wParam, intptr_t lParam)
{
    if (nCode < 0)
        return CallNextHookEx(hHook, nCode, wParam, lParam);

    auto msg = reinterpret_cast<CWPRETSTRUCT*>(lParam);
    auto hook = hHook;

    //Hook Messagebox on Initialization.
    if (!hookCaption.empty() && msg->message == WM_INITDIALOG)
    {
        int nLength = GetWindowTextLength(msg->hwnd);
        char* text = new char[captionLen + 1];

        GetWindowText(msg->hwnd, text, captionLen + 1);

        //If Caption window found Unhook it.
        if (hookCaption == text)
        {
            hookCaption = string("");
            SetTimer(msg->hwnd, (uintptr_t)timerID, hookTimeout, (TIMERPROC)hookTimer);
            UnhookWindowsHookEx(hHook);
            hHook = 0;
        }
    }

    return CallNextHookEx(hook, nCode, wParam, lParam);
}

答案 13 :(得分:0)

我知道这个问题已经有 8 年的历史了,但是为此目的有更好的解决方案。它一直存在,现在仍然是:User32.dll!MessageBoxTimeout

这是 Microsoft Windows 使用的一个未公开的函数,它完全符合您的要求,甚至更多。它也支持不同的语言。

C# 导入:

[DllImport("user32.dll", SetLastError = true)]
public static extern int MessageBoxTimeout(IntPtr hWnd, String lpText, String lpCaption, uint uType, Int16 wLanguageId, Int32 dwMilliseconds);

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetForegroundWindow();

如何在 C# 中使用它:

uint uiFlags = /*MB_OK*/ 0x00000000 | /*MB_SETFOREGROUND*/  0x00010000 | /*MB_SYSTEMMODAL*/ 0x00001000 | /*MB_ICONEXCLAMATION*/ 0x00000030;

NativeFunctions.MessageBoxTimeout(NativeFunctions.GetForegroundWindow(), $"Kitty", $"Hello", uiFlags, 0, 5000);

更聪明地工作,而不是更努力。