WinRT 手机和应用的帮助基础架构
一种将帮助页面嵌入 Windows RT 或 Phone 程序的方法
引言
在为 Windows Phone 创建程序时,我希望以一种与设备设置兼容但又相当灵活地处理不同设备配置的格式为用户提供帮助。我选择的解决方案是将 HTML 页面嵌入到代码文件中,并根据设备设置进行渲染。
背景
要充分利用这个项目,您需要一些 XAML 和 C# 的背景知识,但(我希望)您仍然可以采用相当模板化的方法来使用此代码并获得不错的结果。
包含内容
我将此代码作为一个可用的通用应用提供,它可以生成 Windows 现代应用和 Windows Phone 8.1 应用——它们几乎等效,只是运行在不同的平台上。解压文件并将解决方案加载到 Visual Studio 2013(我使用的是更新 4)中,您应该能够构建它并在设备或模拟器上运行生成的代码。当您这样做时,它会构建一个 Windows Phone 应用,该应用能够显示如下所示的帮助文本。
还有一个 Windows 10 通用应用版本,它使用 Visual Studio 2015 进行编译,它与 Windows 8.1 版本非常相似,但也有一些区别。主要区别集中在运行时管理平台差异(Windows 10 模型)与使用多个构建在编译时管理它们(Windows 8.1 模型)。其他必要的更改与使用预定义资源名称有关(Windows 10 中名称已更改)。一些更改(例如 Helper.cs 中的更改)仅仅是对代码的改进和/或利用 C# 6 的新功能(VS 2015 的那个)。帮助页面本身在结构上是相同的。
由于我没有 Windows 10 手机,因此没有在 Windows 10 手机上进行测试,但已通过手机模拟器和桌面进行了测试。
在您自己的项目中使用的代码
要使用此代码,您需要将一些文件复制到您的项目中。您需要 Helper.cs,它不渲染任何 UI,但包含各种有用的类。然后,您需要一个页面来实际显示帮助(将我的 HelpPage.xaml 和 HelpPage.cs 作为起点进行复制)。
在您的程序中的某个地方,您将需要允许用户导航到帮助页面,为此,请添加一个资源(HelpViewModel,见下文)和一些控件(可能是按钮,也见下文)。您还需要使用 HTML 编写一个或多个帮助页面,并将它们放在项目中的“Help”目录中。
在复制的文件中,请将“UniversalHelp
”替换为您程序的名称。
在您想要放置帮助控件的每个页面中,您将添加类似这样的内容:
<Page.Resources>
<local:HelpViewModel x:Key="HelpContext" />
</Page.Resources>
实际的控件看起来像这样:
<Button Content="Help for this page"
Command="{Binding Help, Source={StaticResource HelpContext}}"/>
查看示例中的“MainPage.xaml”以了解其在上下文中的外观。
帮助文件本身
构成程序帮助系统的各个文件都相当标准,但它们确实有一些要求,这是一个示例文件(包含指向其他页面的链接的索引文件)。
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>help index</title>
<link href="default.css" rel="stylesheet" type="text/css" />
</head>
<body>
<p>Other information:</p>
<ul>
<li><a href="introduction.html">Introduction</a></li>
<li><a href="main.html">Main Page</a> (sample text)</li>
</ul>
</body>
</html>
最关键的一行是定义样式表为“default.css”。这很重要,因为代码会自动生成一个具有正确值的该名称的样式表,以匹配当前的设备设置。
“viewport
”很重要,因为没有它,项目符号列表中的行(以 <li>
开头的行)在调整字体大小时不会改变字体大小。
<title>
将显示在每个帮助页面顶部的内容。
我发现编辑这些文件的最佳方式是使用 Expression Web,它是微软提供的免费工具,为了更方便,我通常会在 help 目录中放置一个“default.css”文件,此外,Expression Web 还会添加一个“web.config" 文件。您的程序在运行时不需要这两个文件,因此如果您将它们放入项目中,可以将它们的生成操作设置为“none”(在 VS2013 维护的每个文件的属性中)。其他文件(HTML、PNG 等)的生成操作应默认为“content”,这将把它们放入 VS 为您的程序生成的应用程序包中。
您可以为帮助文件命名,并将其作为“CommandParameter
” string
值明确提供名称,但如果您不这样做,名称将默认为页面类名,并去掉末尾的“page
”一词。例如,如果您的页面类声明为“class MainPage ...
”,则相应的帮助文件将是“main.html”(或“Main.html”——文件名不区分大小写)。
工作原理
本节讨论代码如何工作以及为什么。您无需了解这些即可使用此代码在您自己的程序中提供帮助文本功能,但如果您想了解发生了什么,请继续阅读。本讨论侧重于该代码的 Windows Phone 版本,但“大型 Windows”版本是类似的,或者在许多情况下是相同的。
基本组成部分
起点是显示帮助的页面,因为 HTML 是底层格式,所以带有 WebView
控件的页面是一个显而易见的的选择,并且有一种简单的方法可以将 WebView
指向包内的内容(ms-appx://
URI 前缀)。拥有一个可控的页面标题很有用,所以您至少需要几个字段:一个 TextBlock
(称为 pageTitle
)和一个 WebView
(称为 webView
)。为了读取 HTML 页面标题以便在 TextBlock
中显示,在 WebView
上定义了一个 NavigationCompleted
事件,它看起来像这样:
private void webView_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
{
if (args.IsSuccess)
pageTitle.Text = webView.DocumentTitle;
else
pageTitle.Text = "Helpless, sorry";
}
硬件“后退”按钮不应直接退出程序,而应撤销最近一次的网页导航(如果存在),并在没有导航时返回帮助页面,所以我将一个方法附加到后退按钮事件 Windows.Phone.UI.Input.HardwareButtons.BackPressed
来执行此操作。该方法只需要这样做:
if (webView.CanGoBack)
webView.GoBack();
else
Frame.GoBack();
还有一些机制可以允许立即退出嵌套帮助(请参阅 CancelAppBarButton
)。
就这样,在 XAML 中定义一个带有 WebView
的页面,并在进入 XAML 定义的页面时导航到应用程序包中存储的命名网页。这效果很好,但显示的 HTML 具有预设的颜色和字体大小,这意味着它对设备配置(主题选择)不敏感,并且字体大小固定。
控制帮助文本格式
通过定义一个 CSS 文件来控制颜色和字体大小,并随着这些设置的变化动态生成它,可以控制帮助渲染,使其与设备的当前配置相匹配。不幸的是,使用本地文件的 WebView
(出于安全原因)不能使用 CSS 文件。
但是,通过使用 WebView.NavigateToLocalStreamUri
方法并传递一个实现 IUriToStreamResolver
的对象,所有 URI 引用都将在本地解析。在这种情况下,允许 CSS 引用,并且“default.css" 的内容可以根据当前设备设置动态生成。
我使用的对象是 StreamUriWinRTResolver
,它需要知道三种颜色(背景、前景和高亮)以及字体大小。字体大小可以由用户更改,颜色基于当前的设备设置(遗憾的是,在 Phone 和 Big Windows 上,资源名称不同),因此在手机上,您可能需要像这样设置它们:
StreamUriWinRTResolver myResolver = new StreamUriWinRTResolver();
Color foreground = (Color)App.Current.Resources["PhoneForegroundColor"];
Color background = (Color)App.Current.Resources["PhoneBackgroundColor"];
Color highlight = ((SolidColorBrush)App.Current.Resources["PhoneAccentBrush"]).Color;
StreamUriWinRTResolver.SetColors(foreground, background, highlight);
CSS 数据的实际创建在 StreamUriWinRTResolver.CreateStyle
中,主要包括将程序中的数据转换为 CSS 兼容格式。
HTML 文件、图像等可以直接从发布包中读取,无需复制到本地存储。在 StreamUriWinRTResolver.GetContent
中创建存储数据上的流,其中相关的代码是:
Uri localUri = new Uri("ms-appx:///help" + path);
StorageFile f = await StorageFile.GetFileFromApplicationUriAsync(localUri);
stream = await f.OpenAsync(FileAccessMode.Read);
完成此操作后,您就可以通过简单地添加以下内容来使用帮助页面:
Frame.Navigate(typeof(HelpPage), "HelpFileName");
在任何需要访问帮助文本的页面的代码隐藏中。
代码隐藏有害论
在 MVVM 中不鼓励使用代码隐藏,而且无论如何,在每个页面上重复这种代码,但存在细微的差异,既乏味又容易出错。因此,最好提供一个命令接口,任何页面都可以直接从 XAML 使用。这意味着声明一个继承自 ICommand 的对象并定义至少一个方法——Execute
。Execute
方法无法像代码隐藏那样访问页面变量,但可以使用 Window.Current.Content
访问,因此您可以使用以下方法获取正确的页面名称(以便推导出默认帮助文件名):
((Frame)Window.Current.Content).CurrentSourcePageType.ToString();
同样的技巧可以让你导航到帮助页面:
((Frame)Window.Current.Content).Navigate(typeof(HelpPage), whichHelp);
您可以使用 Microsoft 在“RelayCommand.cs" 中提供的标准 RelayCommand
实现来定义所需的命令对象,但显式实现更容易理解。该实现包含在 HelpCommandClass
中,并且该类的公共属性 Help
定义在 HelpViewModel
类中。
完成后,任何页面都可以通过 Help
属性使用该命令,方法是声明一个引用 HelpViewModel
的资源,然后在需要使用它的任何控件的 Command
属性中引用该资源(请参阅“在您自己的项目中使用的代码”)。
最后一个细节是,允许用户更改帮助文本大小是有益的,但每当这样做时,都需要重绘 WebView
控件以反映新的大小。大小更改请求由 HelpCommandZoom
对象处理,更改通知通过委托回调用 RefreshView
的页面进行处理。
可能的改进
一个显而易见的改进是响应动态主题更改——尽管这些更改很少见,但目前只有当用户进入帮助页面时才会生效(换句话说,如果用户已在查看帮助文本,则会忽略它们)。
历史
- 2015 年 1 月:首次发布
- 2015 年 8 月:Windows 10 版本