1

我需要在 C# Framework 3.5 中创建一个自定义 MessageBox,它显示一些 MessageBoxButtons,并返回 DialogResult 值。如果没有用户反应,在一定的超时时间后,MessageBox 应该关闭,返回 null。

我在这里按照 DmitryG 的回答进行了一些细微的更改:

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

要创建 MessageBox,我们只需要调用 Show 函数

AutoClosingMessageBox.Show("Show me sth", "capt", 3000, MessageBoxButtons.AbortRetryIgnore);

当用户单击 MessageBox 中的按钮时,此方法确实返回 dialogResult_ 值,但 WM_Close 消息在超时时间后不再关闭 MessageBox。

这是因为 MessageBox 仍在等待对话框结果吗?如果是,我该如何避免?我想避免必须在新线程中启动消息框并不得不终止线程。

4

1 回答 1

1

我同意其他评论,您应该制作自己的消息框表单。

也就是说,如果您仍想使用其他方法,您应该能够通过向已识别的对话框发送适当的消息来使其工作;例如,Alt-I 表示“忽略”。

这是您发布的代码版本:

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

}

该类SendKeys仅适用于当前活动窗口,因此我包含了一个调用SetForegroundWindow()以确保键到达正确的窗口。

当然,上面的硬编码是Alt-I必需的。如果您想要一个更通用的解决方案,您可能会包含一个字典,该字典将MessageBoxButtons值映射到关闭该对话框所需的适当SendKeys字符串和/或让调用者提供该信息(强制他们提供实际SendKeys字符串或(更好)拥有它们传递一个枚举值,指示他们想要使用哪个按钮来关闭对话框,然后让您的实现将其映射到适当的字符串。

于 2015-12-09T06:40:34.500 回答