work hard work smart

专注于Java后端开发。 不断总结,举一反三。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

先前的学习中,我们已经了解了 Metro式的 JavaScript 应用程序大致如何,以及通过使用 Microsoft Visual Studio 11 Express for Windows Developer Preview 搭建你的应用程序。接下来要探讨的是,把  Windows 平台的所提供的功能,呈现在您眼前,为扩展您的程序而准备!

In the previous topic, you learned about Metro style app usingJavaScript, and used Microsoft Visual Studio 11 Express for WindowsDeveloper Preview to create a framework for your first app. Now it'stime to extend your application using functionality exposed by theWindows platform.

前言

该文是《运用 JavaScript 构建你的第一个 Metro式应用程序(on Windows8)的第二篇,如果你尚未阅读第一篇,请立刻翻阅。如果读者已经熟悉 HTML5、CSS3 和 JavaScript最好,不熟悉也没关系,不影响理解主体意思。
This topic is the second in a series describing how to build yourfirst Windows Metro style app using JavaScriptMetro style app usingJavaScript. If you haven't already done so, read Building your firstWindows Metro style app before you read this topic. Familiarity withHTML5, Cascading Style Sheets, Level 3 (CSS3), and JavaScript ishelpful, but not essential, to understanding this topic.

加入依赖条件Capabilities

当用户从 Windows Store 处安装程序的时候,Windows 会对应用程序的元数据作分析,认定那些依赖条件(Capabilities)。例如,要求访问互联网、访问文档、图片、播放音频视频的时候,又或者访问如摄像头、麦克风等的硬件时候,必须先告知用户,我这个程序需要些以上何种何种的条件才能运行。要用户首肯才能使用这些资源或设备。反之,如果没有列出的那些资源或设备,则一定不是依赖的条件,而且一定不能够运行的(译注:意思即是未列出的就不运行)。程序所需要的依赖条件列明在程序清单(manifest)中,具体说,就是你可以双击“Visual  Studio Solution Explorer ”里面的 package.appxmanifest 文件,点击 “Capabilities” 标签页就可以选择你需要的依赖条件。默认下,你的项目都是设定有最常见的依赖条件,具备充当 Internet 客户端的条件。
When a user installs an app from the Windows Store, Windows use theapp's metadata to figure out what capabilities it needs to run. Forexample, accessing data from the internet, accessing documents,pictures, music or videos or getting access to hardware like themicrophone or webcam. At install time, the user sees whichcapabilities an app asks for, so the app is installed only if theuser agrees for it to access those resources. If the app hasn'tlisted itself as needing access to a certain kind of resource, itisn't allowed access at run time. An app lists the capabilities itneeds in the manifest, so you can double-click on thepackage.appxmanifest file in the Visual Studio Solution Explorer,click the Capabilities tab and select the ones you need. By default,your project has the most common capability enabled, the ability toact as a client for internet service.

Windows Runtime运行时

目前为止,程序运行起来,仍属于浏览器技术的一部分内容,至今还未真正接触到 Windows 平台的运行时,特定称谓 “WindowsRuntime”,简称 “WinRT”。而 WinJS,就是允许在 JavaScript Metro 式的应用程序中,借此对象可以访问到 Windows Runtime 类库里面许许多多的功能。通过这个命名空间你还可以通过 Metr app 文档区翻阅更详尽功能。比如现在我们所使用的 Windows.Web.Syndication,用它直接获取已解析的 RSSReed,好处是不用在处理 RSS 或 ATOM 的之间上考虑怎么用 Xpath 的问题而浪费时间。

So far, we haven't really used the Windows platform on which ourapp is running, instead we used the web technologies that are alsopart of the browser. But in addition to WinJS, a Metro style appusing JavaScript can access a large amount of functionality in theWindows Runtime, broken up into namespaces that you can explore inthe Metro style app reference section. The namespace we want isWindows.Web.Syndication, because it has the SyndicationClient classthat we can use to retrieve a fully-parsed RSS feed. This saves usthe trouble of XPath or worrying about the difference between RSS andATOM.

无须太多语句或者项目引用,Metro App 下的 WinRT 元素就直接可用,这样使用 SyndicationClient 非常地简单。

The elements of the WinRT are available to Metro style apps usingJavaScript without any additional script includes or projectreferences, so using the SyndicationClient is simple:

[javascript] view plaincopy
  1. WinJS.Application.onmainwindowactivated = function (e) {  
  2.     if (e.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {  
  3.         // 开始下载  
  4.         downloadStatus.innerText = "downloading posts...";  
  5.   
  6.         //  用WinRT 下载 RSS  
  7.         var syn = new Windows.Web.Syndication.SyndicationClient();  
  8.         var url = new Windows.Foundation.Uri("http://blogs.msdn.com/b/oldnewthing/rss.aspx");  
  9.         syn.retrieveFeedAsync(url).then(processPosts, downloadError);  
  10.     }  
  11. }  

创建 SyndicationClient 对象之后接着创建一个 URL对象,并将其送入 RetrieveFeedAsync 进行下载。就像其他数量庞大的 Windows Runtime 函数那样,RetrieveFeedAsync 是异步的,自然也会产生“允诺Promise”。这样处理 RSS 的话更简单一些了:

After creating the SyndicationClient object, we create a URLobject and feed it to the RetrieveFeedAsync method to start thedownload. Like a large number of Windows Runtime functions,RetrieveFeedAsync is asynchronous and therefore produces a promise.Processing the RSS becomes a little bit easier now:

 

[javascript] view plaincopy
  1. function processPosts(feed) {  
  2.   // 改变指示器的状态  
  3.   downloadStatus.innerText = "";  
  4.   
  5.   // 循环条目  
  6.   for (var i = 0, len = feed.items.length; i < len; i++) {  
  7.     var item = feed.items[i];  
  8.     // 向 #posts div 添加数据  
  9.     var parent = document.createElement("div");  
  10.     appendDiv(parent, item.title.text, "postTitle");  
  11.     appendDiv(parent, item.publishedDate, "postDate");  
  12.     appendDiv(parent, item.summary.text, "postContent");  
  13.     posts.appendChild(parent);  
  14.   }  
  15. }  
  16.   
  17. function appendDiv(parent, html, className) {  
  18.     var div = document.createElement("div");  
  19.     div.innerHTML = html;  
  20.     div.className = className;  
  21.     parent.appendChild(div);  
  22. }  

回想一下,调用 xhr 的情况是提供 XML 然而并没有为我们解析XML 为对象,不同于 xhr 现在 SyndicationClient却包办搞定了,省却了循环的代码。结果虽然是一样,但其实后者会好一点。接下来介绍的数据绑定会干得更漂亮的说。

 

Unlike in the case of the xhr call, which provided XML but didn'tparse the RSS into objects for us, SyndicationClient does just that,simplifying our iteration code. The result looks the same, but thecode is a little nicer. And we can do even better by using templatesand data binding.

使用模板和数据绑定 UsingTemplates and Binding

看看 processPosts 函数与 appendDiv 函数结合的地方,你会发现那些静态的内容都被变量替换掉了。这种模式就是我们常说的模板(Template)。WinJS 考虑到这一功能,其语法如下所示:

If you look at the combination of the processPosts and appendDivfunction, you notice that we're mixing static content with variablesubstitution in code. This pattern of UI creation is known as atemplate. WinJS come with a similar feature and the syntax looks likethis:

 

  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4.     <meta charset="utf-8" />  
  5.     <title>RssReader</title>  
  6.     <!-- WinJS references -->  
  7.     <link rel="stylesheet" href="/winjs/css/ui-light.css" />  
  8.     <script src="/winjs/js/base.js"></script>  
  9.     <script src="/winjs/js/ui.js"></script>  
  10.     <script src="/winjs/js/binding.js"></script>  
  11.     <script src="/winjs/js/controls.js"></script>  
  12.     <script src="/winjs/js/animations.js"></script>  
  13.     <script src="/winjs/js/uicollections.js"></script>  
  14.     <script src="/winjs/js/wwaapp.js"></script>  
  15.     <!-- RssReader references -->  
  16.     <link rel="stylesheet" href="/css/default.css" />  
  17.     <script src="/js/default.js"></script>  
  18. </head>  
  19. <body>  
  20.     <h1>The Old New Thing</h1>  
  21.     <div id="downloadStatus"></div>  
  22.     <div id="posts"></div>  
  23.     <div id="template" data-win-control="WinJS.Binding.Template">  
  24.         <div data-win-bind="innerText: title" class="postTitle"></div>  
  25.         <div data-win-bind="innerText: date" class="postDate"></div>  
  26.         <div data-win-bind="innerHTML: content" class="postContent"></div>  
  27.     </div>  
  28. </body>  
  29. </html>  

欲使用 WinJS 模板,我们必须引用一组 WinJS 的 CSS 和JavaScript 文件。WinJS 围绕 data-win-control 属性提供了若干样式和行为,等等。对于HTML 元素,HTML5 规范了一组 data-*的属性以便去特定某些程序自定义的数据(译注,HTML5 data-*可参考http://dev.w3.org/html5/spec/Overview.html#embedding-custom-non-visible-data-with-the-data-attributes)。在该例子中,我们声明一个 WinJS.Binding.Template 的 JavaScript 构造器(constructor),该构造器获取了 div 以便为 div 登记相关的事件。顺便说一下,这也是在 WinJS 中声明一个控件的基础步骤。要触发 data-win-control 属性及其事件的执行,必须通过呼叫 WinJS.UI.processAll()。应该在哪个地方调用这个方法呢?事件 onmainwindowactivated 那里便最适合不过了。

 

To use WinJS templates, we must reference the bulk of the WinJSCSS and JavaScript files. These files provide the styles and behaviorfor the data-win-control attribute, among other things. HTML5 definesthe set of data-* attributes that we use for anything app-specific wewant to associate with an HTML element. In this case, we declare aJavaScript constructor function name WinJS.Binding.Template. Thisconstructor gets the associated div so that it can attach itself andprovide the necessary behavior. This is the foundation fordeclarative controls in WinJS. To cause the data-win-controlattributes to be executed, you must call WinJS.UI.processAll, whichis an excellent addition to your onmainwindowactivated event handler:

[javascript] view plaincopy
  1. WinJS.Application.onmainwindowactivated = function (e) {  
  2.     if (e.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {  
  3.         // 下载  
  4.         downloadStatus.innerText = "downloading posts...";  
  5.   
  6.         // use the WinRT to download the RSS  
  7.         var syn = new Windows.Web.Syndication.SyndicationClient();  
  8.         var url = new Windows.Foundation.Uri("http://blogs.msdn.com/b/oldnewthing/rss.aspx");  
  9.         syn.retrieveFeedAsync(url).then(processPosts, downloadError);  
  10.   
  11.         // 执行已声明控件  
  12.         WinJS.UI.processAll();  
  13.     }  
  14. }  

调用 processAll 则就是执行 WinJS.Binding.Template 的构造器函数,构造器内部具体的过程便是查找拥有属性 data-win-bind 的那些 div,代码如下:

The call to processAll causes the WinJS.Binding.Templateconstructor to be called, which in turn parses the contents of thediv looking for data-win-bind attributes in the following format:

 

  1. <div data-win-bind=" elemAttrib1: dataProp1; elemAttrib2:dataProp2;..."></div>  

如何构成数据绑定?请注意分号前面的部分为元素属性值(attribute)与分号后面的部分为 JavaScript 属性名称(property)。不管何种对象,模板只要遇到与属性(property)相吻合的情形即会渲染出来。于是,两者形成了绑定的关系,数据转化为特定的内容。所以模板这里的地方,我们的  processPosts 函数改为:

 

The attribute establishes a data binding relationship between theelement attribute name before the colon (innerText or innerHTML inour sample) and the JavaScript property name after the colon. Thisproperty name is applied to whatever object is handed to the templatewhen it is asked to render, using the data to expand variablecontent. With this template in place, our processPosts functionchanges to:

[javascript] view plaincopy
  1. function processPosts(feed) {  
  2.   // 改变指示器的状态  
  3.   downloadStatus.innerText = "";  
  4.   
  5.   // 建立控件与模板的联系  
  6.   var template = WinJS.UI.getControl(document.getElementById("template"));  
  7.   
  8.   // 循环条目  
  9.   for (var i = 0, len = feed.items.length; i < len; i++) {  
  10.     var item = feed.items[i];  
  11.     var post = {  
  12.       title: item.title.text,  
  13.       date: item.publishedDate,  
  14.       content: item.summary.text,  
  15.     };  
  16.   
  17.     // 渲染每一项的模板实例  
  18.     template.render(post).then(function (element) {  
  19.       // 向 #posts div添加数据  
  20.       posts.appendChild(element);  
  21.     });  
  22.   }  
  23. }  

函数 WinJS.UI.getControl 的参数是承托控件的元素,这里就是模板的div,getControl() 返回控件对象,实质是 JavaScript 对象,带有控件的状态和行为的对象。这里的行为就是每一项进行渲染时所消化的模板实例方法,就像刚才提到的 createElement 代码那样。模板的渲染函数是一个异步哈书,所以也需要 promise 一个函数,这里是添加帖子数据的过程。运行我们的更新程序所产生的输出与前面的过程一致,但因为模板的缩进就会看起来更清晰。在处理样式的处理时候,在HTML中编辑好模板会比 createElement 里面写简单很多。

 

The call to WinJS.UI.getControl function takes the elementassociated with the control, our template div in this case, andreturns the JavaScript object associated with it that is acting as acontrol. This control brings with it state and behavior. The behaviorwe want is the render function which stamps out an instance of thetemplate for each item we pass in, just like our createElement codewas doing before. This happens asynchronously in the case of thetemplate's render function, so we use a promise to append the newlycreated element to our set of posts. Running our updated programproduces output that looks exactly the same as before, but ourintent, with templates, is getting clearer. It's easier to edit thetemplate in the HTML than it is to write more and more complicatedcreateElement code as our styling and functionality grow.

不得不说,现在程序的样子离我们想像中的视窗应用程序差很远,列表内容太多了。实际上,多数 RSS Reader 只是会列出列表然后让你选择。我们也应该这样做,使用自带的ListView 控件做就好。

But our big list of all the content isn't as friendly as we want aWindows app to be. In fact, most RSS reader apps show a list of itemsand then let you select one to read. We can do that, too, using thebuilt-in ListView control.

添加控件 Adding Controls

除了模板控件和固有的控件外,尚有 WinJS内建的若干控件可用,例如 ListView 便是一个不错的控件,能够把数据渲染成为 Grid(表格)或 List (列表)。本例中是 Grid 布局。Grid 布局可以有效地分组、组织相关的数据。Grid 里的每一项又是模板来的,这正是利用了模板的原理。不妨想像一下,首页出现时候我们不打算显示全部内容,只要求显示标题,将多个标题形成一列表,帖子的列表。通过点击便可以看到全文内容。为此,default.html 添加了 ListView 控件,形如:

In addition to the Template control and the HTML intrinsiccontrols, WinJS has several other built-in controls. For example, theListView control is a useful control that knows how to arrange datainto a grid or a list and, in the case of the grid layout, canorganize data into groups of related-data. Each item in the grid isan expansion of a template, as we've already seen. For example,imagine that we want to create a list of posts, but instead ofproviding the full content on the first page, want to click on anentry to see the content. Adding a ListView to default.html lookslike this:

  1. <body>  
  2.     <h1>The Old New Thing</h1>  
  3.     <div id="downloadStatus"></div>  
  4.     <div id="template" data-win-control="WinJS.Binding.Template">  
  5.         <div data-win-bind="innerText: title" class="postTitle"></div>  
  6.         <div data-win-bind="innerText: date" class="postDate"></div>  
  7.     </div>  
  8.     <div id="posts" data-win-control="WinJS.UI.ListView"  
  9.          data-win-options="{itemRenderer: template, layout: {type: WinJS.UI.ListLayout}}"></div>  
  10. </body>  

通过声明data-win-control 即可创建 ListView 控件,具体参数可透过data-win-options 属性其“名称-值”的结构,传入到ListView构造器(这是可选的参数)。本例中,ListView的每一项依然使用模板来决定它显示的值(没有内容的修改版本)和布局的类型(默认为grid layout)。指定 ListView 数据的方式仍然是我们熟悉的:

Here we create the ListView control declaratively using"data-win-control". Also, use the data-win-optionsattribute as a set of name-value pairs that we pass as an optionalparameter to the ListView constructor. In this case, we declare thetemplate want to use for each item (a trimmed down version withoutthe content) and the kind of list layout we want (grid layout is thedefault). Populating the ListView with the data isn't very differentfrom what we've done so far:

 

[javascript] view plaincopy
  1. var postItems = [];  
  2.   
  3. function processPosts(feed) {  
  4.     downloadStatus.innerText = "";  
  5.   
  6.     for (var i = 0, len = feed.items.length; i < len; i++) {  
  7.         var item = feed.items[i];  
  8.         var post = {  
  9.             title: item.title.text,  
  10.             date: item.publishedDate,  
  11.             content: item.summary.text,  
  12.         };  
  13.         postItems.push(post);  
  14.     }  
  15.   
  16.     // 填入ListView 控件的数据源  
  17.     posts.winControl.dataSource = postItems;  
  18. }  

这一步,我们没有创建模板实例,而是建立data列表然后交给 ListView,即可自己渲染模板。值得一提的是,ListView 是一个聪明的控件,它会自动渲染那些实际显示的控件,现在我们创建 ListView 并填充数据:

 

This time, instead of creating an instance of our template foreach item as we process it, we build up a list of data to hand to theListView, which itself renders the template for us. But the ListViewis much smarter about it, rendering only the template for items thatare actually visible. Now that we created our ListView and populatedit with data, we get:

 

下一步

RSS Reader 还是半成品,如果弄好了内容区,那么就完全搞定了。下一辑的《运用JavaScript创建 Metro 式程序》,将演示如何通过加入阅读模板来增强程序的外观,为你程序平添视窗应用程序的风格与特征。

We now have half of an RSS reader. We still need the other half –that part where you can actually read the content. The next topic inthis series, Enhancing the presentation of your app, demonstrates howto complete your first Metro style app using JavaScript by adding areading pane, and using a Microsoft Visual Studio template that givesyour app the personality of Windows.

 

原文地址