WEB开发网
开发学院图形图像Flash Silverlight 翻页化繁为简 阅读

Silverlight 翻页化繁为简

 2008-10-26 11:49:44 来源:WEB开发网   
核心提示:本文示例源代码或素材下载 目录 PageTurnDemo 应用程序使用翻页框架XAML 结构框架内部总结两年前,我在雷蒙德的一条走廊里遇到了一个朋友,Silverlight 翻页化繁为简,他说:“我有些东西你需要看看,”他打开他的便捷式计算机给我做了一个演示,他还是 Wintellect

本文示例源代码或素材下载

目录

PageTurnDemo 应用程序

使用翻页框架

XAML 结构

框架内部

总结

两年前,我在雷蒙德的一条走廊里遇到了一个朋友。他说:“我有些东西你需要看看。”他打开他的便捷式计算机给我做了一个演示,这一演示给我带来了很大的震动。他演示是 SilverlightTM 早期版本的翻页示例(现在可通过 silverlight.net/samples/1.0/Page-Turn/default.html 查看)。

我无法相信自己的眼睛,因为该演示是在浏览器中运行的!更令人吃惊的是,它不需要 Microsoft® .NET Framework,不需要 Internet Explorer®,并且相信总有一天它会连也 Windows® 也不再需要了。

PageTurn 是最典型的 Silverlight 1.0 演示。如果希望初次接触 Silverlight 的人留下深刻印象,我总是会用到它。但是通过深入了解 PageTurn,您会发现构建自己的翻页应用程序并非易事。PageTurn 依赖于转换、剪辑区域、动态创建的 XAML 对象等功能,并且需要大量时间和精力(以及精通 Silverlight)才能理解源代码。它灵巧地展示了 Silverlight 的一些最丰富的功能,但它没有考虑到反复使用这一因素。

所以我构建了一个常用翻页框架,它使将翻页功能集成到 Silverlight 1.0 应用程序中变得极为简单。通过借助此框架,仅使用几行 JavaScript 就可构建整个应用程序。它不需要精通 Silverlight,并且由于整个框架包括约 500 行 JavaScript,,因此可深入了解其工作原理,而不必埋头苦读成千上万行代码。当然,您还可根据需要进行修改。

PageTurnDemo 应用程序

在介绍此框架之前,让我们先来研究一个围绕它构建的应用程序。通过使用图 1 中所示的 PageTurnDemo 应用程序,可翻阅 Microsoft Systems Journal(现在称为《MSDN® 杂志》)1988 年 11 月刊的前几页。(从杂志复制页面时如何避免出现版权问题?使用对应杂志的页面,并且使用自己撰写的文章,这是起码条件!)每个页面都是一个扫描图像,但其中一个即最后一页使用可扩展应用程序标记语言 (XAML) 文本来覆盖图像。我还要强调:应用于框架的页面不仅限于图像;对它的限制很少,可包含任意 XAML(包括 Image、TextBlock、MediaElement 等等)。

Silverlight 翻页化繁为简

图 1PageTurnDemo 显示部分翻开的页面

可通过下载源代码并从 Visual Studio® 2008 将其启动来运行此演示,或者可在 wintellect.com/silverlight/pageturndemo 中查看此演示。出现杂志封面后(一个进度条会通知您下载进度,此下载会检索应用程序中使用的所有图像),按下鼠标左键在封面上从右到左拖动来向前翻页。继续向左拖动右边页面会显示更多页面,从左向右拖动左边页面会向后翻页。

创建此示例的难度如何?除扫描、剪切、确定图像大小以及将它们打包到 ZIP 文件中外,根本就不难。三个关键源代码文件(可在本专栏随附的下载中找到)为 HTML 文件、相关 JavaScript 文件以及 XAML 文件。除去使用 Silverlight 下载器对象来下载图像的代码,基本就没什么源代码了。实际上,仅有大约 10 行是翻页功能专用的。

使用翻页框架

PageTurnDemo 展示了使用翻页框架所需完成的四个基本步骤。第一步是将 PageTurn.js(包含框架实现的脚本文件)加入 HTML 文件中。以下是 Default.html 中的相关代码行:

<script ... src="PageTurn.js"></script>

第二步是实例化框架。由于翻页框架封装在名为 PageTurnFramework 的 JavaScript 类中,Default.html.js 中的以下语句将实例化框架:

_ptf = new PageTurnFramework(_control,
  _control.content.findName('PageTurnCanvas'));

传递给 PageTurnFramework 构造函数的第一个参数是对 Silverlight 控件的引用。第二个参数是对包含页面的画布的引用。在 PageTurnDemo 的 XAML 文档中,该画布称为“PageTurnCanvas”。

第三步是向框架注册页面。每个页面都由一块画布表示,并且 PageTurnFramework 公开了一个 addPage 方法,可调用此方法来注册页面。AddPage 接受两个参数:

_ptf.addPage(_control.content.findName('EvenPage0'),
  _control.content.findName('OddPage0'));

第一个参数是对代表对页中左边页面的画布的引用;而第二个参数则是对代表右边页面的画布的引用。可任意多次调用 addPage 以注册所有的页面对。最后一次调用 addPage 之后必须接着调用 initializeFramework:

_ptf.initializeFramework();

initializeFramework 方法初始化框架的内部状态。此外,它创建了用于剪辑和旋转页面的多个 XAML 对象,并且它还注册了关键事件的处理程序。

使用框架的第四个也是最后一个需求为确保正确执行清理。为避免内存泄漏,通过编程方式注册事件处理程序且基于浏览器的应用程序还应取消注册这些处理程序。PageTurnFramework 包括一个 dispose 方法来注销 addPage 和 initializeFramework 所注册的事件处理程序。在 PageTurnDemo 中,当卸载页面时,<body> 元素中的 onunload 属性会调用本地 dispose 函数:

<body ... onunload="dispose()">

Default.html.js 中的本地 dispose 函数将调用框架的 dispose 方法:

if (_ptf != null)
  _ptf.dispose();

我使用了一个 DOM 事件来触发对 dispose 的调用,因为 Silverlight 不会引发卸载事件。如果愿意,使用 window.unload 事件也一样轻松。

PageTurnFramework 类公开了六个公共方法,可调用它们来将翻页功能添加到应用程序中(请参阅图 2)。PageTurnDemo 使用了其中三个:addPage、initializeFramework 和 dispose。其他方法可用于添加更多功能。例如,如果希望在应用程序中包括由页面略缩图组成的一个导航栏(就像 Silverlight PageTurn 演示的一样),可在单击缩略图时调用 PageTurnFrame work.goToPage 来直接导航到对应页面。

Silverlight 翻页化繁为简Figure2翻页框架 API

方法说明
addPage向框架注册页面对。
Dispose释放框架所占用的资源以完成适当的清理。
getCurrentPageIndex返回当前显示页面对的基于 0 的索引。
getPageCount返回使用 addPage 添加的页面对数。
goToPage显示指定页面对。
initializeFramework初始化翻页框架。

XAML 结构

翻页框架对 XAML 文档结构强加了一些基本要求。这些要求包括:

使用 XAML 画布(页面画布)代表页面对中的每个页面。

包括一个“翻页”画布,作为所有页面画布的容器。

指定根画布的宽度、高度和背景颜色(即使它为 Transparent)。

产生第三个要求的原因是在初始化时,框架将注册根画布所引发的 MouseLeave 事件处理程序。当光标离开 Silverlight 控件而页面部分打开时,这些事件将用于完成翻页功能。

出于最佳实践方面的考虑,捕获鼠标的所有 Silverlight 1.0 应用程序(就像开始翻动时翻页框架的动作一样)应处理从根画布发出的 MouseLeave 事件并利用该机会释放鼠标。为使元素可接收鼠标事件(如 MouseEnter 和 MouseLeave),元素在 Silverlight 中必须为“可点击测试”。其实现方法为确保元素(在本例中为“画布”)具有尺寸且具有“非空”背景。

请记牢这些要求,图 3 显示了与翻页框架配用的 XAML 文档常规结构。翻页画布是传递给 PageTurnFramework 类构造函数的画布。页面画面传递给 PageTurnFramework.addPage。翻页画布和页面画布通常应标记有显式宽度和高度,从而确保可正确引发框架所使用的鼠标事件,无论画面包含的是何种 XAML 内容。剩下的全都是 XAML 了。

Silverlight 翻页化繁为简Figure3XAML 结构

<Canvas
  xmlns="http://schemas.microsoft.com/client/2007"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Background="color" Width="width" Height="height">
  <!-- Other XAML content goes here -->
  <!-- Page turn canvas -->
  <Canvas>
    <!-- Canvases representing first page pair -->
    <Canvas>
      <!-- Content for left-hand page goes here -->
    </Canvas>
    <Canvas>
      <!-- Content for right-hand page goes here -->
    </Canvas>
    <!-- Canvases representing additional page pairs go here -->
  </Canvas>
  <!-- Other XAML content goes here -->
</Canvas>

翻页画布可恰当地与 XAML 文档中的任意其他丰富内容共存。单个页面可包含简单的 XAML 图像或复杂的 XAML 呈现。通常应避免使用页面画布的 Clip 属性,因为框架本身会使用该属性。然而,可始终在页面画布内声明一个子画布,并使用子画布的 Clip 属性来定义剪辑区域。

如果希望第一个左边页面为空白以便第一个右边页面看起来像未打开书籍的封面,只需在第一个左边页面中声明一个不透明矩形即可。同样,可对最后一个右边页面使用一个不透明的矩形,从而在用户翻到最后一页时产生合上书籍的感觉。PageTurnDemo 实现了这两种效果,从而让您一直保持正在翻阅一本杂志的感觉。

在扫描书籍或杂志的页面以用于翻页框架时(就如我对 PageTurnDemo 所执行的处理),扫描的内部边框常常存在阴影,这使页面具有更加真实的外观。如果未使用扫描图像,也可使用几行 XAML 来模拟阴影。对于图 4(它描绘了一些无线电控制的飞机和喷气机,可在 wintellect.com/silverlight/mymodels 中进行查看)中所示的应用程序,我使用图 5 中的 XAML 矩形来在每个页面对中心的垂直分界线周围创建阴影。GradientStop 颜色的 Alpha 值使矩形在左边页面中从右到左变淡,在右边页面中从左到右变淡。

Silverlight 翻页化繁为简Figure5在页面内部创建阴影的 XAML

<!-- Shadow on left-hand page -->
<Rectangle Canvas.Left="380" Canvas.Top="0" Width="20" Height="600">
  <Rectangle.Fill>
    <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
      <GradientStop Color="#00000000" Offset="0.0" />
      <GradientStop Color="#40000000" Offset="1.0" />
    </LinearGradientBrush>
  </Rectangle.Fill>
</Rectangle>
<!-- Shadow on right-hand page -->
<Rectangle Canvas.Left="0" Canvas.Top="0" Width="20" Height="600">
  <Rectangle.Fill>
    <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
      <GradientStop Color="#40000000" Offset="0.0" />
      <GradientStop Color="#00000000" Offset="1.0" />
    </LinearGradientBrush>
  </Rectangle.Fill>
</Rectangle>

Silverlight 翻页化繁为简

图 4页面内部存在 XAML 阴影的翻页示例

如果存在不清楚的地方,可从 PageTurnDemo 开始构建应用程序。只需将我的各个页面的 XAML 替换成您页面的 XAML 即可。我的页面画布的宽度和高度为 400 和 544,但是您可以将其更改为所需的任意尺寸。

框架内部

翻页框架是在 PageTurn.js(包含在本期的下载中)中实现的。从开头下数几行就是 PageTurnFramework 类。JavaScript 不支持类,但此框架使用 ASP.NET AJAX 和许多 Silverlight 1.0 应用程序所用的相同原型模式来在 JavaScript 中模拟类。它虽然只是一种虚拟,但十分有效。

类构造函数定义存储内部状态所需的所有实例变量(等价于 C# 中的字段)。例如,语句

  this._control = control;

声明一个名为 _control 的“字段”,并且使用传递给构造函数的 Silverlight 控件引用来对它实例化。共声明和使用了约 40 个字段来保存所有内容,包括正在进行的页面翻动的完成百分比图 (_percent) 以及代表已注册事件处理程序的令牌(dispose 用于取消注册处理程序)。字段还存储对在 initializeFramework 中动态创建的多个 XAML 对象的引用,以及对 XAML 对象(通过对 addPage 的调用注册)的引用。

PageTurnFramework.prototype 包含所有 PageTurnFramework 方法。这些方法分为三个大类:公共方法(如 addPage 和 initializeFramework);事件处理程序,用于响应框架在内部使用的鼠标事件和 Storyboard.Completed 事件;以及私有方法,它们由框架在内部使用且不用于从外部调用。有许多非常有趣的代码,但由于空间限制,您必须得通过 PageTurn.js 来查看它们。

框架体系结构一个有趣的地方是正在翻页时如松开鼠标按钮(或者光标离开控件)它完成翻页的方式。在初始化时,框架使用 createFromXaml 来创建一个用作计时器的 Storyboard 对象。然后,它会将 Storyboard 添加到翻页画布(这是需要将对翻页画布的引用传递给类构造函数的原因之一)并且注册 Storyboard.Completed 事件的处理程序。

为结束未完成的翻页,框架调用 Storyboard.begin 来启动计时器。计时器每滴答一次,它会将页面前进一个增量(使用 _step 字段中存储的步长),如果页面翻动仍未完成,它会再次调用 Storyboard.begin。可通过将页面翻动一部分然后松开鼠标按钮来查看这一操作效果。根据松开按钮时页面的翻动距离,它会移回到完全闭合或完全打开位置。

Storyboard 的 XAML 定义存储在 initializeFramework 方法中名为 _sb 的变量内。您可能想知道为什么该定义包含一个值为 GUID 的 x:Name 属性。在 Silverlight 1.0 中,必须对使用 createFromXaml 创建的 Storyboard 进行命名,否则 createFromXaml 将失败。我已为 Storyboard 命名,但我希望确保此名称并未与应用程序中使用的其他 Storyboard 相冲突。因此,我按 GUID 来对其进行命名。

此体系结构另一有趣的地方是框架使用鼠标事件的方式。对于代表页面的画布,addPage 注册 MouseLeftButtonDown、MouseMove 和 MouseLeftButtonUp 事件的处理程序。当单击右边页面时开始左翻,并且在按下左键向左移动鼠标的同时继续翻页。同样,当单击左边画布时开始右翻,并且在鼠标向右移动时将继续翻页。事件处理程序使用的关键方法是 _turnTo,它会将部分打开的页面翻动到完成百分比参数所表示的位置。

您可能希望深入了解的最后一个方面是它如何使用转换和剪辑区域来描述翻页。initializeFramework 创建了以下两个 PathGeometry 对象:一个用作右边页面的剪辑区域,另一个用作左边页面的剪辑区域。它还创建一个包含 RotateTransform 和 TranslateTransform 的 TransformGroup 对象。

图 6 显示如何使用剪辑区域和转换来描述部分打开的页面。红色三角形是最上面的右边页面上使用的剪辑区域。当鼠标移到左边时,剪辑区域变小,从而使上方页面可见区变小,显示更多的下方页面。蓝色三角形代表进行翻动时显示的左边页面上使用的剪辑区域,而黄色矩形代表被剪切掉的页面区域。

Silverlight 翻页化繁为简

图 6用于描述翻页的剪辑区域和转换

RotateTransform 和 TranslateTransform 一起用于对页面进行定位和定向。(是的,还涉及到很多三角学!)当鼠标向左移动时,想象黄色矩形滑向左边且旋转到垂直位置。再结合在红色三角形逐渐变窄时蓝色三角形尺寸变大,您就会明白翻页的工作原理。

总结

在结束本专栏之前,我想到了 PageTurnFramework API 其他有用的添加项。例如,如果每次翻页时框架都引发一个事件将非常有用,这样可编写更新页面上其他内容的处理程序。并且,公开控制关键翻页参数的属性也非常有用(如翻页后的阴影宽度以及未完成翻动的动画步长)。

如有必要,可在您的项目中自由使用此框架并对其进行修改。如有反馈,敬请告知;并且如果想到功能集和 API 的有用添加内容,也请一并告知。我会综合利用您的建议和我的想法,从而确保翻页框架的版本 2.0 优于版本 1.0。

请将您想向 Jeff 询问的问题和提出的意见发送至wicked@microsoft.com.

Jeff Prosise 是对《MSDN 杂志》贡献很大的编辑以及多本书籍的作者,这些书籍中包括《Programming Microsoft .NET》。他还是 Wintellect (www.wintellect.com) 公司的共同创始人,该公司专门从事 .NET Framework 软件咨询和教育。想对本专栏发表评论吗?您可以通过 wicked@microsoft.com 与 Jeff 联系。

Tags:Silverlight

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接