Avalonia框架使用CefGlue嵌入网页

Avalonia跨平台框架列出了以下几种嵌入网页的方式:(来源

Web Browsers

  • CefGlue - .NET/Mono binding for The Chromium Embedded Framework (CEF).
  • CefNet - .NET binding for the Chromium Embedded Framework (CEF).
  • OutSystems WebView - Fully featured Avalonia WebView Control.

平时工程中常用的网页嵌入框架是CefSharp,因此优先考虑Cef方式。其中CefNet已经许久没有更新,于是决定选用CefGlue.

CefGlue的仓库链接:CefGlue,仓库里包含了WPF和Avalonia两种版本的Demo. 

CefGlue支持的平台和架构如下图

引用CefGlue可以在Nuget程序包管理器中直接下载程序包:

直接引用dll的话主要用到这四个,没有用到WPF相关的库:

由于我们项目中需要用到浏览器透明效果,这一功能需要开启离屏渲染,我下载源码对Demo进行调试时发现CefGlue官方代码中离屏渲染的焦点获取有些问题,导致无法使用键盘输入,所以对源码进行了一点修改,只有一行代码(浏览器所在控件没有设置为可获取焦点,加上就可以了):

如果要用输入法,还需要实现一个TextInputMethodClient,并给_control添加TextInputMethodClientRequestedEvent事件的响应,这样Avalonia的输入法管理器InputMethodManager在拿到Client之后才会唤起输入法。现在这个写法是始终在浏览器界面上启用输入法的,输入密码时也是,所以还需要改进。

namespace Xilium.CefGlue.Avalonia.Platform
{

internal class OsrInputMethodClient : TextInputMethodClient
{
    private ITextInputMethodImpl _im;
    private Visual _presenter;
    public override Visual TextViewVisual => _presenter!;

    public override bool SupportsPreedit => true;

    public override bool SupportsSurroundingText => true;

    public override string SurroundingText => "";

    public override Rect CursorRectangle => default;

    public override TextSelection Selection { get; set; }

    public void SetPresenter(Visual? presenter)
    {
        _presenter = presenter;
        if (presenter != null)
        {

            _im = (TopLevel.GetTopLevel(_presenter) as ITextInputMethodRoot)?.InputMethod;
            
        }
        else {  _im = null; }
        
    }
}

    internal class AvaloniaControl : Common.Platform.IControl
    {
        // on Windows all browsers will be hosted in the same long-lived window to prevent crashes
        // during browser window creation, which could occur if the hosting window was closed
        private static IPlatformHandle _hostWindowPlatformHandle;

        private IHandleHolder _browserView;
        private readonly IAvaloniaList<Visual> _controlVisualChildren;

        protected readonly Control _control;
        protected readonly OsrInputMethodClient _imClient = new OsrInputMethodClient();

        public event Action GotFocus;
        public event Action<CefSize> SizeChanged;

        public AvaloniaControl(Control control, IAvaloniaList<Visual> visualChildren)
        {
            _control = control;
            _controlVisualChildren = visualChildren;
            _control.Focusable = true;
            _control.GotFocus += OnGotFocus;
            _control.LostFocus += OnLostFocus;
            _control.LayoutUpdated += OnLayoutUpdated;

            _imClient.SetPresenter(_control);
            Control.TextInputMethodClientRequestedEvent.AddClassHandler<Control>((tb, e) =>
            {                
                e.Client = _imClient;
                
            });
        }

        private void OnLostFocus(object sender, global::Avalonia.Interactivity.RoutedEventArgs e)
        {
            _imClient.SetPresenter(null);
        }

        private void OnLayoutUpdated(object sender, EventArgs e)
        {
            SizeChanged?.Invoke(new CefSize((int)_control.Bounds.Width, (int)_control.Bounds.Height));
        }

        private void OnGotFocus(object sender, GotFocusEventArgs e)
        {
            GotFocus?.Invoke();
            _imClient.SetPresenter(_control);
        }
}

}

以上这些改动可查看这个git

CefGlue提供的Demo引用了内部项目和libcef库本身,为了测试CefGlue独立使用的效果,我仿照Demo单独创建了一个工程,只引用CefGlue的库,工程结构如图:

关键代码修改如下:

public static AppBuilder BuildAvaloniaApp()
{
    try
    {
        LogHelper.Info("APP创建");
        Console.WriteLine("DEBUG_CONSOLE: BuildAvaloniaApp");
        string defaultFont = "fonts:AppFontFamilies#FZLanTingHeiS-R-GB";
        Uri fontName = new Uri("fonts:AppFontFamilies", UriKind.Absolute);
        Uri fontSource = new Uri("avares://AvaloniaOnlyApp/Res/Fonts", UriKind.Absolute);
        var ap = AppBuilder.Configure<App>()
        .UsePlatformDetect()
        .WithInterFont()
        .LogToTrace()
        .With(new FontManagerOptions()
        {
            //使用编译到程序中的字体
            DefaultFamilyName = defaultFont,
            FontFallbacks = new[] { new FontFallback { FontFamily = new FontFamily(defaultFont) } }

            //麒麟系统用下面这个字体设置
            //DefaultFamilyName = "Noto Sans CJK SC",
            //FontFallbacks =
            //        [
            //                new FontFallback { FontFamily = "文泉驿正黑" },
            //                new FontFallback { FontFamily = "DejaVu Sans" },
            //        ],
        })
        .ConfigureFonts(manager=>manager.AddFontCollection(new EmbeddedFontCollection(fontName, fontSource)))
        .AfterSetup(_ => CefRuntimeLoader.Initialize(new CefSettings()
        {
            WindowlessRenderingEnabled = true,
            //BackgroundColor = new CefColor(0x00, 0xff, 0xff, 0xff),
            Locale = "zh-CN",
            RootCachePath = AppDomain.CurrentDomain.BaseDirectory + "CefCache",
            LogFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Log", "CefLog.log"),
            LogSeverity = CefLogSeverity.Debug,
        },
              flags: new Dictionary<string, string> {
                  {"--ignore-urlfetcher-cert-requests", "1" },
                  {"--ignore-certificate-errors", "1" },
                  {"--disable-web-security", "1" },
                  {"--no-sandbox","1"},
                  {"disable-keyring-access","1" },
                  {"disable-gpu", "1" },
                  {"disable-gpu-compositing", "1" },
                  {"enable-begin-frame-scheduling", "1" },
                  {"disable-gpu-vsync", "1" }
                  //{"--disable-gpu-sandbox","1" },

              }.ToArray(),
              customSchemes: new[] {
                new CustomScheme()
                {
                    SchemeName = "test",
                    SchemeHandlerFactory = new CustomSchemeHandler()
                }
              }));

        return ap;
    }
    catch (Exception ex)
    {
        LogHelper.Fatal("Program 异常",ex);
        throw;
    }
   
}

 工程在国产化系统麒麟V10上运行,需要注意两个地方:

  1. 默认字体问题:Avalonia启动时会自动选择默认字体,但是在麒麟环境下这个DefaultFamilyName获取到的不正确或无法使用,导致程序运行失败,有两种解决方法,一是如代码中注释掉的地方一样,直接将它设置为麒麟系统下指定的中文字体:
    //麒麟系统用下面这个字体设置
                DefaultFamilyName = "Noto Sans CJK SC",
                FontFallbacks =
                        [
                                new FontFallback { FontFamily = "文泉驿正黑" },
                                new FontFallback { FontFamily = "DejaVu Sans" },
                        ],

    二就是将要用的字体文件作为资源加入到程序中,启动时直接使用自带字体。具体方法可以看这个解决WPF+Avalonia在openKylin系统下默认字体问题 - 踏平扶桑 - 博客园和官方文档如何使用自定义字体 | Avalonia Docs

  2. Cef启动参数:在Linux系统下运行时,--no-sandbox参数是必须的。为了能正常加载网页还增加了其他参数,具体查看上面代码即可。

代码编译完后点击在VS2022中点击发布,部署模式选择独立,这样就不需要单独安装.net运行环境了。

由于是直接引用的CefGlue的dll,所以它依赖的libcef等库文件需要手动复制到目标文件夹中,具体文件列表如图:

这些依赖库可以从Demo的生成路径中获取。第一个文件夹中是CefGlue的子进程,第二个是chromium语言依赖,第三个是运行环境相关的依赖,里面不需要的平台或架构文件夹可以删除进行精简。

将整个目标文件夹打包到Linux操作系统中就可以运行了。我是直接压缩复制到Linux下再解压运行的,在Windows下直接打DEB等安装包比较麻烦。

运行时需要注意,CefGlue的浏览器子进程Xilium.CefGlue.BrowserProcess需要修改权限(主程序也是一样的,但是子进程不会提示无权限,只会运行到一半直接退出)

运行效果涉及内部数据,不放上来

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值