强制以编程方式关闭MessageBox

时间:2011-10-28 07:01:48

标签: c# .net winforms messagebox

让我给你背景资料。

我们有一个在各个地方(数百个)使用MessageBox.Show(....)的应用程序(中等大小)。

这些消息框是工作流程的一部分,用于通知,警告或从用户那里获取输入。如果没有活动,应用程序应该在一定时间后自动注销。我们要求在注销应用程序时,只是为了清理会话数据,清除视图并隐藏自身,以便在下次启动时,它不必执行启动过程,这在时间上是昂贵的。

一切正常,但是在屏幕上有一些消息框并且用户离开机器而没有响应消息框然后由于没有活动使应用程序退出的情况下。问题是消息框不会消失。

如何隐藏应用程序时关闭打开的消息框(如果有的话)?

11 个答案:

答案 0 :(得分:9)

这是一段基于UIAutomation(一个很酷但仍未使用的API)的代码,它试图关闭当前进程的所有模态窗口(包括用MessageBox打开的窗口):

    /// <summary>
    /// Attempt to close modal windows if there are any.
    /// </summary>
    public static void CloseModalWindows()
    {
        // get the main window
        AutomationElement root = AutomationElement.FromHandle(Process.GetCurrentProcess().MainWindowHandle);
        if (root == null)
            return;

        // it should implement the Window pattern
        object pattern;
        if (!root.TryGetCurrentPattern(WindowPattern.Pattern, out pattern))
            return;

        WindowPattern window = (WindowPattern)pattern;
        if (window.Current.WindowInteractionState != WindowInteractionState.ReadyForUserInteraction)
        {
            // get sub windows
            foreach (AutomationElement element in root.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window)))
            {
                // hmmm... is it really a window?
                if (element.TryGetCurrentPattern(WindowPattern.Pattern, out pattern))
                {
                    // if it's ready, try to close it
                    WindowPattern childWindow = (WindowPattern)pattern;
                    if (childWindow.Current.WindowInteractionState == WindowInteractionState.ReadyForUserInteraction)
                    {
                        childWindow.Close();
                    }
                }
            }
        }
    }

例如,如果你有一个WinForms应用程序,当你按下某个按钮1时弹出一个MessageBox,你仍然可以使用Windows“关闭窗口”菜单关闭应用程序(右键单击任务栏):

    private void button1_Click(object sender, EventArgs e)
    {
        MessageBox.Show("Don't click me. I want to be closed automatically!");
    }

    protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        const int WM_SYSCOMMAND = 0x0112;
        const int SC_CLOSE = 0xF060;

        if (m.Msg == WM_SYSCOMMAND) // this is sent even if a modal MessageBox is shown
        {
            if ((int)m.WParam == SC_CLOSE)
            {
                CloseModalWindows();
                Close();
            }
        }
        base.WndProc(ref m);
    }

您可以在代码中的其他位置使用CloseModalWindows,这只是一个示例。

答案 1 :(得分:7)

MSDN论坛上的这个link显示了如何使用FindWindow关闭消息框并发送WM_CLOSE消息。虽然问题是.NET / WindowsCE,但它可能会解决您的问题,值得一看

答案 2 :(得分:3)

首先提出一个问题:如果消息框用作工作流的一部分,不会以编程方式关闭消息框导致流程改变/继续吗?

我认为你有三个选择

  1. 创建自己的messagebox类版本,打开一个对话框窗口,该窗口看起来像一个具有附加功能的消息框,因此在一段时间后会自动关闭。

  2. 在c#中实现类似的功能,以编程方式关闭消息框。 http://www.codeproject.com/KB/dialog/AutoCloseMessageBox.aspx

  3. 从中断工作流程中删除消息框。这可能是最好的解决方案,因为它以编程方式关闭消息框的声音将导致工作流程继续/更改,甚至可能导致另一个消息框显示哪些可能不合适。但显然修复根本问题可能是最好的,但并不总是最容易的。

  4. 1和2需要从一个单独的线程完成,所以你需要考虑它的含义,因为显示消息框将被阻止。

答案 3 :(得分:2)

继承我的SendKeys示例 - 测试和工作:

假设我们在表单中有背景工作和按钮。单击按钮后 - 启动工作人员并显示消息框。在工作者DoWork事件中睡5秒然后发送回车键 - 消息框关闭。

private void button1_Click(object sender, EventArgs e)
{
    backgroundWorker1.RunWorkerAsync();
    MessageBox.Show("Close this message!");
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    Thread.Sleep(5000);
    SendKeys.SendWait("{Enter}");//or Esc
}

答案 4 :(得分:1)

假设您可以编辑正在调用的代码 MessageBox.Show()方法,我建议不要使用 消息框。相反,只需使用您自己的自定义表单,调用ShowDialog() 在它上面做与MessageBox类基本相同的事情。那么你 拥有表单本身的实例,您可以在其上调用Close() 实例关闭它。

一个很好的例子是here

答案 5 :(得分:1)

我认为最干净的方法是实现你自己的消息框形式,如

class MyMessageBox : Form {
  private MyMessageBox currentForm; // The currently active message box

  public static Show(....) { // same as MessageBox.Show
    // ...
  }

  public static Show(...) { // define additional overloads
  }

  public static CloseCurrent() {
    if (currentForm != null)
      currentForm.Close();
  }

  // ...
}

在我的一些大型项目中,我发现这种方法也可用于其他目的(例如自动记录错误消息等)。

我的第二个想法是使用GetTopWindow()(或者可能是其他一些WIN32函数)来获取应用程序的当前顶级窗口并向其发送WM_CLOSE消息。

答案 6 :(得分:1)

我使用.net 2和两种方法使用相同的技巧。

使用MessageBox.Show(this,"message")

从存根表单中打开MessageBox

当表单不可见或者没有真正的UI时。

  1. 保留表单处理程序并使用以下命令关闭它:

    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
    

  2. 将表单保存为类参数并使用FormX.Close()

  3. 由于Form是MessageBox的所有者,因此关闭它将关闭MessageBox。

答案 7 :(得分:1)

请参阅DmitryG post in "Close a MessageBox after several seconds"

超时到达后自动关闭MessageBox

using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

    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);
            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(null, _caption);
            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);
    }

并通过

进行调用
AutoClosingMessageBox.Show("Content", "Title", TimeOut);

答案 8 :(得分:1)

该主题已在其他SO问题中进行了广泛介绍,但是由于该特定主题提供了有关使用UI自动化/窗口查找技术(我不太喜欢)的几个答案以及有关在不提供代码的情况下创建自己的对话框的一般建议,决定发布我自己的解决方案。可以像下面这样创建可实例化的MessageBox类似类:

using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Text.RegularExpressions;

namespace Common
{
    // Loosely based on: https://www.codeproject.com/Articles/17253/A-Custom-Message-Box
    class MsgBox : Form
    {
        private Panel _plHeader = new Panel();
        private Panel _plFooter = new Panel();
        private Panel _plIcon = new Panel();
        private PictureBox _picIcon = new PictureBox();
        private FlowLayoutPanel _flpButtons = new FlowLayoutPanel();
        private Label _lblMessage;

        private MsgBox()
        {
            FormBorderStyle = FormBorderStyle.FixedDialog;
            BackColor = Color.White;
            StartPosition = FormStartPosition.CenterScreen;
            MinimizeBox = false;
            MaximizeBox = false;
            ShowIcon = false;
            Width = 400;

            _lblMessage = new Label();
            _lblMessage.Font = new Font("Segoe UI", 10);
            _lblMessage.Dock = DockStyle.Fill;
            _lblMessage.TextAlign = ContentAlignment.MiddleLeft;

            _flpButtons.FlowDirection = FlowDirection.RightToLeft;
            _flpButtons.Dock = DockStyle.Fill;

            //_plHeader.FlowDirection = FlowDirection.TopDown;
            _plHeader.Dock = DockStyle.Fill;
            _plHeader.Padding = new Padding(20);
            _plHeader.Controls.Add(_lblMessage);

            _plFooter.Dock = DockStyle.Bottom;
            _plFooter.BackColor = Color.FromArgb(240, 240, 240);
            _plFooter.Padding = new Padding(10);
            _plFooter.Height = 60;
            _plFooter.Controls.Add(_flpButtons);

            _picIcon.Location = new Point(30, 50);

            _plIcon.Dock = DockStyle.Left;
            _plIcon.Padding = new Padding(20);
            _plIcon.Width = 70;
            _plIcon.Controls.Add(_picIcon);

            Controls.Add(_plHeader);
            Controls.Add(_plIcon);
            Controls.Add(_plFooter);
        }

        public static DialogResult Show(IWin32Window owner, string message, string title = null, MessageBoxButtons? buttons = MessageBoxButtons.OK, MessageBoxIcon icon = MessageBoxIcon.Information)
        {
            var msgBox = Create(message, title, buttons, icon);
            return msgBox.ShowDialog(owner);
        }

        public static DialogResult Show(string message, string title = null, MessageBoxButtons? buttons = MessageBoxButtons.OK, MessageBoxIcon icon = MessageBoxIcon.Information)
        {
            var msgBox = Create(message, title, buttons, icon);
            return msgBox.ShowDialog();
        }

        public static MsgBox Create(string message, string title = null, MessageBoxButtons? buttons = MessageBoxButtons.OK, MessageBoxIcon icon = MessageBoxIcon.Information)
        {
            var msgBox = new MsgBox();
            msgBox.Init(message, title, buttons, icon);
            return msgBox;
        }

        void Init(string message, string title, MessageBoxButtons? buttons, MessageBoxIcon icon)
        {
            _lblMessage.Text = message;
            Text = title;
            InitButtons(buttons);
            InitIcon(icon);
            Size = MessageSize(message);
        }

        void InitButtons(MessageBoxButtons? buttons)
        {
            if (!buttons.HasValue)
                return;

            switch (buttons)
            {
                case MessageBoxButtons.AbortRetryIgnore:
                    AddButton("Ignore");
                    AddButton("Retry");
                    AddButton("Abort");
                    break;

                case MessageBoxButtons.OK:
                    AddButton("OK");
                    break;

                case MessageBoxButtons.OKCancel:
                    AddButton("Cancel");
                    AddButton("OK");
                    break;

                case MessageBoxButtons.RetryCancel:
                    AddButton("Cancel");
                    AddButton("Retry");
                    break;

                case MessageBoxButtons.YesNo:
                    AddButton("No");
                    AddButton("Yes");
                    break;

                case MessageBoxButtons.YesNoCancel:
                    AddButton("Cancel");
                    AddButton("No");
                    AddButton("Yes");
                    break;
            }
        }

        void InitIcon(MessageBoxIcon icon)
        {
            switch (icon)
            {
                case MessageBoxIcon.None:
                    _picIcon.Hide();
                    break;
                case MessageBoxIcon.Exclamation:
                    _picIcon.Image = SystemIcons.Exclamation.ToBitmap();
                    break;

                case MessageBoxIcon.Error:
                    _picIcon.Image = SystemIcons.Error.ToBitmap();
                    break;

                case MessageBoxIcon.Information:
                    _picIcon.Image = SystemIcons.Information.ToBitmap();
                    break;

                case MessageBoxIcon.Question:
                    _picIcon.Image = SystemIcons.Question.ToBitmap();
                    break;
            }

            _picIcon.Width = _picIcon.Image.Width;
            _picIcon.Height = _picIcon.Image.Height;
        }

        private void ButtonClick(object sender, EventArgs e)
        {
            Button btn = (Button)sender;

            switch (btn.Text)
            {
                case "Abort":
                    DialogResult = DialogResult.Abort;
                    break;

                case "Retry":
                    DialogResult = DialogResult.Retry;
                    break;

                case "Ignore":
                    DialogResult = DialogResult.Ignore;
                    break;

                case "OK":
                    DialogResult = DialogResult.OK;
                    break;

                case "Cancel":
                    DialogResult = DialogResult.Cancel;
                    break;

                case "Yes":
                    DialogResult = DialogResult.Yes;
                    break;

                case "No":
                    DialogResult = DialogResult.No;
                    break;
            }

            Close();
        }

        private static Size MessageSize(string message)
        {
            int width=350;
            int height = 230;

            SizeF size = TextRenderer.MeasureText(message, new Font("Segoe UI", 10));

            if (message.Length < 150)
            {
                if ((int)size.Width > 350)
                {
                    width = (int)size.Width;
                }
            }
            else
            {
                string[] groups = (from Match m in Regex.Matches(message, ".{1,180}") select m.Value).ToArray();
                int lines = groups.Length+1;
                width = 700;
                height += (int)(size.Height+10) * lines;
            }
            return new Size(width, height);
        }

        private void AddButton(string caption)
        {
            var btn = new Button();
            btn.Text = caption;
            btn.Font = new Font("Segoe UI", 8);
            btn.BackColor = Color.FromArgb(225, 225, 225);
            btn.Padding = new Padding(3);
            btn.Height = 30;
            btn.Click += ButtonClick;
            _flpButtons.Controls.Add(btn);
        }
    }
}

然后可以只将对话框的引用保留在类范围内,显示对话框并获取结果,或者仅在应用程序退出事件处理程序中将其关闭。

MsgBox _msgBox;

void eventHandler1(object sender, EventArgs e)
{
    _msgBox = MsgBox.Create("Do you want to continue", "Inquiry", MessageBoxButtons.YesNo);
    var result = _msgBox.ShowDialog();
    // do something with result
}

void applicationExitHandler(object sender, EventArgs e)
{
    if (_msgBox != null)
        _msgBox.Close();
}

答案 9 :(得分:0)

为此创建自己的控件并实现您喜欢的行为。作为选项,可能有一个计时器来关闭此MessageBox。

答案 10 :(得分:0)

最简单的解决方案是创建一个将在timer_tick上关闭的表单

private int interval = 0;
private string message = "";

public msgBox(string msg = "", int i = 0)
{
    InitializeComponent();
    interval = i;
    message = msg;
}

private void MsgBox_Load(object sender, EventArgs e)
{
    if (interval > 0)
        timer1.Interval = interval;

    lblMessage.Text = message;
    lblMessage.Width = panel1.Width - 20;
    lblMessage.Left = 10;
}

private void Timer1_Tick(object sender, EventArgs e)
{
    this.Close();
}

private void Panel1_Paint(object sender, PaintEventArgs e)
{
    ControlPaint.DrawBorder(e.Graphics, this.panel1.ClientRectangle, Color.DarkBlue, ButtonBorderStyle.Solid);
}

主要形式使用的方法

   private void showMessage(string msg, int interval = 0)
    {
        msgBox mb = new msgBox(msg, interval);
        mb.ShowDialog(this);
    }

称呼

  showMessage("File saved");
相关问题