有过从事前端开发经验的工作者,还想要开发最好的桌面程序,最好是能继承前端技术UI设计界面的优势,这里讲一讲如何集成主流浏览器控件的方案。
这里用的是CefSharp.Wpf的插件的解决方案,
该插件是基于Chromium内核的开源框架做出来的,
- 支持Windows/Linux/macOS平台
- 提供完整的浏览器功能,包括HTML5、CSS3、JavaScript等
- 典型应用:VS Code、Spotify等
当前趋势:
- Chromium内核已成为主流选择(CEF、WebView2)
- 微软已放弃旧版IE/EdgeHTML内核,改用Chromium,
- Chromium基于WebKit,WebKit在苹果生态中仍保持优势
开发者更倾向选择跨平台、维护活跃的方案桌面程序使用主流浏览器控件有哪些呢,肯定是用weikit内核的
回到正题,本文章将以开发WPF(Windows Presentation Foundation)桌面应用程序为例进行讲解。
基于Microsoft的.NET Framework 4.7.2及以上版本进行开发,这是一套成熟的Windows应用程序开发框架,提供了丰富的UI控件库和强大的数据绑定功能。
在座的同学需要熟悉以下开发环境和工具,动手时准备好Windows系统的电脑:
- 开发工具:Visual Studio 2022社区版或专业版(2020版本亦可);
- 编程语言:使用C# 8.0及以上版本;
- 使用开发工具创建好一个WPF项目例子
文章目录
WebBrowser
开发过项目的同学都知道,这是开发工具组件工具箱内置的控件,是桌面程序的常见浏览器控件,
使用控件
在页面布局文件里,添加的控件
xml
<WebBrowser x:Name="webBrowser1"/>
此控件使用的IE内核,浏览很多网页会有兼容问题,
渲染简单HTML是没有问题的,常见于帮助文档,展示使用协议等...
如果是想要主流浏览器的Web页面渲染一样的效果还是有问题的,
如有需要,这里就用替代方案,
接下来,解决问题
CefSharp.Wpf
这是类似谷歌浏览器内核webkit的插件,
安装控件
此插件可以在NeGet包管理器上找到,安装后如下图

注意选
CefSharp.Wpf安装前,留意看看描述下的依赖版本,其它的是相关依赖,会自动安装在一起;
- 若安装失败,请检查项目属性设置的.Net Framework框架版本是否低于插件的依赖版本,如果是,就往上提高一下项目的框架版本再试;
- 项目的框架版本越低,就能运行在越旧的电脑Windows系统版本上;
使用控件
在页面布局文件里,这样添加控件
xml
<Window x:Class="WpfApp2.MainWindow"
...
xmlns:cefSharp="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
Title="MainWindow" Height="450" Width="800">
<Grid>
<cefSharp:ChromiumWebBrowser x:Name="browser" />
</Grid>
</Window>
在写入使用的控件cefSharp时,按下保存键,需要留意到输入位置的自动引用提示,按确定后会自动添加引用,如果没有,需手动填写,声明引用xmlns:cefSharp=...
初始化配置
添加CefSharp引用,
在页面代码里,这样做
csharp
public partial class MainWindow : Window
{
public MainWindow()
{
CefSharp.Cef.Initialize(); // 确保CEF初始化
InitializeComponent();
//如果没有在xaml页面布局中添加此控件,就在代码中添加
//browser = new ChromiumWebBrowser();
}
}
注意编译目标平台要选
x64或者x86,否则编译会报错
用户设置
在初始化里,修改此控件的用户设置
csharp
var settings = new CefSharp.Wpf.CefSettings();
settings.LogSeverity = CefSharp.LogSeverity.Verbose;
settings.CachePath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache");
settings.Locale = "zh-CN";
settings.AcceptLanguageList = "zh-CN";
settings.UserAgent = "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Mobile Safari/537.36";
CefSharp.Cef.Initialize(settings);
注意上面的初始化一段代码要放在初始化代码调用
InitializeComponent();方法之前,否则运行会抛出异常;不同的CefSharp.wpf版本的初始化步骤中的CefSettings配置属性, 和一些方法会有所改动,部分可能会不支持;
考虑到新手看不懂一些配置项怎么按需求设置,百度Ai一下,你就知道了
初始化的代码不应该重复调用,否则会报错,
建议把初始化代码放在程序执行入口开始时执行一次,
要判断是否已初始化,代码如下
csharp
var isInitialized = false;
try
{
isInitialized = Cef.IsInitialized ?? false;
}
catch(Exception ex)
{
string errMsg = ex.Message;
//...
throw new Exception(errMsg);
}
判断初始化代码可能会抛出异常:
System.IO.FileNotFoundException:"未能加载文件或程序集"CefSharp.Core.Runtime, Version=140.1.140.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138"或它的某一个依赖项。系统找不到指定的文件。"
尝试解决:需要把项目属性中的编译目标Any CPU 改为 x64,再编译运行
自带功能
此控件有自带了一些基本的功能
访问地址
调用控件的Navigate方法,访问地址
csharp
var url = "https://www.baidu.com/";
browser.Navigate(url);
例如打开开发者工具,这是开发者常用到的调试必备工具,
其它基本功能就不多了,请自行探索
开发者工具
再调用浏览器控件的方法即可打开,
代码如下
csharp
using CefSharp;
//打开DevTools
browser.ShowDevTools();
查看网页源码
查看网页源码时异步操作的,
在初始化里添加,网页加载完成时处理事件
csharp
browser1.FrameLoadEnd += OnFrameLoadEnd;
当网页加载完成时,就可以看到源码
csharp
private void OnFrameLoadEnd(object sender, CefSharp.FrameLoadEndEventArgs e)
{
e.Frame.GetSourceAsync().ContinueWith(t =>
{
Debug.WriteLine($">>> OnFrameLoadEnd Source:{t.Result}");
});
}
执行网页脚本
要等网页加载完才可以执行脚本
csharp
var jsCode = "alert(\"Hello CefSharp\");";
browser.Load("https://www.baidu.com");
browser.ExecuteScriptAsyncWhenPageLoaded(jsCode);
要立即执行脚本,
csharp
var jsCode = "alert(\"Hello CefSharp\");";
browser.ExecuteScriptAsync(jsCode);
执行后需要返回结果
csharp
var jsCode = "(function(){alert(\"Hello CefSharp\");})()";
browser.EvaluateScriptAsync(jsCode).ContinueWith(res => {
JavascriptResponse jRes = res.Result;
Debug.WriteLine($">>> evaluate sj is success res:{jRes.Result}");
});
其中,
jRes.Result返回的对象类型,有dynamic或者List<dynamic>;
实现功能
此控件默认是没有浏览器常见功能的,
不过有提供接口,需要自己实现过程,
处理打开链接
默认是创建新窗口打开新链接,
要在同一窗口中打开,
在初始化时,添加如下代码,
csharp
browser.LifeSpanHandler = new OpenPageSelf();
新建一个处理类,重写LifeSpanHandler类的方法,代码如下
csharp
class OpenPageSelf : LifeSpanHandler
{
public OpenPageSelf() : base()
{
}
override
protected bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
{
// 限制在同一窗口中加载
chromiumWebBrowser.Load(targetUrl);
newBrowser = null;
return true;
}
}
禁用右键菜单
在初始化里,设置代码如下
csharp
browser.MenuHandler = new OpenMenuPanel();
新建一个处理类,实现继承IContextMenuHandler的方法
csharp
class OpenMenuPanel : IContextMenuHandler
{
public void OnBeforeContextMenu(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
{
model.Clear();
}
public bool OnContextMenuCommand(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags)
{
return false;
}
public void OnContextMenuDismissed(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame)
{
}
public bool RunContextMenu(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback)
{
return false;//这里禁用
}
}
处理下载文件
此控件默认是未实现下载功能的,
在初始化里,实现下载功能
csharp
browser.DownloadHandler = new DownloadHandler();
新建一个下载类,实现继承IDownloadHandler的方法
csharp
class DownloadHandler : CefSharp.IDownloadHandler
{
public bool CanDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, string url, string requestMethod)
{
return true;//返回true可下载
}
public void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback)
{
if (string.IsNullOrEmpty(downloadItem.FullPath))
{
var path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString());
System.IO.Directory.CreateDirectory(path);
downloadItem.FullPath = System.IO.Path.Combine(path, downloadItem.SuggestedFileName);
}
if (callback.IsDisposed)
{
return;
}
using (callback)
{
//设置初始下载路径,是否打开保存文件对话框
callback.Continue(downloadItem.FullPath, true);
}
}
public void OnDownloadUpdated(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback)
{
var fileName = downloadItem.FullPath;
if (downloadItem.IsInProgress)
{
//下载中
}
else if (downloadItem.IsComplete)
{
//此处下载完成,可以处理一些,例如通知,打开文件
}
}
}
使用证书访问
如果是https加密访问的话,默认可能会无法正常访问,提示证书安全问题,
初始化里设置到浏览器控件中
csharp
browser.RequestHandler = new ExampleRequestHandler();
新建一个请求处理类ExampleRequestHandler,继承它的方法RequestHandler,
可以采取一些措施来忽略证书错误,,代码如下
csharp
public class ExampleRequestHandler : RequestHandler
{
protected override bool OnCertificateError(IWebBrowser chromiumWebBrowser, IBrowser browser, CefErrorCode errorCode, string requestUrl, ISslInfo sslInfo, IRequestCallback callback)
{
Task.Run(() =>
{
if (!callback.IsDisposed)
{
using (callback)
{
// 如果URL包含特定字符串,则忽略证书错误
if (requestUrl.Contains("reportSystem"))
{
callback.Continue(true); // 重点部分
}
else
{
callback.Continue(false);
}
}
}
});
return true;
}
}
或者,在初始化的设置CefSettings中添加配置项,代码如下
csharp
// 忽略HTTPS证书的问题
settings.CefCommandLineArgs.Add("--ignore-urlfetcher-cert-requests", "1");
settings.CefCommandLineArgs.Add("--ignore-certificate-errors", "1");
// 禁止启用同源策略安全限制,禁止后不会出现跨域问题
settings.CefCommandLineArgs.Add("--disable-web-security", "1");
使用问题
脚本执行问题
执行脚本代码抛出异常,
使用不同的CefSharp.Wpf版本,新版的对脚本的执行方式做了安全限制,
可能需要在初始化里添加以下配置
csharp
browser.JavascriptObjectRepository.Settings.LegacyBindingEnabled = true;
//browser.JavascriptObjectRepository.Settings.JavascriptBindingApiEnabled = true;
FileLoadException
编译通过, 运行时会抛出异常,如下
"System.IO.FileLoadException"类型的未经处理的异常在 CefSharp.Wpf.dll 中发生
是不是改变了库版本哦, 或者改了项目框架版本,
在解决方案名称上鼠标右键,弹出如下图, 选择 管理解决方案的 NuGet程序包

这时, NuGet管理器上会多出一个合并选项卡`, 看看有没有,
有的话, 处理合并, 这也是冲突的缘故,
没有的话, 重新安装CefSharp的其它版本看看,
先卸载了, 重新安装
卸载完之前留意自动安装依赖的其它项, 发现管理器多出了哪两个, 那是没有经过自己手动安装的, 也一起卸了,卸载干净重新安装试试,

会不会是这个引起的冲突呢
如果这次还抛出异常了, 开发工具下控制台输出的调试信息, 如下
FileLoadException: 未能加载由"CefSharp.Core.Runtime.dll"导入的过程
检查包, 链接库版本冲突
估计是文件占用了, CefSharp.Wpf 运行初始化后未正常关闭,需要在程序关闭时执行如下代码
if (Cef.IsShutdown != true)
{
Cef.Shutdown();
}
若还是不行,将编译输出的所有文件都清空, 重新编译运行看看呢;
考虑到新手在实现时可能会没招,可以试试求助万能的AI...
相关项目
如果你觉得使用此控件安装包体积过大,可以考虑使用轻便的控件Geckofx,如下
桌面程序使用火狐浏览器内核Geckofx控件详解
写到这里,到此告一段落...
考虑到新手会嫌写代码一个个实现太慢,有没有速成的方法,可以参考作者开发过的浏览器相关项目源码
把自己认为最好的在线AI智能助手写到一个自用浏览器里,好主意!
在需要的时候按一键就用上了,同时不用担心装在电脑里的AI读取你的使用记录...(不可能 )
