自定义消息框显示时间在用完 WinForms 之前到期

Custom message box show time expiring before it runs out WinForms

我有这个自定义消息框 class :

public class AutoCloseMsb
{
    readonly System.Threading.Timer _timeoutTimer;
    readonly string _caption;

    private AutoCloseMsb(string text, string caption, int timeout)
    {
        _caption = caption;
        _timeoutTimer = new System.Threading.Timer(OnTimerElapsed,
            null, timeout, System.Threading.Timeout.Infinite);
        MessageBox.Show(text, caption);
    }
    public static void Show(string text, string caption, int timeout)
    {
        new AutoCloseMsb(text, caption, timeout);
    }

    private void OnTimerElapsed(object state)
    {
        IntPtr mbWnd = FindWindow("#32770", _caption);
        if (mbWnd != IntPtr.Zero)
            SendMessage(mbWnd, WmClose, IntPtr.Zero, IntPtr.Zero);
        _timeoutTimer.Dispose();
    }

    private const int WmClose = 0x0010;
    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
    private static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
}

我在几个地方一个接一个地调用它:

AutoCloseMsb.Show("Bot 1 Turn", "Turns", ThinkTime);
AutoCloseMsb.Show("Bot 2 Turn", "Turns", ThinkTime);
AutoCloseMsb.Show("Bot 3 Turn", "Turns", ThinkTime);
AutoCloseMsb.Show("Bot 4 Turn", "Turns", ThinkTime);
AutoCloseMsb.Show("Bot 5 Turn", "Turns", ThinkTime);

变量 ThinkTime 正在从我实际更改它的资源中获取价值。但是,如果我将 3000 毫秒作为显示时间,它将显示第一个 3 秒,而其他的则不会在此期间显示,它们将在大约 100-200 毫秒内关闭(它们只是显示并立即关闭)为什么这是发生了吗?我应该在显示每个消息框后重置变量的值吗?

如果您单击 MessageBox 中的“确定”按钮,您并没有摆脱 Timer,因此它仍会在消息框关闭后触发。由于标题的 non-uniqueness,OnTimerElapsed 中的事件处理程序将找到一个消息框,然后将其关闭。这会导致随后关闭消息框,因为总有一个 Timer 仍然需要触发。

您的错误的快速修复是移动代码以直接在 MessageBox 而不是 OnTimerElapsed 事件之后删除计时器事件:

private AutoCloseMsb(string text, string caption, int timeout)
{
    _caption = caption;
    _timeoutTimer = new System.Threading.Timer(OnTimerElapsed,
        null, timeout, System.Threading.Timeout.Infinite);
    // the next call blocks, until either the user
    // or the timer closes the the messagbox
    MessageBox.Show(text, caption);
    // now we can stop this timer
    _timeoutTimer.Change(Timeout.Infinite, Timeout.Infinite);
    // and dispose it
    _timeoutTimer.Dispose();
}

TimerElapsedEvent 仅发出清理可以开始的信号

private void OnTimerElapsed(object state)
{
    Debug.WriteLine("on timer");
    IntPtr mbWnd = FindWindow("#32770", _caption);
    if (mbWnd != IntPtr.Zero)
        SendMessage(mbWnd, WmClose, IntPtr.Zero, IntPtr.Zero);
    // don't touch the state here, only signal to continue
}

这些更改实现了您想要的并保持代码流清晰。