65.9K
CodeProject 正在变化。 阅读更多。
Home

针对 .NET CF 中 WebBrowser 控件的鼠标事件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.56/5 (7投票s)

2008年7月13日

CPOL

3分钟阅读

viewsIcon

55632

downloadIcon

601

扩展 .NET CF 2.0 的 WebBrowser 以处理鼠标事件。

引言

几乎在我的一个 .NET Compact Framework 2.0 项目中,我希望使用 WebBrowser 控件来显示富媒体内容。 不幸的是,.NET CF 中的 WebBrowser 控件不像 .NET Framework 的控件那么强大。 它没有一些有用的接口,尤其是它不支持 DOM 模型... 我对该控件的要求是

  • 底部没有进度条。
  • 不使用默认的上下文菜单(禁用它,或者更好的是,我可以自定义上下文菜单)。
  • 有一种方法可以将 HTML 文本附加到其中(DocumentText 或类似的东西)。
  • 可以处理鼠标事件(MouseUpMouseDownMouseMove)。

OpenNETCF 的 SDF WebBrowser 控件确实满足了前三个要求,但它仍然无法处理鼠标事件。 所以我决定扩展标准的 WebBrowser 控件,以便它可以处理鼠标事件。

背景

在 .NET Framework 中,使用 Document 属性,我们可以轻松处理 WebBrowser 控件的鼠标事件; 但 .NET CF 不行。 因为当用户点击 WebBrowser 控件时,他实际上点击了该控件托管的 Document,所以 WebBrowser 不知道此事件,当然它也不会引发相关事件。

解决问题的思路:派生 WebBrowser 控件,并钩住该控件内部发生的所有鼠标事件。 感谢 OpenNETCF 的 ApplicationEx 类,我们可以在托管代码中实现这一点。 具体来说,我们编写一个名为 WebBrowserEx 的类,它派生自 .NET CF 的 WebBrowser 控件,并实现 IMessageFilter 接口。 此接口将帮助我们钩住并捕获 WM_LBUTTONUPWM_LBUTTONDOWNWM_MOUSEMOVE 消息。 (如果您不熟悉 IMessageFiter,您应该去 OpenNETCF 的网站看看)。

但是,当您单击 WebBrowser 时,哪个子窗口接收这些消息? 没问题! 使用 Remote Spy 实用程序(在 Visual Studio Remote Tools 中),我们可以看到 WebBrowser 控件的孙窗口。 此窗口属于 PIEHTML 类,并接收所有鼠标消息。

browsermouseevents/Remotespy.jpg

实现

WebBrowserEx 类中大约只有 100 行代码,所以我在下面全部粘贴出来。

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using OpenNETCF.Windows.Forms;
using OpenNETCF.Win32;
using Microsoft.WindowsCE.Forms;

namespace webBrowserEx
{
    /// <summary>
    /// An extended WebBrowser, can handle Mouse Events.
    /// </summary>
    public class WebBrowserEx : System.Windows.Forms.WebBrowser, IMessageFilter
    {

        public WebBrowserEx()
        {
            //Initialize Message Filter, using OpenNETCF's ApplicationEx
            OpenNETCF.Windows.Forms.ApplicationEx.AddMessageFilter(this);
        }

        #region ------ IMessageFilter implementation ------

        public bool PreFilterMessage(ref Message m)
        {
            if ((m.Msg == (int)WM.LBUTTONDOWN || m.Msg == (int)WM.LBUTTONUP || 
			m.Msg == (int)WM.MOUSEMOVE)
                 && IsChildestWindow(this.Handle, m.HWnd))
            {
                int xPos = (int)m.LParam & 0xFFFF;
                int yPos = ((int)m.LParam >> 16) & 0xFFFF;

                MouseEventArgs e = new MouseEventArgs(MouseButtons.Left, 1,
                                        xPos, yPos, 0);

                switch (m.Msg)
                {
                    case (int)WM.LBUTTONUP:
                        base.OnMouseUp(e);
                        break;
                    case (int)WM.LBUTTONDOWN:
                        base.OnMouseDown(e);
                        break;
                    case (int)WM.MOUSEMOVE:
                        base.OnMouseMove(e);
                        break;
                }
            }

            return false;
        }

        #endregion

        #region ------- Private functions -----------------

        /// <summary>
        /// Check whether <see cref="hCheck"/> 
        /// is one of <see cref="hWnd"/>'s grandchildren.
        /// </summary>
        /// <param name="hWnd"></param>
        /// <param name="hCheck"></param>
        /// <returns></returns>
        private static bool IsChildestWindow(IntPtr hWnd, IntPtr hCheck)
        {
            IntPtr ret = hWnd;

            //Find the first "smallest" child
            while ((hWnd = GetWindow(hWnd, (int)GetWindowFlags.GW_CHILD)) != IntPtr.Zero)
            {
                ret = hWnd;
            }
            
            //goes through all of "smallest" grandchildren
            hWnd = ret;
            while ((ret != hCheck) &&
                ((hWnd = GetWindow(ret, (int)GetWindowFlags.GW_HWNDNEXT)) != IntPtr.Zero))
            {
                ret = hWnd;
            }

            return (hWnd != IntPtr.Zero);
        }

        #endregion

        #region -------- P/Invoke declarations ------------

        /// <summary>
        /// Get relative window with a given window.
        /// </summary>
        /// <param name="hwnd">the Given window</param>
        /// <param name="cmd">an <see cref="GetWindowFlags"/> value, 
        /// indicates the relation.</param>
        /// <returns></returns>
        [DllImport("coredll.dll")]
        private static extern IntPtr GetWindow(IntPtr hwnd, int cmd);

        private enum GetWindowFlags : int
        {
            GW_HWNDFIRST = 0,
            GW_HWNDLAST = 1,
            GW_HWNDNEXT = 2,
            GW_HWNDPREV = 3,
            GW_OWNER = 4,
            GW_CHILD = 5,
            GW_MAX = 5
        }

        #endregion
    }
}

实现非常简单。 每当收到 WM_MOUSEMOVEWM_LBUTTONUPWM_LBUTTONDOWN 消息时,我们将检查 m.HWnd 是否是 WebBrowser 的孙子窗口(即 PIEHTML 窗口)并引发相关事件。

我们要做的最后一件事是使用 OpenNETCF.Windows.Forms.ApplicationEx 类来运行应用程序,而不是 System.Windows.Forms.Application. 这将有助于在 PreFilterMessage 函数中接收和处理消息。 我们将此代码放在 Program.cs

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [MTAThread]
    static void Main()
    {            
        // Instead of using Application class,
        // we use OpenNETCF's ApplicationEx which enables MessageFilter.
        OpenNETCF.Windows.Forms.ApplicationEx.Run(new Form1());
        //Application.Run(new Form1());
    }
} 

关注点

  • 我在 VS 2008 中构建了示例代码,使用 .NET Compact Framework 2.0 上的智能设备项目。 但在 .NET CF 3.5 中应该没问题。
  • 您可以使用 OpenNETCF.Windows.Forms.WebBrowser 类,而不是使用 System.Windows.Forms.WebBrowser。 在这种情况下,您只需将 System.Windows.Forms.WebBrowser 替换为 OpenNETCF.Windows.Forms.WebBrowser
    /// <summary>
    /// An extended WebBrowser, can handle Mouse Events.
    /// </summary>
    public class WebBrowserEx : OpenNETCF.Windows.Forms.WebBrowser, IMessageFilter
    { 
        //.... 
    }

    派生自 OpenNETCF 的 WebBrowser,您可以在 .NET CF 1.0 中使用 WebBrowserEx,它具有内置功能,例如:禁用默认的上下文菜单,没有进度条...

多年来,我从 CodeProject 了解到很多有趣的事情,但这是我的第一篇文章,附带了一些代码... 所以,请随意评论和... ^^。

抱歉我的英语,这不是我的母语。

历史

  • 2008 年 7 月 13 日:文章提交
© . All rights reserved.