65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (9投票s)

2015 年 4 月 9 日

MIT

5分钟阅读

viewsIcon

64711

如何在 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 提供了两个名为 TestUrlCommandTestUrl1Command 的命令属性。当单击“关于”按钮时,将调用第一个命令;当单击“测试 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(在 TestResourceUrlTestUnicodeResourceUrl 中定义),并将其与字符串(responseBodyunicodeResponseBody)中定义的 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)上,即可在应用程序主窗口中看到相应的突出显示。

参考文献

© . All rights reserved.