几秒钟后关闭MessageBox


80

我有一个Windows Forms应用程序VS2010 C#,在其中显示用于显示消息的MessageBox。

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

有自定义的MessageBox(从Form继承)或另一个报告程序Forms,但是有趣的是不必使用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

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

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

异常报告器
/programming/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


1
看看这个(Windows手机,但应该是相同的):stackoverflow.com/questions/9674122/...
江淮汽车

6
@istepaniuk如果他不知道,他不能尝试。所以不要
再问

1
你应该能够创建一个定时器,并将其设置在设定的时间量后关闭
史蒂芬阿克莱

2
您可以将表单创建为MessageBox
spajce 2013年

2
@MustafaEkici,我邀请OP展示他尝试了什么。我认为他在实际提出要求之前一定已经尝试过并失败了。这就是为什么Ramhound和我否决了这个问题。您可以阅读meta.stackexchange.com/questions/122986/...
istepaniuk

Answers:


120

请尝试以下方法:

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包获得

Install-Package AutoClosingMessageBox

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


最好使用System.Threading.Timer或System.Timers.Timer(如@Jens答案)?SendMessage与PostMessage?
Kiquenet

@Kiquenet我相信在这种特定情况下没有显着差异。
DmitryG

1
@GeorgeBirbilis谢谢,这很有道理...在这种情况下,您可以将#32770值用作类名
DmitryG

2
如果按钮不起作用,这对我不起作用System.Windows.Forms.MessageBoxButtons.YesNo
Jack Jack

1
@Jack对不起,我延迟回复,我已经用YesNo按钮检查了案件-绝对正确-无法正常工作。我将在上下文中提供单独的AutoclosingMessageBox库的修复程序。包含重新设计的方法,我认为可能会有用。谢谢!
DmitryG

29

在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线程或用户创建的线程上执行的,那么using可以确保使用。如果代码是在线程池(例如,计时器回调)或任务池(例如,使用默认参数创建TaskFactory.StartNewTask.Run具有默认参数创建的任务)上的线程上执行的,则该示例将无法正常工作。


.NET是什么版本?什么是TaskScheduler?
Kiquenet

1
@Kiquenet .NET 4.0及更高版本。Task并且TaskScheduler来自System.Threading.Tasksmscorlib.dll中的命名空间,因此不需要其他程序集引用。
BSharp 2014年

很好的解决方案!另外一个...在Winforms应用程序中效果很好,在创建新窗体后立即为新窗体添加BringToFront。否则,对话框有时会显示当前活动表单的后面,即,对用户而言不会出现。var w = new Form(){Size = new Size(0,0)}; w.BringToFront();
Developer63

@ Developer63我无法重现您的经验。即使在w.SentToBack()之前调用时MessageBox.Show(),对话框仍显示在主窗体的顶部。在.NET 4.5和4.7.1上测试。
BSharp

此解决方案可能不错,但仅可用于.NET 4.5及更高版本,而不能用于4.0(由于Task.Delay),请参阅:stackoverflow.com/questions/17717047/…–
KwentRell

16

AppActivate!

如果您不介意将参考文献弄糊涂,则可以包括Microsoft.Visualbasic,并使用这种非常简短的方法。

显示消息框

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

现在去洗手!


11

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

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

到现在为止还挺好。但是,如果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;
}

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

窗口类型适用于Windows窗体吗?
Kiquenet

为什么需要向隐藏的父窗口发送消息以将其关闭?您不能仅在其上调用某些“关闭”方法或以其他方式处置它吗?
乔治·比比里斯

为了回答我自己的问题,该WPF窗口的OwnedWindows属性似乎显示0个窗口,并且“关闭”不会关闭消息
框子

2
出色的解决方案。其中有一些命名重叠System.WindowsSystem.Windows.Forms这花了我一些时间来弄清楚。你将需要:SystemSystem.Runtime.InteropServicesSystem.Threading.TasksSystem.WindowsSystem.Windows.InteropSystem.Windows.Media
m3tikn0b

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

2
最好使用System.Threading.Timer或System.Timers.Timer(例如@DmitryG答案)?SendMessage与PostMessage?
Kiquenet

1
参见stackoverflow.com/questions/3376619/…,也许还有stackoverflow.com/questions/2411116/…关于SendMessage和PostMessage之间的区别
George Birbilis

7

罗杰·B对此答案提供了最的解决方案之一,他在04年就做到了,而且仍然很成功。

基本上,您到这里去他的项目并下载CS文件。万一链接消失了,我有一个备份要点这里。将CS文件添加到您的项目中,或者将代码复制/粘贴到某个位置(如果您愿意)。

然后,您要做的就是切换

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

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

而且你很好。


2
我需要快速解决方案,而且效果很好!感谢分享。
Mark Kram

1
您在示例中错过了.Show扩展名...应该读为:DialogResult结果= MessageBoxEx.Show(“ Text”,“ Title”,MessageBoxButtons.CHOICE,timer_ms)
Edd

2

有一个可在此处提供此功能的codeproject项目。

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

编辑:

我有一个想法,嗯。

使用计时器并在出现消息框时开始。如果您的MessageBox仅侦听“确定”按钮(只有一种可能性),请使用OnTick-Event模拟ESC-Press,SendKeys.Send("{ESC}");然后停止计时器。


1
计时器概念是一种简单的方法...但是,如果没有或失去焦点,则必须确保发送的键会击中您的应用程序。这将需要SetForegroundWindow,答案开始包含更多代码,但请参阅下面的“ AppActivate”。
FastAl

2

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

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


0

使用EndDialog而不是发送WM_CLOSE

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

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);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.