Silverlight Page Turning Made Simple [silverlight翻页实例 源码 原理 如何使用]

Wicked Code
Silverlight Page Turning Made Simple
Jeff Prosise

Code download available at: WickedCode2008_05.exe (1110 KB)
Browse the Code Online
Two years ago, I was cruising down a hallway in Redmond when a friend stopped me. "I have something you need to see," he said. He popped open his laptop and showed me a demo that changed my life. That demo was an early version of the Silverlight TM Page Turn Sample, which you can see now at silverlight.net/samples/1.0/Page-Turn/default.html.

I couldn't believe what I was seeing because the demo was running—gasp—in a browser! More surprisingly, it didn't require the Microsoft ® .NET Framework, it didn't require Internet Explorer ®, and although no one knew it at the time, one day it wouldn't even require Windows ®.

PageTurn is the quintessential Silverlight 1.0 demo. I use it all the time when I want to impress upon first-timers what Silverlight is all about. But look inside PageTurn and you'll find that building a page-turning application of your own is no small chore. PageTurn relies on transforms, clipping regions, dynamically created XAML objects, and more; the source code requires time and effort (and no small knowledge of Silverlight) to understand. It deftly demonstrates some of the most prolific capabilities of Silverlight, but it was not necessarily designed for general reuse.

That's why I built a general-purpose page-turning framework that makes it incredibly simple to incorporate page turns into your Silverlight 1.0 applications. With my framework to help out, an entire application can be built with just a few lines of JavaScript. It requires minimal knowledge of Silverlight itself, and since the entire framework consists of about 500 lines of JavaScript, you can dig in and understand how it works without having to wrap your head around thousands of lines of code. And, of course, you can modify it to your heart's content.

The PageTurnDemo Application
Before I introduce the framework, let's examine an application built around it. The PageTurnDemo application pictured in Figure 1 lets you page through the first several pages of the November 1988 issue of Microsoft Systems Journal, now known as MSDN ® Magazine. (How do you avoid copyright issues when reproducing pages from a magazine? Use pages from the magazine you work with, and use an article you wrote yourself, no less!) Each of the pages is a scanned image, but one of them—the final page—overlays the image with Extensible Application Markup Language (XAML) text. I included the text to reinforce a key point: pages fed to the framework aren't limited to just images; with few constraints, they can contain arbitrary XAML, including Images, TextBlocks, MediaElements, and more.
Figure 1  PageTurnDemo Shows a Partially Turned Page (Click the image for a larger view)

You can run the demo by downloading the source code and launching it from Visual Studio ® 2008, or you can view it at wintellect.com/silverlight/pageturndemo. Once the magazine cover appears (a progress bar apprises you of the progress of the download that retrieves all the images used in the app), use the left mouse button to drag the mouse over the cover from right to left to page forward. You can continue dragging left over right-hand pages to reveal more pages, or drag from left to right over left-hand pages to page backward.

How difficult was it to create this sample? Aside from scanning, cropping, sizing the images, and packaging them in a ZIP file, not difficult at all. The three key source code files (which you can find in the download accompanying this column) are the HTML file, the associated JavaScript file, and the XAML file. Take away the code that uses the Silverlight downloader object to download the images, and there's very little source code at all. In fact, only about 10 lines are devoted to the page-turning functionality.

Using the Page-Turn Framework
PageTurnDemo demonstrates the four basic steps you'll need to complete to use the page-turn framework. The first step is to include PageTurn.js—the script file containing the framework implementation—in the HTML file. Here's the relevant line in Default.html:
Copy Code
<script ... src="PageTurn.js"></script>
The second step is to instantiate the framework. Because the page-turn framework is encapsulated in a JavaScript class named PageTurnFramework, the following statement in Default.html.js instantiates the framework:
Copy Code
_ptf = new PageTurnFramework(_control,
    _control.content.findName('PageTurnCanvas'));
The first parameter passed to the PageTurnFramework constructor is a reference to the Silverlight control. The second parameter is a reference to the canvas containing your pages. In PageTurnDemo's XAML document, that canvas is named "PageTurnCanvas."
The third step is to register your pages with the framework. Each page is represented by a canvas, and PageTurnFramework exposes an addPage method that you can call to register pages. AddPage accepts two parameters:
Copy Code
_ptf.addPage(_control.content.findName('EvenPage0'),
    _control.content.findName('OddPage0'));
The first parameter is a reference to the canvas representing the left-hand page in a pair of facing pages; the second is a reference to the canvas representing the right-hand page. You can call addPage as many times as you need to register all your page pairs. And then, the final call to addPage must be followed by a call to initializeFramework:
Copy Code
_ptf.initializeFramework();
The initializeFramework method initializes the internal state of the framework. Among other things, it creates several XAML objects used to clip and rotate pages, and it also registers handlers for key events.
The fourth and final requirement for using the framework is to be sure to clean up properly. To avoid memory leaks, browser-based apps that programmatically register event handlers should unregister them as well. PageTurnFramework includes a dispose method that deregisters the event handlers registered by addPage and initializeFramework. In PageTurnDemo, an onunload attribute in the <body> element calls a local dispose function when the page unloads:
Copy Code
<body ... οnunlοad="dispose()">
This local dispose function, which lives in Default.html.js, calls the framework's dispose method:
Copy Code
if (_ptf != null)
    _ptf.dispose();
I used a DOM event to trigger calls to dispose because Silverlight doesn't fire unload events. You could just as easily use window.unload events if you'd prefer.
The PageTurnFramework class exposes six public methods that you can call to add page-turning functionality to your apps (see Figure 2). PageTurnDemo uses three of them: addPage, initializeFramework, and dispose. The other methods can be used to add extra functionality. For example, if you wanted to include a navigation bar consisting of page thumbnails in your app (as the Silverlight PageTurn demo does), you could call PageTurnFrame work.goToPage when a thumbnail is clicked to navigate directly to the corresponding page.


MethodDescription
addPageRegisters a page pair with the framework.
DisposeReleases resources held by the framework for proper cleanup.
getCurrentPageIndexReturns the 0-based index of the page pair currently displayed.
getPageCountReturns the number of page pairs added with addPage.
goToPageDisplays the specified page pair.
initializeFrameworkInitializes the page-turn framework.

XAML Structure
The page-turn framework imposes a few basic requirements on the structure of XAML documents. Those requirements are:
  1. Represent each page in a page pair with a XAML canvas (a "page canvas").
  2. Include a "page-turn" canvas that is a container for all of the page canvases.
  3. Assign a width, height, and background color (even if it's Transparent) to the root canvas.
The reason for the third requirement is that at initialization, the framework registers a handler for MouseLeave events fired by the root canvas. These events are used to complete page turns if the cursor leaves the Silverlight control with a page partially turned.
As a matter of best practice, all Silverlight 1.0 apps that capture the mouse, as the page-turn framework does when a turn begins, should process MouseLeave events emanating from the root canvas and take that opportunity to release the mouse. And in order for an element to receive mouse events such as MouseEnter and MouseLeave, the element must be "hit testable" within Silverlight. This is done by ensuring that the element (in this case, a Canvas) has a size and that it has a "non null" background.
With these requirements in mind, Figure 3 shows the general structure of a XAML document used with the page-turn framework. The page-turn canvas is the one you pass to PageTurnFramework's class constructor. Page canvases are passed to PageTurnFramework.addPage. The page-turn canvas and the page canvases should generally be tagged with explicit widths and heights in order to ensure that the mouse events used by the framework fire properly no matter what kind of XAML content the canvases contain. The rest is just XAML.

Copy Code
<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>

The page-turn canvas can happily exist alongside any other rich content in your XAML document. And individual pages can consist of simple XAML images or complex XAML renderings. You should generally avoid using the Clip property of the page canvases because the framework itself uses that property. However, you could always declare a subcanvas inside a page canvas and use the subcanvas's Clip property to define clipping regions.
If you want the first left-hand page to be blank so that the first right-hand page looks like the cover of an unopened book, simply declare an opaque rectangle in the first left-hand page. Similarly, you can use an opaque rectangle for the final right-hand page to create the appearance of a closed book when the user turns to the last page. PageTurnDemo does both to perpetuate the illusion that you're flipping through the pages of a magazine.
When you scan pages from a book or magazine to use with the page-turn framework, as I did for PageTurnDemo, the scans often have shadows at the inside edges, which give the pages a more realistic appearance. If you don't use scanned images, a bit of XAML can simulate shadows. For the application pictured in Figure 4 (which depicts some of my radio-controlled airplanes and jets and is viewable at wintellect.com/silverlight/mymodels), I used the XAML rectangles in Figure 5 to create shadows around the vertical divide at the center of each page pair. Alpha values in the GradientStop colors cause the rectangles to fade from right to left on left-hand pages and from left to right on right-hand pages.

Copy Code
<!-- 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>

Figure 4  Page-Turn Sample with XAML Shadows on Page Interiors (Click the image for a larger view)
If any of this is unclear, you can start an application by building from PageTurnDemo. Simply replace my XAML for individual pages with XAML of your own. My page canvases are dimensioned with a width and height of 400 and 544, but you can change those dimensions to whatever you prefer.

Framework Internals
The page-turn framework is implemented in PageTurn.js, which you can see in the download for this issue. The PageTurnFramework class begins just a few lines from the top. JavaScript doesn't support classes, but this framework uses the same prototype pattern used by ASP.NET AJAX and many Silverlight 1.0 apps to simulate classes in JavaScript. It's just an illusion, but it's an effective one.
The class constructor defines all the instance variables—the equivalent of fields in C#—needed to store internal state. For example, the statement
Copy Code
   this._control = control;
declares a "field" named _control and initializes it with the Silverlight control reference passed to the constructor. In all, approximately 40 fields are declared and used to hold everything from the percent-complete figure for an in-progress page turn (_percent) to tokens representing registered event handlers (which dispose uses to de-register the handlers). Fields also store references to several XAML objects that are created dynamically in initializeFramework and to XAML objects registered with calls to addPage.
PageTurnFramework.prototype contains all the PageTurnFramework methods. The methods fall into three broad categories: public methods such as addPage and initializeFramework; event handlers, which act in response to the mouse events and Storyboard.Completed events that the framework uses internally; and private methods, which are used internally by the framework but aren't intended to be called from the outside. There's plenty of interesting code, but due to space constraints, you'll have to download PageTurn.js to see them.
One of the interesting aspects of the framework architecture is how it completes page turns if the mouse button is released (or the cursor leaves the control) while a page turn is ongoing. At initialization time, the framework uses createFromXaml to create a Storyboard object to use as a timer. Then it adds the Storyboard to the page-turn canvas (this is one of the reasons you need to pass a reference to the page-turn canvas to the class constructor) and registers a handler for Storyboard.Completed events.
To finish an incomplete page turn, the framework calls Storyboard.begin to start the timer. On each timer tick, it advances the page another increment (using the step size stored in the _step field) and calls Storyboard.begin again if the page turn still isn't complete. You can see this in action by turning a page halfway and then releasing the mouse button. Depending on how far the page was turned when you released it, it will move back to the fully closed or fully opened position.
The Storyboard's XAML definition is stored in the variable named _sb in the initializeFramework method. You may wonder why that definition includes an x:Name attribute whose value is a GUID. In Silverlight 1.0, Storyboards created with createFromXaml must be named or createFromXaml will fail. I had to give the Storyboard a name, but I wanted to make sure the name didn't conflict with other Storyboards used in the application. Therefore, I named it after a GUID.
Another interesting part of the architecture is how the framework uses mouse events. For canvases representing pages, addPage registers handlers for MouseLeftButtonDown, MouseMove, and MouseLeftButtonUp events. A left turn begins when a right-hand page is clicked and progresses as the mouse moves to the left with the left button held down. Similarly, a right turn begins when a left-hand canvas is clicked and progresses as the mouse moves to the right. The key method used by the event handlers is _turnTo, which advances a partially turned page to the position denoted by the percent-complete parameter.
A final aspect of the framework that you may care to examine more closely is how it uses transforms and clipping regions to depict page turns. initializeFramework creates two PathGeometry objects: one to serve as a clipping region for right-hand pages and another for left-hand pages. It also creates a TransformGroup object containing a RotateTransform and a TranslateTransform.
Figure 6 illustrates how the clipping regions and transforms are used to depict a partially turned page. The red triangle is the clipping region used on the right-hand page that's on top. As the mouse moves to the left, the clipping region grows smaller so that less of the top page is visible and more of the page underneath is revealed. The blue triangle represents the clipping region used on the left-hand page that is revealed as the turn progresses, and the yellow rectangle represents the portion of that page that is clipped out.
Figure 6  Clipping Regions and Transforms Used to Depict Page Turns (Click the image for a larger view)
The RotateTransform and TranslateTransform are combined in order to position and orient the page. (Yes, there is a lot of trigonometry involved!) As the mouse moves to the left, imagine the yellow rectangle sliding to the left and rotating into an upright position. Couple that with a mental image of the blue triangle increasing in size while the red one grows progressively narrower and you'll have a pretty good idea of how page turns work.

Conclusion
Before the ink was dry on this column, I thought of other useful additions to the PageTurnFramework API. For example, it might be useful if the framework fired an event every time a page turns so you could write handlers that update other content on the page. And it could be useful to expose properties to control key page-turn parameters, such as the width of the shadow that follows turning pages and the step size used to animate incomplete turns.
Feel free to use this framework in your own projects and to modify it. Let me know your feedback, and if you think of useful additions to the feature set and API, let me know about that, too. I'll fold in your suggestions with my own and make sure that version 2.0 of the page-turn framework is even better than version 1.0.

Send your questions and comments for Jeff to wicked@microsoft.com.


Jeff Prosise is a contributing editor to MSDN Magazine and the author of several books, including Programming Microsoft .NET. He's also cofounder of Wintellect ( www.wintellect.com), a software consulting and education firm that specializes in the .NET Framework. Have a comment about this column? Contact Jeff at wicked@microsoft.com.

转自:http://msdn.microsoft.com/en-us/magazine/cc507644.aspx
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
社会的进步导致人们对于学习的追求永不止境,那么追求农业信息化的方式也从单一的田地教程变成了多样化的学习方式。多样化的学习方式不仅仅是需要人们智慧的依靠,还需要能够通过软件的加持进行信息化的价值体现。软件和系统的产生,从表面上来看是方便了某一行业和某一行业的用户,其实是从本质上来说是提高了社会的进步。就拿我们常见的出行而言,滴滴出行看似是为了打车和出行的人方便,但其实通过另外一种程度上来说,可以通过软件应用的调度和发展来为社会、城市出行的发展做出巨大的贡献。我们国家从最早的中国制造业演变到现在的“智造”,就是因为有软件信息系统的价值,能够将一些智慧的因素加入到制造的过程当中,而这一点就是软件系统来改变生产和现实的需求。在计算机时代日益发展的今天,计算机网络正快速融入这个社会的每一个领域。农业的发展是社会当中一种必有可少的方式。果树在种植和培养是直接影响果农及果商的发展,但在果树的资源管理方面还是有着很大的不同,所以信息多样化的果树管理方式很重要。在传统的果树资源管理上还有着很大的约束,为此开发和设计JSP杏种质资源管理系统,该系统内容丰富多彩,用户可以在线进行果杏树的资源查询等。本文还是使用JSP的方式来进行管理的,但在系统建设过程当中也考虑了许许多多信息安全的保护。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值