I need to create a custom MessageBox in C# Framework 3.5 which displays some MesageBoxButtons, and returns the DialogResult value. If there is no user reaction, after a certain timeout time, the MessageBox should close, returning null.
I followed DmitryG's answer here, with minor changes:
static DialogResult? dialogResult_ = null;
public AutoClosingMessageBox(string text, string caption, int timeout, MessageBoxButtons msbb)
{
_caption = caption;
_timeoutTimer = new System.Threading.Timer(OnTimerElapsed,
null, timeout, System.Threading.Timeout.Infinite);
dialogResult_ = MessageBox.Show(text, caption, msbb);
}
public static DialogResult? Show(string text, string caption, int timeout, MessageBoxButtons efb)
{
new AutoClosingMessageBox(text, caption, timeout, efb);
return dialogResult_;
}
void OnTimerElapsed(object state)
{
IntPtr mbWnd = FindWindow("#32770", _caption);
if (mbWnd != IntPtr.Zero)
{
SendMessage(mbWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
_timeoutTimer.Dispose();
}
dialogResult_ = null;
}
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);
To create the MessageBox we only have to call the Show function
AutoClosingMessageBox.Show("Show me sth", "capt", 3000, MessageBoxButtons.AbortRetryIgnore);
This approach does return the dialogResult_ value when the user clicks on a button within the MessageBox, but the WM_Close message doesn't close the MessageBox anymore after the timeout time.
Is this because the MessageBox is still waiting for the Dialog Result? If yes, how can I avoid it? I would like to avoid having to start the Message Box in a new thread and having to kill the thread.
I agree with the other comments, that you should just make your own message box form.
That said, if you still want to use that other approach, you should be able to get it to work by sending an appropriate message to the dialog that is recognized; e.g. Alt-I for "ignore".
Here's a version of the code you posted that does that:
class AutoClosingMessageBox
{
System.Threading.Timer _timeoutTimer;
string _caption;
static DialogResult? dialogResult_ = null;
private AutoClosingMessageBox(string text, string caption, int timeout, MessageBoxButtons msbb)
{
_caption = caption;
_timeoutTimer = new System.Threading.Timer(OnTimerElapsed,
null, timeout, System.Threading.Timeout.Infinite);
dialogResult_ = MessageBox.Show(text, caption, msbb);
}
public static DialogResult? Show(string text, string caption, int timeout, MessageBoxButtons efb)
{
new AutoClosingMessageBox(text, caption, timeout, efb);
return dialogResult_;
}
void OnTimerElapsed(object state)
{
IntPtr mbWnd = FindWindow("#32770", _caption);
if (mbWnd != IntPtr.Zero)
{
SetForegroundWindow(mbWnd);
SendKeys.SendWait("%I");
_timeoutTimer.Dispose();
}
dialogResult_ = null;
}
[DllImport("user32.dll", SetLastError = true)]
extern static IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
extern static IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
extern static bool SetForegroundWindow(IntPtr hwnd);
}
The SendKeys
class only works on the current active window, so I included a call to SetForegroundWindow()
to ensure the keys get to the correct window.
Of course, the above hard-codes the Alt-I required. If you wanted a more general solution, you might include a dictionary that maps the MessageBoxButtons
value to the appropriate SendKeys
string needed to dismiss that dialog and/or have the caller provide that information (either force them to provide the actual SendKeys
string or (nicer) have them pass an enum value indicating which button they want to use to dismiss the dialog and then have your implementation map that to the appropriate string.