本文是关于CefSharp系列教程的延续,旨在解释CefSharp是什么以及如何使用它在WPF中显示HTML文档。本教程部分专注于CefSharp中的ResourceHandler,它是处理网络请求的一种方式。
ResourceHandler在CefSharp中非常有用,尤其是在需要显示静态HTML页面时。截至目前,ResourceHandler是同步执行的,因此应该谨慎使用,以避免阻塞网络请求。一个典型的应用场景是,当用户在浏览器应用的URL部分输入"about"字符串时,可以显示一个"关于"页面。
当然,"关于"页面并不是唯一的应用场景。ResourceHandler接口在CefSharp中的另一个有趣应用是作为查看器应用程序,它可以将文件内容(例如:Markdown)转换为HTML以供查看。还有其他用例,比如实现Windows中的控制面板页面。但这种用例在这里没有覆盖,而且可能更冒险地使用SchemeHandler来实现,将在本系列的下一篇文章中解释。
"关于ResourceHandler示例项目"演示了想要将URL与在实现时已知的静态内容关联的简单案例。应用程序在启动时显示一个"关于"页面,并允许加载该页面链接的页面。还有一个单独的"测试URL 2"页面,可以通过鼠标点击加载。
以下是驱动应用程序的XAML代码,位于MainWindow.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由三部分组成:顶部的按钮列表、中间的名为browser的CefSharp浏览器控件,以及显示页面加载信息的状态栏。
让看看顶部的按钮是如何工作的。MainWindows.xaml代码绑定到一个名为AppViewModel的viewModel类,该类存储在"KnowledgeBase/ViewModels/AppViewModel.cs"中。这个viewmodel提供了两个命令属性,分别称为TestUrlCommand和TestUrl1Command。当点击"关于"按钮时,第一个命令被调用,第二个命令在点击"测试URL 2"按钮时被调用。
上述每个命令所做的工作并不多,只是设置AppViewModel类的BrowserAddress属性:
BrowserAddress = TestResourceUrl;
BrowserAddress = TestUnicodeResourceUrl;
这个属性反过来与MainWindow.xaml中看到的CefSharp web浏览器控件绑定。那么,CefSharp是如何知道每个URL的含义,并且为什么它实际上显示自定义HTML呢?答案隐藏在MainWindow构造函数中,其中调用了AppViewModel.RegisterTestResources(browser);方法,参数为ChromiumWebBrowser实例:
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中)并将其与在字符串中定义的HTML内容(responseBody或unicodeResponseBody)关联。因此,每当IWebBrowser控件(实际上是MainWindow.xaml中的ChromiumWebBrowser实例)被请求加载任一URL时,它通过加载上述预定义的HTML字符串来实现这一点。
Sample2文件夹还包含一个"Sample 2 MarkDown ResourceHandler"应用程序,该应用程序可用于在WPF应用程序中查看Markdown内容。这个示例应用程序包含了Markdown转换器项目的副本。以下是用户点击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子文件夹中加载示例markdown文本:
这个应用程序的工作原理与上面讨论的"关于"示例应用程序非常相似,不同之处在于,该应用程序可以根据文件系统(在bin/Debug或bin/Release文件夹中)中存储的Readme.md文件生成半静态内容。
这个示例应用程序还实现了一种刷新功能。实际上,可以在运行时编辑并保存Readme.md文件,然后点击Markdown按钮,在示例应用程序的窗口中看到编辑结果。
理解这个刷新功能的重要代码部分是以下几行:
RegisterMarkdownTestResources(browser);
BrowserAddress = TestMarkDown2HTMLConversion;
在TestUrl1Command属性中。当用户点击Markup时执行此命令。第一行重新加载Markdown文件,将其转换为HTML并存储在字符串中,第二行更改当前地址以通知浏览器新的请求。
请务必查看MainWindow.xaml.cs中的BrowserNavStateChanged(object sender, NavStateChangedEventArgs e)和BrowserStatusMessage(object sender, StatusMessageEventArgs e)方法,以了解如何更新MainWindow的状态栏和标题部分。
值得指出的是,示例应用程序扩展了http地址方案。CefSharp中的ResourceHandler也可以与自定义URI一起使用,例如:about,但这需要自定义SchemeHandler,将在本系列的下一篇文章中解释。
示例还包含AppViewModel类中的Dev Tools按钮和命令部分:
Chrome开发工具可用于验证和调试应用程序中加载的HTML、Javascript或CSS样式的状态。只需将鼠标悬停在某个部分上,例如上面的截图中的h1,就可以看到应用程序主窗口中的相应高亮显示。
[1] 在WPF和CefSharp中显示HTML教程第1部分 http://www.codeproject.com/Articles/881315/Display-HTML-in-WPF-and-CefSharp-Tutorial-Part