在 WPF 中显示 HTML 和 CefSharp 教程 第二部分





5.00/5 (9投票s)
如何在 CefSharp 中实现 ResourceHandler 以在 WPF 中显示自定义 HTML。
源代码下载自 GitHub
引言
本文接续之前一篇 文章,该文章解释了 CefSharp 是什么以及如何将其用于在 WPF 中显示 HTML 文档 [1]。本教程部分重点介绍 CefSharp 中的 ResourceHandler
[2]。ResourceHandler
是处理网络请求的一种方法。
背景
ResourceHandler 的应用示例
ResourceHandler
对于显示静态 HTML 非常有用。在撰写本文时,ResourceHandler
是以同步方式执行的,因此应谨慎用于网络请求,因为它们是阻塞的。此场景下的一个应用领域是例如“关于”页面,当用户在浏览器应用程序的 URL 部分输入字符串“about”时,该页面可以出现。
但“关于”页面肯定不是极限。ResourceHandler
接口在 CefSharp 中的另一个有趣的应用领域是查看器应用程序,该应用程序可以将文件内容(例如 MarkDown)转换为 HTML 以供查看。还有其他用例,例如实现 Windows 中常见的 控制面板 页面。但此处不涵盖此类用例,并且使用 SchemeHandler
实现此目的可能更具优势,我们将在本系列的下一篇文章中进行解释。
使用代码
Sample 2 关于 ResourceHandler 的演示项目
名为 Sample 2 About ResourceHandler 的项目演示了一个简单的场景,在该场景中,我们将一个 URL 与在实现时已知的静态内容关联起来。该应用程序启动时会显示一个“关于”页面,并允许加载从该页面链接的页面。还有一个单独的“测试 URL 2”页面,可以通过鼠标单击加载。
以下是驱动 MainWindow.xaml 文件中应用程序的 XAML 代码
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.Resources>
<conv:InverseBooleanConverter x:Key="InverseConv" />
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Content="About"
Command="{Binding TestUrlCommand}"
IsEnabled="{Binding ElementName=browser, Path=IsLoading, Converter={StaticResource InverseConv}}"/>
<Button Content="MarkDown" Grid.Column="1"
Command="{Binding TestUrl1Command}"
CommandParameter="{Binding ElementName=browser}"
IsEnabled="{Binding ElementName=browser, Path=IsLoading, Converter={StaticResource InverseConv}}"/>
<Button Content="Dev Tools" Grid.Column="2"
Command="{Binding DevToolsCommand}"
CommandParameter="{Binding ElementName=browser}"
IsEnabled="{Binding ElementName=browser, Path=IsLoading, Converter={StaticResource InverseConv}}"/>
</Grid>
<cefSharp:ChromiumWebBrowser Grid.Row="1"
Address="{Binding BrowserAddress, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Title="{Binding BrowserTitle, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Name="browser" />
<StatusBar Grid.Row="2" >
<TextBlock Name="Status" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</StatusBar>
</Grid>
我们可以看到 MainWindow 由 3 部分组成
- 顶部的按钮列表;
- 中间的 CefSharp 浏览器控件,名为 browser;
- 以及显示页面当前是否正在加载或已完成加载的 StatusBar。
让我们看看顶部的按钮是如何工作的。
MainWindow.xaml 代码绑定到一个名为 AppViewModel
的 viewModel 类,该类存储在“KnowledgeBase/ViewModels/AppViewModel.cs”中。此 viewModel 提供了两个名为 TestUrlCommand
和 TestUrl1Command
的命令属性。当单击“关于”按钮时,将调用第一个命令;当单击“测试 URL 2”按钮时,将调用第二个命令。
上述每个命令除了设置 AppViewModel
类中的 BrowserAddress
属性之外,并没有做太多事情。
BrowserAddress = TestResourceUrl;
BrowserAddress = TestUnicodeResourceUrl;
正如我们在 MainWindow.xaml 列表所示,此属性反过来绑定到 CefSharp 网页浏览器控件。那么,CefSharp 是如何知道每个 URL 的含义以及为什么它实际上显示自定义 HTML 的呢?这个问题的答案隐藏在 MainWindow
构造函数中,其中将 ChromiumWebBrowser 实例作为参数调用了 AppViewModel.RegisterTestResources(browser);
方法。
public MainWindow()
{
InitializeComponent();
AppViewModel.RegisterTestResources(browser);
DataContext = new AppViewModel();
browser.StatusMessage += BrowserStatusMessage;
browser.NavStateChanged += BrowserNavStateChanged;
}
用于注册每个 ResourceHandlers
的代码相当简单。
public static void RegisterTestResources(IWebBrowser browser)
{
var factory = browser.ResourceHandlerFactory;
if (factory != null)
{
const string responseBody =
"<html><body><h1>About</h1>"
...
+ "</body></html>";
factory.RegisterHandler(TestResourceUrl, ResourceHandler.FromString(responseBody));
const string unicodeResponseBody = "<html><body>整体满意度</body></html>";
factory.RegisterHandler(TestUnicodeResourceUrl, ResourceHandler.FromString(unicodeResponseBody));
}
}
该代码获取一个 URL(在 TestResourceUrl
或 TestUnicodeResourceUrl
中定义),并将其与字符串(responseBody
或 unicodeResponseBody
)中定义的 HTML 内容相关联。因此,每当 IWebBrowser
控件(实际上是 MainWindow.xaml 中的 ChromiumWebBrowser
实例)被请求加载任一 URL 时,它都会通过加载上述预定义的 HTML 字符串来实现。
Sample 2 Markdown ResourceHandler
Sample2 文件夹还包含一个名为 Sample 2 MarkDown ResourceHandler 的应用程序,可用于在 WPF 应用程序中查看 MarkDown 内容。此示例应用程序包含 Markdown 转换器项目 [3] 的副本。这是用户单击 MarkDown 按钮时显示的内容的屏幕截图。
这看起来并不那么有趣,毕竟它只是纯 HTML,但这种 HTML 是基于 Markdown 语法的,比 HTML 更容易编辑。内容的实际来源存储在此示例应用程序的 KnowledgeBase/SampleData/Readme.md 文件中。
此示例应用程序的工作方式与上面讨论的“关于”示例应用程序非常相似。但在某些地方有所不同。此应用程序通过 App.xaml
中配置的 App.xaml.cs
启动方法启动。
private void Application_Startup(object sender, StartupEventArgs e)
{
var mainWindow = new MainWindow();
var viewModel = new AppViewModel();
viewModel.RegisterTestResources(mainWindow.browser);
mainWindow.DataContext = viewModel;
mainWindow.Show();
}
AppViewModel
类中的此 RegisterTestResources
方法从 KnowledgeBase 项目的 SampleData 子文件夹中存储的 Readme.md 文件加载示例文本。此应用程序的工作方式与上面讨论的“关于”示例应用程序非常相似,不同之处在于,该应用程序可以基于文件系统中存储的 Readme.md 文件(在 bin/Debug 或 bin/Release 文件夹中)生成半静态内容。
此示例应用程序还实现了一种刷新功能。您实际上可以在运行时编辑和保存 Readme.md 文件,然后单击 MarkDown 按钮以在示例应用程序的窗口中查看编辑结果。
理解此刷新功能的关键代码部分是以下几行
RegisterMarkdownTestResources(browser); BrowserAddress = TestMarkDown2HTMLConversion;
位于 TestUrl1Command
属性内部。单击 Markup 时会执行此命令。第一行重新加载 Markdown 文件,将其转换为 HTML 并将其存储在字符串中,然后重新注册新内容。第二行更改当前地址,以告知浏览器新的请求。
备注
请务必查看 MainWindow.xaml.cs 中的 BrowserNavStateChanged(object sender, NavStateChangedEventArgs e)
和 BrowserStatusMessage(object sender, StatusMessageEventArgs e)
方法,以了解 StatusBar 和 MainWindow 的标题部分是如何更新的。
值得指出的是,该示例应用程序扩展了 http
地址方案。CefSharp 中的 ResourceHandler
也可以与自定义 URI 一起使用,例如 about
,但这需要自定义 SchemeHandler
,我们将在本系列的下一篇文章中进行解释。
该示例还在 AppViewModel 类中包含一个 Dev Tools 按钮和命令部分。
此 Chrome 开发工具可用于验证和调试应用程序中加载的 HTML、Javascript 或 CSS 样式。只需将鼠标悬停在某个部分(例如上图中的 h1)上,即可在应用程序主窗口中看到相应的突出显示。
参考文献
- [1] WPF 和 CefSharp 显示 HTML 教程第一部分
https://codeproject.org.cn/Articles/881315/Display-HTML-in-WPF-and-CefSharp-Tutorial-Part
- [2] ChromiumEmbedded 项目中的 ResourceHandler 文档
https://code.google.com/p/chromiumembedded/wiki/GeneralUsage#Request_Handling
http://magpcss.org/ceforum/apidocs3/projects/%28default%29/CefResourceHandler.html
- [3] MarkDownSharp 项目,用于将 MArkDown 内容转换为 HTML
http://code.google.com/p/markdownsharp/