C# 几秒钟后关闭 MessageBox

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14522540/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 12:09:31  来源:igfitidea点击:

Close a MessageBox after several seconds

c#winformsmessagebox

提问by Kiquenet

I have a Windows Forms application VS2010 C# where I display a MessageBox for show a message.

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

I have an okay button, but if they walk away, I want to timeout and close the message box after lets say 5 seconds, automatically close the message box.

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

There are custom MessageBox (that inherited from Form) or another reporter Forms, but it would be interesting not necessary a Form.

有自定义 MessageBox(从 Form 继承)或其他报告器 Forms,但有趣的是不需要 Form。

Any suggestions or samples about it?

有什么建议或样品吗?

Updated:

更新:

For WPF
Automatically close messagebox in C#

对于 WPF
在 C# 中自动关闭消息框

Custom MessageBox (using Form inherit)
http://www.codeproject.com/Articles/17253/A-Custom-Message-Box

自定义 MessageBox(使用 Form 继承)
http://www.codeproject.com/Articles/17253/A-Custom-Message-Box

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

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://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/

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

Scrollable MessageBox
A Scrollable MessageBox in C#

可滚动消息框
C# 中的可滚动消息框

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

异常报告器
https://stackoverflow.com/questions/49224/good-crash-reporting-library-in-c-sharp

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

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

Solution:

解决方案:

Maybe I think the following answers are good solution, without use a Form.

也许我认为以下答案是很好的解决方案,无需使用表单。

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

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

采纳答案by DmitryG

Try the following approach:

尝试以下方法:

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

Where the AutoClosingMessageBoxclass implemented as following:

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

Update:If you want to get the return value of the underlying MessageBox when user selects something before the timeout you can use the following version of this code:

更新:如果您想在用户在超时之前选择某些内容时获取底层 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);
}

Yet another Update

又一个更新

I have checked the @Hyman's case with YesNobuttons and discovered that the approach with sending the WM_CLOSEmessage does not work at all.
I will provide a fixin the context of the separate AutoclosingMessageBoxlibrary. This library contains redesigned approach and, I believe, can be useful to someone.
It also available via NuGet package:

我用YesNo按钮检查了@Hyman 的情况,发现发送WM_CLOSE消息的方法根本不起作用。
我将在单独的AutoclosureMessageBox库的上下文中提供修复。这个库包含重新设计的方法,我相信对某人有用。 它也可以通过NuGet 包获得

Install-Package AutoClosingMessageBox

Release Notes (v1.0.0.2):
- New Show(IWin32Owner) API to support most popular scenarios (in the context of #1);
- New Factory() API to provide full control on MessageBox showing;

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

回答by jAC

There is an codeproject project avaliable HEREthat provides this functuanility.

有一个CodeProject上项目avaliable这里提供此functuanility。

Following many threads here on SO and other boards this cant be done with the normal MessageBox.

在 SO 和其他板上遵循许多线程,这是无法使用普通 MessageBox 完成的。

Edit:

编辑:

I have an idea that is a bit ehmmm yeah..

我有一个想法,有点 ehmmm 是的..

Use a timer and start in when the MessageBox appears. If your MessageBox only listens to the OK Button (only 1 possibility) then use the OnTick-Event to emulate an ESC-Press with SendKeys.Send("{ESC}");and then stop the timer.

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

回答by Jens Granlund

You could try this:

你可以试试这个:

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

回答by FastAl

AppActivate!

应用激活!

If you don't mind muddying your references a bit, you can include Microsoft.Visualbasic,and use this very short way.

如果您不介意稍微混淆您的参考文献,您可以包含Microsoft.Visualbasic,和使用这种非常简短的方式。

Display the MessageBox

显示消息框

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

CloseIt Function:

关闭功能:

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

Now go wash your hands!

现在去洗手!

回答by Esge

The System.Windows.MessageBox.Show() method has an overload which takes an owner Window as the first parameter. If we create an invisible owner Window which we then close after a specified time, it's child message box would close as well.

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

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

So far so good. But how do we close a window if the UI thread is blocked by the message box and UI controls can't be accessed from a worker thread? The answer is - by sending a WM_CLOSE windows message to the owner window handle:

到现在为止还挺好。但是,如果 UI 线程被消息框阻塞并且无法从工作线程访问 UI 控件,我们如何关闭窗口?答案是 - 通过向所有者窗口句柄发送 WM_CLOSE windows 消息:

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

And here is the import for the SendMessage Windows API method:

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

回答by BSharp

A solution that works in WinForms:

一个适用于 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);

Based on the effect that closing the form that owns the message box will close the box as well.

基于关闭拥有消息框的表单也会关闭该框的效果。

Windows Forms controls have a requirement that they must be accessed on the same thread that created them. Using TaskScheduler.FromCurrentSynchronizationContext()will ensure that, assuming that the example code above is executed on the UI thread, or an user-created thread. The example will not work correctly if the code is executed on a thread from a thread pool (e.g. a timer callback) or a task pool (e.g. on a task created with TaskFactory.StartNewor Task.Runwith default parameters).

Windows 窗体控件要求必须在创建它们的同一线程上访问它们。TaskScheduler.FromCurrentSynchronizationContext()假设上面的示例代码在 UI 线程或用户创建的线程上执行,使用将确保。如果代码是在来自线程池(例如计时器回调)或任务池(例如使用TaskFactory.StartNewTask.Run使用默认参数创建的任务)的线程上执行的,则该示例将无法正常工作。

回答by kayleeFrye_onDeck

RogerBover at CodeProject has one of the slickest solutions to this answer, and he did that back in '04, and it's still bangin'

CodeProject 的 RogerB对这个答案提出了最巧妙的解决方案之一,他在 04 年就这样做了,现在仍然很成功

Basically, you go here to his project and download the CS file. In case that link ever dies, I've got a backup gisthere. Add the CS file to your project, or copy/paste the code somewhere if you'd rather do that.

基本上,您到这里访问他的项目并下载 CS 文件。万一该链接失效,我在这里有一个备份要点。将 CS 文件添加到您的项目中,或者如果您愿意,可以将代码复制/粘贴到某处。

Then, all you'd have to do is switch

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

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

to

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

And you're good to go.

你很高兴去。

回答by Greg Wittmeyer

There is an undocumented API in user32.dll named MessageBoxTimeout() but it requires Windows XP or later.

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

回答by OnWeb

DMitryG's code "get the return value of the underlying MessageBox" has a bug so the timerResult is never actually correctly returned (MessageBox.Showcall returns AFTER OnTimerElapsedcompletes). My fix is below:

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

回答by Kaven Wu

use EndDialoginstead of sending WM_CLOSE:

使用EndDialog而不是发送WM_CLOSE

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