我有一个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
可滚动的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
答案 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)
如果您不介意稍微弄清楚您的参考文献,可以加入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)
基本上,你是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);
更聪明地工作,而不是更努力。