基于CefSharp开发浏览器(十一)增添F11、F12功能

一、前言

最近,一位朋友提出希望完善F11和12功能,因此在这篇文章中,我将分享F11全屏和F12开发者工具的思路以及代码,做个记录的同时也希望对cefsharp开发感兴趣的朋友提供一些思路。

二、WPF使用CefSharp.Winform带来的问题

上文提到,为了更好的使用输入法,我将项目中的CefSharp.WPF转为了CefSharp.WinForms,通过WindowsFormsHost来承载CefSharp.WinForms

现在CefSharp的版本为:

<PackageReference Include="CefSharp.WinForms.NETCore" Version="120.1.110" />

同时带来一些问题

1、AllowsTransparency属性不能用

AllowsTransparency只能设置为fasle否则WindowsFormsHost将不可见

可能的原因是Windows Forms本身并不直接支持透明度的特性,而WPF窗体的透明度是通过更先进的图形系统实现的。

因此,当启用WPF窗体的透明度时,可能会导致与Windows Forms控件的嵌入和交互方面的问题,从而使WindowsFormsHost不可用或出现异常。

2、键盘事件和鼠标事件不起作用

当Windows Forms控件在WindowsFormsHost中获得焦点时,WPF的键盘事件可能不会直接传递给嵌入的Windows Forms控件。

为了解决这些问题,需要编写一些额外的代码来将键盘和鼠标事件

三、使用Win32 API拦截和处理特定窗口消息

以下为处理窗口信息的代码,部分代码来源于官方示例

        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
        private void SetupMessageInterceptor()
        {
            if (messageInterceptor != null)
            {
                messageInterceptor.ReleaseHandle();
                messageInterceptor = null;
            }
            Task.Run(async () =>
            {
                try
                {
                    while (true)
                    {
                        IntPtr chromeWidgetHostHandle;
                        if (ChromiumRenderWidgetHandleFinder.TryFindHandle(this, out chromeWidgetHostHandle))
                        {
                            messageInterceptor = new ChromiumWidgetNativeWindow(this, chromeWidgetHostHandle);
                            messageInterceptor.OnWndProc(message =>
                            {
                                const int WM_MOUSEACTIVATE = 0x0021;
                                const int WM_NCLBUTTONDOWN = 0x00A1;
                                const int WM_DESTROY = 0x0002;
                                const int WM_MOUSEWHEEL = 0x020A;

                                // Render process switch happened, need to find the new handle
                                if (message.Msg == WM_DESTROY)
                                {
                                    SetupMessageInterceptor();
                                    return false;
                                }

                                if (message.Msg == WM_MOUSEACTIVATE)
                                {
                                    var topLevelWindowHandle = message.WParam;
                                    PostMessage(topLevelWindowHandle, WM_NCLBUTTONDOWN, IntPtr.Zero, IntPtr.Zero);
                                }

                                if(message.Msg == WM_MOUSEWHEEL)
                                {
                                    int rawDelta = (int)message.WParam.ToInt64();
                                    int delta = rawDelta >> 16;
                                    MouseWheelEvent?.Invoke(delta);
                                }
                                return false;
                            });

                            break;
                        }
                        else
                        {
                            await Task.Delay(10);
                        }
                    }
                }
                catch
                {
                }
            });
        }
View Code

WM_MOUSEACTIVATE 消息,它模拟在非客户区按下鼠标左键的操作。
WM_DESTROY 消息,说明窗口即将被销毁,它调用 SetupMessageInterceptor 方法重新设置消息拦截器。
WM_MOUSEWHEEL 消息,它触发一个事件 (MouseWheelEvent),传递鼠标滚轮的滚动量。

WM_MOUSEWHEEL 为处理浏览器网页缩放的消息,这里增加了MouseWheelEvent当有WM_MOUSEWHEEL 时

触发MouseWheelEvent来处理浏览器缩放,浏览器缩放可以参考 基于CefSharp开发浏览器(六)浏览器网页缩放

四、使用KeyboardHandler处理键盘事件

    public class CustomKeyboardHandler: KeyboardHandler
    {
        public Action<int> KeyboardCallBack;
        protected override bool OnKeyEvent(IWebBrowser chromiumWebBrowser, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey)
        {
            if (chromiumWebBrowser.IsBrowserInitialized)
            {
                KeyboardCallBack?.Invoke(windowsKeyCode);
            }
            return base.OnKeyEvent(chromiumWebBrowser, browser, type, windowsKeyCode, nativeKeyCode, modifiers, isSystemKey);
        }
    }
    public class CustomWebBrowser : ChromiumWebBrowser
    {
        public CustomWebBrowser()
        {
            this.KeyboardHandler = new CustomKeyboardHandler();
        }
    }

使用KeyboardHandler的OnKeyEvent方法处理键盘事件如F5,F11,F12等

五、F11全屏

由于程序的主体为一个自定义的TabControl进行的网页多开,直接处理TabControl令其全屏比较麻烦,

这里采用当按下F11时弹出一个WPF窗体来承载现有的CefSharp控件,当按下F11时,获取当前tab页的cefsharp控件,并将其填充到F11窗体中,

再次按下F11时将cefsharp控件 从新填充到tab页中,这样可以保证全屏和非全屏状态下使用的是同一个cefsharp实例。

窗体定义如下:

<Window x:Class="MWebBrowser.View.F11Window"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="F11Window" Height="450" Width="800"
        WindowState="Maximized"
        WindowStyle="None">
    <Grid x:Name="Parent">
       
    </Grid>
</Window>
    public partial class F11Window : Window
    {
        public WindowsFormsHost WebFormsHost { get; }
        public F11Window()
        {
            InitializeComponent();
            WebFormsHost = new WindowsFormsHost();
            InitData();
        }

        private void InitData()
        {
            Parent.Children.Add(WebFormsHost);
        }
    }

F11 处理代码如下:

        public static void F11(BrowserUserControl browserUserControl, WindowsFormsHost orgWebFormsHost)
        {
            DispatcherHelper.UIDispatcher.Invoke(() =>
            {
                if (f11Window != null)
                {
                    ExitFullscreen(browserUserControl, orgWebFormsHost);
                }
                else
                {
                    EnterFullscreen(browserUserControl, orgWebFormsHost);
                }
            });
        }

        private static void EnterFullscreen(BrowserUserControl browserUserControl, WindowsFormsHost orgWebFormsHost)
        {
            originalBounds = new Rect(
                browserUserControl.CefWebBrowser.Margin.Left,
                browserUserControl.CefWebBrowser.Margin.Top,
                browserUserControl.CefWebBrowser.Width,
                browserUserControl.CefWebBrowser.Height
                );
            f11Window = new F11Window();
            orgWebFormsHost.Child.Controls.Remove(browserUserControl);
            f11Window.WebFormsHost.Child = browserUserControl;
            f11Window.Show();
            Application.Current.MainWindow.Hide();
        }

        private static void ExitFullscreen(BrowserUserControl browserUserControl, WindowsFormsHost orgWebFormsHost)
        {
            f11Window.WebFormsHost.Child.Controls.Remove(browserUserControl);
            orgWebFormsHost.Child = browserUserControl;
            f11Window.Close();
            f11Window = null;
            Application.Current.MainWindow.Show();
        }

 

六、F12开发者工具

CefSharp提供了两种开发者工具展示方式

1、ShowDevTools

这种直接弹出一个新窗口

2、ShowDevToolsDocked

这种方式将DevTools控件嵌入到Winform的SplitContainer中,代码如下:

public void ShowDevToolsDocked()
{
    this.Invoke(() =>
    {
        if (browserSplitContainer.Panel2Collapsed)
        {
            browserSplitContainer.Panel2Collapsed = false;
        }
        Control devToolsControl = GetDevToolsControl();
        if (devToolsControl == null || devToolsControl.IsDisposed)
        {
            CefWebBrowser.ShowDevToolsDocked(browserSplitContainer.Panel2, nameof(devToolsControl), DockStyle.Fill);
        }
    });
}

 

七、F12开发者工具

gitee地址:https://gitee.com/sirius_machao/mweb-browser

github地址:https://github.com/sirius-chao/MWebBrowser

项目邀请:如对该项目有兴趣,欢迎联系我共同开发!!!

posted @ 2024-01-25 16:10  咸鱼翻身?  阅读(419)  评论(0编辑  收藏  举报