博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

扩展WINFORM的MessageBox功能,自定义按钮文字

Posted on 2009-04-17 11:52  Hobo Zhuang  阅读(11093)  评论(2编辑  收藏  举报

最近在公司一项目中,客户要求在截图功能操作中加入一个用户确认的动作,是“发送截图”,还是“保存文件”,还是“取消”。这个需求首先想到的就是MessageBox.Show(),可是Show出来的对话框上的文字是固定的,不能修改成自己需要的文字。

怎么办呢?
自己做一个FORM来Show?办法太笨且重用性不高。
直接Show个MessageBox,加些提示内容,告诉用户哪个按钮代表什么意思?太不以人为本了!!!

看来还是得动用WIN32 API这把宝刀。用SPY++研究了一下Show出来的MessageBox,看来不复杂,把窗口中子控件的类名为“Button”的文字给替换为想要的内容就OK了。

说干就干,卷起裤管,拿上锄头,三下五除二封装了一个“MessageBoxEx”的类,在MessageBox.Show的参数基础上加了一个“buttonTitles”的字符串数组参数,是想修改的内容数组。具体实现就不描述了,见所附代码。

其中获取对话框窗体句柄的方法是参考了一个关于扩展OpenFileDialog窗体的代码,具体出处不记得了。其实有了这个基础,可以在Show出来的窗体上做很多事情,如加一个图片、按钮或是*****,这就要看具体的需求了。抛砖引玉,希望能引出点啥。

代码:

    class MessageBoxEx
    {
        //测试样例
        static void test()
        {
            Show("提示消息", "提示标题", MessageBoxButtons.YesNoCancel, new string[] { "按钮一(&O)", "按钮二(&T)", "按钮三(&H)" });
        }

        public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, string[] buttonTitles)
        {
            DummyForm frm = new DummyForm(buttons, buttonTitles);
            frm.Show();
            frm.WatchForActivate = true;
            DialogResult result = MessageBox.Show(frm, text, caption, buttons);
            frm.Close();
            return result;
        }

        class DummyForm : Form
        {
            IntPtr _handle;
            MessageBoxButtons _buttons;
            string[] _buttonTitles = null;

            bool _watchForActivate = false;

            public bool WatchForActivate
            {
                get { return _watchForActivate; }
                set { _watchForActivate = value; }
            }

            public DummyForm(MessageBoxButtons buttons, string[] buttonTitles)
            {
                _buttons = buttons;
                _buttonTitles = buttonTitles;

                //让自己在界面上看不到
                this.Text = "";
                this.StartPosition = FormStartPosition.Manual;
                this.Location = new Point(-32000, -32000);
                this.ShowInTaskbar = false;
            }

            protected override void OnShown(EventArgs e)
            {
                base.OnShown(e);
                //把自己藏起来,在任务列表里也看不到
                NativeWin32API.SetWindowPos(this.Handle, IntPtr.Zero, 0, 0, 0, 0, 659);
            }

            protected override void WndProc(ref System.Windows.Forms.Message m)
            {
                if (_watchForActivate && m.Msg == 0x0006)
                {
                    _watchForActivate = false;
                    _handle = m.LParam;
                    CheckMsgbox();
                }
                base.WndProc(ref m);
            }

            private void CheckMsgbox()
            {
                if (_buttonTitles == null || _buttonTitles.Length == 0)
                    return;

                //按钮标题的索引
                int buttonTitleIndex = 0;
                //获取子控件的句柄
                IntPtr h = NativeWin32API.GetWindow(_handle, OCCommon.Message.GW_CHILD);
                while (h != IntPtr.Zero)
                {
                    //按顺序把按钮标题赋上
                    if (NativeWin32API.GetWindowClassName(h).Equals("Button"))
                    {
                        if (_buttonTitles.Length > buttonTitleIndex)
                        {
                            NativeWin32API.SetWindowText(h, _buttonTitles[buttonTitleIndex]);
                            buttonTitleIndex++;
                        }
                    }
                    h = NativeWin32API.GetWindow(h, OCCommon.Message.GW_HWNDNEXT);
                }
            }
        }
    }

用到的消息:

        public const int GW_CHILD = 5;
        public const int GW_HWNDNEXT = 2;

用到的WIN32 API:

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int Width, int Height, int flags);
        [DllImport("user32.dll")]
        public static extern IntPtr GetWindow(IntPtr hWnd, long wCmd);
        [DllImport("user32.dll")]
        public static extern bool SetWindowText(IntPtr hWnd, string lpString);
        [DllImport("user32.dll")]
        public static extern int GetClassNameW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)]StringBuilder lpString, int nMaxCount);

        public static string GetWindowClassName(IntPtr handle)
        {
            StringBuilder sb = new StringBuilder(256);

            GetClassNameW(handle, sb, sb.Capacity); //得到窗口类名并保存在strClass中
            return sb.ToString();
        }