Windows10-开发秘籍-二-

187 阅读1小时+

Windows10 开发秘籍(二)

原文:Windows 10 Development Recipes

协议:CC BY-NC-SA 4.0

五、数据绑定和导航

通常,当您查看任何应用时,它都是由用户界面(UI)和底层业务逻辑组成的。数据绑定是一个连接应用 UI 和业务逻辑的过程。当数据更改其值时,绑定到数据的元素会自动反映这些更改,并且当元素值更改时,底层数据也会更新并反映这些更改。

5.1 数据绑定到简单对象

问题

作为应用业务逻辑的一部分,您有一个带有数据属性的简单业务对象。您希望将业务对象的数据属性绑定到 UI 上的 HTML 元素。

解决办法

数据绑定由WinJS.Binding名称空间提供。它提供了processAll()方法,将对象的值绑定到任何 DOM 元素的值。DOM 元素必须使用data-win-bind属性,并提供需要绑定的属性名。

它是如何工作的

让我们看看如何在您的应用中执行简单的数据绑定。

Open Visual Studio 2015. Select File ➤ New Project ➤ JavaScript ➤ Windows ➤ Universal ➤ Blank App (Universal Windows) template (see Figure 5-1).

A978-1-4842-0719-2_5_Fig1_HTML.jpg

图 5-1。

New Project dialog Visual Studio creates the Universal Windows Apps blank project with all the necessary files added to the solution.   Open default.js, which is found under the js folder. Add the following lines of code inside the immediately invoked function, just after the 'use strict' directive: (function () {         "use strict";         //create a person object var person = {                 name: "John Doe",                 age: 36,                 designation: "Technical Evangelist",                 city: "Boston",         };         var app = WinJS.Application;         var activation = Windows.ApplicationModel.Activation;         app.onactivated = function (args) {                 if (args.detail.kind === activation.ActivationKind.launch) {                         if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {                         } else {                         }                         args.setPromise(WinJS.UI.processAll());                 }         };         app.oncheckpoint = function (args) {         };         app.start(); })(); Let’s bind the Person object to a div element in the HTML.   Open default.html, which is found in the root of the project. Replace the contents of <body> with the following: <div id="container">      <h3>Name:</h3>      <h2><span data-win-bind="innerText: name"></span></h2>      <h3>Age:</h3>      <h2><span data-win-bind="innerText: age"></span></h2>      <h3>Designation:</h3>      <h2><span data-win-bind="innerText: designation"></span></h2>     <h3>City:</h3>       <h2><span data-win-bind="innerText: city"></span> </h2> </div> You have a span element and you have a defined data-win-bind attribute for data binding. You are binding the innerText property of the span element with data property of the Person object you created in default.js.   Next, you need to modify the onactivated function and add the data binding call. Modify app.onactivated, as shown here: app.onactivated = function (args) {         if (args.detail.kind === activation.ActivationKind.launch) {             if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {                 // TODO: This application has been newly launched. Initialize                 // your application here.             } else {                 // TODO: This application has been reactivated from suspension.                 // Restore application state here.             }             var container = document.querySelector('#container');             var prmise = WinJS.UI.processAll().then(function () {                 WinJS.Binding.processAll(container, person)             })             args.setPromise(prmise);         }       };   Build and run the app by pressing F5 in Visual Studio. Figure 5-2 shows the output on a Windows Mobile screen.

A978-1-4842-0719-2_5_Fig2_HTML.jpg

图 5-2。

Snapshot of Windows Mobile output  

如您所见,Person 对象的值(在default.js中定义)被绑定到 HTML 元素。这是可能的,因为有了WinJS.Binding名称空间。如果您还记得,您在 onactivated 函数中调用了WinJS.Binding.processAll()方法。processAll()方法将对象的值绑定到具有data-win-bind属性的 DOM 元素的值。在演示代码中,您已经在输出姓名、年龄、头衔和城市信息的div元素上设置了data-win-bind属性。

5.2 DOM 元素的数据绑定样式属性

问题

HTML 元素可以使用 CSS 定义来设置样式。在运行时,样式属性需要绑定到底层业务对象数据属性的数据属性。

解决办法

除了提供对 DOM 元素属性的数据绑定,WinJS 数据绑定框架还提供了绑定 DOM 元素样式属性的选项。样式属性可以在data-win-bind属性中绑定到绑定对象的数据属性。

它是如何工作的

Open Visual Studio 2015. Select File ➤ New Project ➤ JavaScript ➤ Windows ➤ Universal ➤ Blank App (Universal Windows) template. This creates a Universal Windows app template (see Figure 5-3).

A978-1-4842-0719-2_5_Fig3_HTML.jpg

图 5-3。

New Project dialog   Open default.js in the js folder. Add the following code inside the immediately invoked function after the 'use strict' directive: (function () {     "use strict";     //create person object     var person = {         name: "John Doe",         age: 36,         designation: "Technical Evangelist",         city: "Boston",         favcolor: "orange"     };     //Other app set-up code })(); You have added a favcolor property to the Person object. Let’s bind the favcolor to a div element’s background-color style property.   Open default.html, which is found in the root of the project. Replace the <body> content with the following: <h1>Data Bind Attributes</h1>     <br />     <div id="container">         <h3>Name:</h3>         <h2><span data-win-bind="innerText: name"></span></h2>         <h3>Age:</h3>         <h2><span data-win-bind="innerText: age"></span></h2>         <h3>Designation:</h3>         <h2><span data-win-bind="innerText: designation"></span></h2>         <h3>City:</h3>         <h2><span data-win-bind="innerText: city"></span> </h2>         <h3>Fav Color:</h3>         <div data-win-bind="style.background: favcolor" >             <div class="favcolor" data-win-bind="innerText: favcolor"></div>         </div>     </div>   Go back to the default.js file again. Modify the onactivated method as follows: app.onactivated = function (args) {         if (args.detail.kind === activation.ActivationKind.launch) {             if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {                 // TODO: This application has been newly launched. Initialize                 // your application here.             } else {                 // TODO: This application has been reactivated from suspension.                 // Restore application state here.             }             var container = document.querySelector("#container");             var prmise = WinJS.UI.processAll().then(function () {                 WinJS.Binding.processAll(container, person)             })             args.setPromise(prmise);         }     }; You just call the WinJS Binding processAll() as usual on the container that needs to be data bound.   Next, build and run the app by pressing F5 in Visual Studio. The output on a Windows Mobile screen is shown in Figure 5-4.

A978-1-4842-0719-2_5_Fig4_HTML.jpg

图 5-4。

Snapshot of Windows Mobile output  

5.3 使用模板进行数据绑定

问题

简单数据绑定一次只能绑定到一个数据项。当您有一个数据项列表时,简单的数据绑定方法是不够的。

解决办法

使用简单数据绑定,您可以将一个数据项绑定到一个 DOM 元素,该元素显示数据属性的值。为了使用项目列表并允许用户更改他们想要查看的数据项,您需要一个模板。数据模板充当蓝图,在运行时,它绑定到所提供的数据项,并在指定的 DOM 元素上呈现标记。

它是如何工作的

Open Visual Studio 2015. Select File ➤ New Project ➤ JavaScript ➤ Windows Universal ➤ Blank App (Universal Windows) template. This creates a Universal Windows app template (see Figure 5-5).

A978-1-4842-0719-2_5_Fig5_HTML.jpg

图 5-5。

New Project dialog   Open default.js in js folder. Inside the immediately invoked function, let’s declare a Person object with a couple of properties. This time you will use WinJS.Binding.define to declare the Person object. Using define will make all the properties bindable. Here is the code snippet for a Person object: (function () {     "use strict";     var Person = WinJS.Binding.define({         name: "",         color: "",         birthday: "",         petname:"",         dessert:""     });     //other app code ... })();   Open default.html, found in root of the project. Replace the content of the <body> tag with the following code: <h1>Data Binding Template</h1> <div id="templateDiv" data-win-control="WinJS.Binding.Template">     <div class="templateItem" data-win-bind="style.background: color" style="color:white">         <ol>             <li><span>Name :</span><span data-win-bind="textContent: name"></span></li>             <li><span>Birthday:</span><span data-win-bind="textContent: birthday"></span></li>             <li><span>Pet's name: </span><span data-win-bind="textContent: petname"></span></li>             <li><span>Dessert: </span><span data-win-bind="textContent: dessert"></span></li>         </ol>     </div> </div> <div id="renderDiv"></div> You have defined a div with the id templateDiv and then added a data-win-control attribute with a WinJS.Binding.Template value. This defines the template that can be used for data binding. You then have a div with the id renderDiv. At runtime, you data bind the template with data and render the output to the renderDiv DOM element.   For the purpose of this recipe, let’s create three Person objects and add a drop-down list so that you can select the person whose details needs to be shown. Inside the body tag, add the following code right after the "renderDiv" element: <fieldset id="templateControlObject">     <legend>Pick a name:</legend>     <select id="templateControlObjectSelector">         <option value="0">Show John Doe</option>         <option value="1">Show Jane Dow</option>         <option value="2">Show Jake Doe</option>     </select> </fieldset>   Back in default.js file, right after the Person object definition, create an array of three Person objects, as follows: (function () {     "use strict";     var Person = WinJS.Binding.define({         name: "",         color: "",         birthday: "",         petname:"",         dessert:""     })     var people = [         new Person({ name: "John Doe", color: "red", birthday: "2/2/2002", petname: "Spot", dessert: "chocolate cake" }),         new Person({ name: "Jane Doe", color: "green", birthday: "3/3/2003", petname: "Xena", dessert: "cherry pie" }),         new Person({ name: "Jake Doe", color: "blue", birthday: "2/2/2002", petname: "Pablo", dessert: "ice cream" }),     ];     //Other app code ... })();   Next, add a listener to the change event of the drop-down list in the onactivated method in default.js. Here is the code snippet: app.onactivated = function (args) {     // Other activation code ...     var selector = document.querySelector("#templateControlObjectSelector");           selector.addEventListener("change", handleChange, false);     args.setPromise(WinJS.UI.processAll()); }   Let’s create the event handler for the drop-down list change event. Select the div, which contains the template and the div where you want to render the markup. Call render on the template control. Create a handleChange function in default.js with the following code: (function () {     //Other app code...     function handleChange(evt) {         var templateElement = document.querySelector("#templateDiv");         var renderElement = document.querySelector("#renderDiv");         renderElement.innerHTML = "";         var selected = evt.target.selectedIndex;         var templateControl = templateElement.winControl;         templateElement.winControl.render(people[selected], renderElement);     } })();   Build and run the app by pressing F5 in Visual Studio. When you select an item in the drop-down list, the appropriate data is shown in the div above it. Figure 5-6 shows the output on a Windows Mobile screen.

A978-1-4842-0719-2_5_Fig6_HTML.jpg

图 5-6。

Data binding template output on Windows Mobile screen  

5.4 数据绑定 WinJS 控件

问题

您有一个项目列表,您希望将该列表数据绑定到一个 WinJS 控件,如 ListView。

解决办法

使用 WinJS 数据绑定,您可以将任何 WinJS 控件绑定到数据源。为了显示每个单独的项目,您将提供一个数据模板。在运行时,列表获取绑定到控件的数据,并且根据定义的模板呈现每一项。

它是如何工作的

Open Visual Studio 2015. Select File ➤ New Project ➤ JavaScript ➤ Windows ➤ Universal ➤ Blank App (Universal Windows) template. This creates a Universal Windows app template (see Figure 5-7).

A978-1-4842-0719-2_5_Fig7_HTML.jpg

图 5-7。

New Project dialog   Let’s create a new JavaScript file that will provide data to the ListView. Right-click the js folder. Select Add ➤ New Item. In the New Item dialog, select the JavaScript file and name it data.js. Add the following code to the data.js file: (function () {     var flavors = [         { title: "Basic banana", text: "Low-fat frozen yogurt" },         { title: "Banana blast", text: "Ice cream" },         { title: "Brilliant banana", text: "Frozen custard" },         { title: "Orange surprise", text: "Sherbet" },         { title: "Original orange", text: "Sherbet" },         { title: "Vanilla", text: "Ice cream" },         { title: "Very vanilla", text: "Frozen custard" },         { title: "Marvelous mint", text: "Gelato" },         { title: "Succulent strawberry", text: "Sorbet" }     ];     var flavorList = new WinJS.Binding.List(flavors);     WinJS.Namespace.define("DataExample", {         flavorList: flavorList     }); })();   Add a reference to the data.js file in your default.html, which is found at the root of the project. <head>     <!-- Other file references ... -->     <!-- Your data file. -->     <script src="/js/data.js"></script> </head>   In default.html, you will now create a ListView and bind it to the data source you have created in the data.js file. Also define an item template for the list view items. Add the following code to default.html. <h1>ListView Data Binding</h1> <div id="flavorItemTemplate" data-win-control="WinJS.Binding.Template">     <div id="templateContainer">         <div id="itemContainer">             <h4 data-win-bind="innerText: title"></h4>             <h6 data-win-bind="innerText: text"></h6>         </div>     </div> </div> <div id="basicListView" data-win-control="WinJS.UI.ListView"      data-win-options="{itemDataSource : DataExample.flavorList.dataSource,                         itemTemplate:select('#flavorItemTemplate'),                         selectionMode: 'none',                         layout:{type:WinJS.UI.ListLayout}}"> </div> Create the list view by setting the data-win-control attribute with a WinJS.UI.ListView value. Use the data-win-options attribute to set control options like item data source, item template, and the layout of the list view.   Next, you need to add a little bit of style to the list view and the list view items. Open default.css in Windows and Windows Mobile projects, and add the following style sheet definition: #basicListView{     height: 100%;     margin-top: 10px;     margin-right: 20px; } #templateContainer{     display: -ms-grid;     -ms-grid-columns: 1fr;     min-height: 150px; } #itemContainer{     background-color:lightgray;     width:100%;     padding:10px; }   Build and run the app by pressing F5 in Visual Studio. Figure 5-8 shows the output on a Windows Mobile screen.

A978-1-4842-0719-2_5_Fig8_HTML.jpg

图 5-8。

ListView Data Binding Output on a Windows Mobile Screen  

5.5 应用中的导航结构

问题

你想把你的应用分成多个页面/屏幕来处理特定的功能。您想知道应该遵循什么样的导航结构来提供在页面/屏幕之间移动的最佳体验。

解决办法

现实世界中的应用很少只有一个页面/屏幕,而是由许多页面/屏幕组成。每个页面/屏幕负责一个特定的功能。最好是将你的应用分成不同的功能,并分配一个专门的页面/屏幕来处理这些功能。UWP 应用中的导航基于导航结构、元素和系统级功能。通过在应用中提供正确的导航,您可以实现从一个页面到另一个页面或从这个内容到那个内容的直观用户体验。

应用中的每个页面都将包含或满足一组特定的内容或功能。例如,联系人管理应用会有一个列出联系人的屏幕、一个创建联系人的屏幕、一个更新联系人的屏幕或一个删除联系人的屏幕。应用的导航结构由您如何组织应用的不同屏幕来定义。图 5-9 展示了 UWP 应用中可以采用的不同导航结构。

A978-1-4842-0719-2_5_Fig9_HTML.jpg

图 5-9。

Navigation structures

层次导航结构是一个树状结构。每个子页面只有一个父页面。要接触到孩子,你需要穿过父母。

对等是页面并排存在的导航结构。你可以以任何顺序从一页转到另一页。

组合导航结构使用层次结构和对等结构。页面组被组织成对等体或层次结构。

5.6 应用中的导航元素

问题

你了解导航结构。你想知道哪些元素可以用来实现你的应用的导航结构。

解决办法

用户可以在许多导航元素的帮助下找到他想看的内容。在某些情况下,导航元素会向用户指示内容在应用中的位置。导航元素可以用作内容或命令元素。因此,建议使用最适合您的导航结构的导航元素。以下是 UWP 应用可用的一些导航元素:

  • Pivot:该控件用于显示指向同一级别页面的持久链接列表。当您有对等导航结构时,可以使用此控件。
  • SplitView:这个控件用于显示一个应用中顶级页面的链接列表。这可以用在需要对等导航结构的场景中。
  • Hub:这个控件用于显示子页面的预览/摘要。导航到子页面是通过页面本身提供的链接或部分标题。当您具有层次导航结构时,将使用此控件。
  • ListView:这个控件用于显示项目摘要的主列表。选择一个项目会在详细信息部分显示所选项目的详细信息。这可以用于您的层次导航结构场景。

5.7 应用中的透视导航

问题

在您的应用中,您已经确定导航的导航结构是基于对等的。您希望使用透视控件实现导航。

解决办法

当您经常访问不同的内容类别时,Pivot 控件用于导航。Pivot 由两个或多个内容窗格组成,每个窗格都有一个相应的类别标题。标题将持续显示在屏幕上,选择状态清晰可见,这使得用户很容易知道他们选择了哪个类别。用户可以在标题上向左或向右滑动,这样做将允许他们导航到相邻的标题。他们还可以在内容上向左或向右滑动。

它是如何工作的

Open Visual Studio 2015. Select File ➤ New Project ➤ JavaScript ➤ Windows ➤ Universal ➤ Blank App (Windows Universal) template. This creates a Universal Windows app project (see Figure 5-10).

A978-1-4842-0719-2_5_Fig10_HTML.jpg

图 5-10。

New Project dialog   Open default.html, which is found in the root of the project. Replace the content of the body with the following content: <div data-win-control="WinJS.UI.Pivot" data-win-options="{ title: 'PIVOT Navigation', selectedIndex: 0 }">         <div data-win-control="WinJS.UI.PivotItem" data-win-options="{ 'header': 'one'}">             <p> Content - Item One </p>         </div>         <div data-win-control="WinJS.UI.PivotItem" data-win-options="{ 'header': 'two'}">             <p> Content - Item Two </p>         </div>         <div data-win-control="WinJS.UI.PivotItem" data-win-options="{ 'header': 'three'}">             <p> Content - Item Three </p>         </div>     </div> You have placed a div and set the data-win-control attribute to a value of WinJS.UI.Pivot. This converts the div to a Pivot control. You then pass options to the Pivot control using the data-win-options attribute. You are setting the title of the Pivot and the item that will be selected. The items of the pivot control are placed as a div with the data-win-control attribute set to a value of WinJS.UI.PivotItem. Each pivot item has a header. Set the header value using the data-win-options attribute. You can place as many items that your app calls for.   Next, press F5 and run the app. Figure 5-11 is a screenshot from the Windows Mobile output.

A978-1-4842-0719-2_5_Fig11_HTML.jpg

图 5-11。

Windows Mobile output  

如您所见,有三个枢纽项目。您可以单击透视项目的标题,或者从左向右滑动以导航到相邻的内容/项目。

5.8 应用中的拆分视图导航

问题

在您的应用中,您已经确定导航的导航结构是基于对等的。您希望提供一个带有顶级页面链接的菜单。您希望使用 SplitView 控件实现导航。

解决办法

SplitView 控件包含一个窗格和一个内容区域。窗格可以展开或折叠,而内容区域始终可见。窗格也可以保持打开状态,并且可以灵活地从应用的左侧或右侧显示。通常,导航链接位于此窗格中。

它是如何工作的

Open Visual Studio 2015. Select File ➤ New Project ➤ JavaScript ➤ Windows ➤ Universal ➤ Blank App (Windows Universal) template. This creates a Universal Windows app project (see Figure 5-12).

A978-1-4842-0719-2_5_Fig12_HTML.jpg

图 5-12。

New Project dialog   Open default.html, which is found in the root of the project. Replace the content of the body with the following code snippet: <div id="app">         <div class="splitView"              data-win-control="WinJS.UI.SplitView"              data-win-options="{closedDisplayMode:'none',              openedDisplayMode:'overlay'}">             <!-- Pane area -->             <div>                 <div class="header">                     <button class="win-splitviewpanetoggle"                             data-win-control="WinJS.UI.SplitViewPaneToggle"                             data-win-options="{ splitView: select('.splitView') }"></button>                     <div class="title">Menu</div>                 </div>                 <div id='navBar' class="nav-commands">                     <div data-win-control="WinJS.UI.NavBarCommand"                          data-win-options="{ onclick:SplitViewHelper.homeClick,                                              label: 'Home',                                              icon: 'home'}"></div>                     <div data-win-control="WinJS.UI.NavBarCommand"                          data-win-options="{ onclick:SplitViewHelper.favClick,                                              label: 'Favorite',                                              icon: 'favorite'}"></div>                     <div data-win-control="WinJS.UI.NavBarCommand"                          data-win-options="{ onclick:SplitViewHelper.settingsClick,                                              label: 'Settings',                                              icon: 'settings'}"></div>                 </div>             </div>             <!-- Content area -->             <button class="win-splitviewpanetoggle"                     data-win-control="WinJS.UI.SplitViewPaneToggle"                     data-win-options="{ splitView: select('.splitView') }"></button>             <div class="contenttext"><h2>Page Content </h2> </div>         </div>     </div> In order to use SplitView, you need to place a div and set the data-win-control attribute to a value of SplitView. Then you need to create two divs. The first div will be used for the navigation pane and second div will be used as the content area. You use WinJS.UI.NavBarCommand to place the links for different pages. You are listening for the click event of the nav bar items and on click will show content according to the navigation link that was clicked. The content page is just a div, and at runtime, depending on the navigation link clicked, you will update the content accordingly.   Next open default.js, found in the js folder. Replace the app.activated handler with the following code snippet: app.onactivated = function (args) {                 if (args.detail.kind === activation.ActivationKind.launch) {                         if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {                         } else {                         }                         args.setPromise(                     WinJS.UI.processAll().done(function () {                         SplitViewHelper.splitView = document.querySelector(".splitView").winControl;                         new WinJS.UI._WinKeyboard(SplitViewHelper.splitView.paneElement); // Temporary workaround: Draw keyboard focus visuals on NavBarCommands                     })             );                 }         };   Next, after the IIFE (immediately invoked function expression) declaration, place the following code snippet. You are creating a small helper classto handle the navigation click event. (function () {         "use strict";         //other app related code ommitted for brevity })(); WinJS.Namespace.define("SplitViewHelper", {     splitView: null,     homeClick: WinJS.UI.eventHandler(function (ev) {         document.querySelector('.contenttext').innerHTML = "<h2>SplitView Content area</h2>";     }),     favClick: WinJS.UI.eventHandler(function (ev) {         document.querySelector('.contenttext').innerHTML = "<h2>Favorites!</h2>";     }),     settingsClick: WinJS.UI.eventHandler(function (ev) {         document.querySelector('.contenttext').innerHTML = "<h2>Settings!</h2>";     }), }); You are replacing the inner HTML of the content div in this example. If you want to load a new page altogether, you can do so by making use of WinJS.UI.Pages.render('<URI>',<Content Host Elementn>);.   Now, press F5 and run the app. Figures 5-13 and 5-14 show output on a Windows Mobile.

A978-1-4842-0719-2_5_Fig14_HTML.jpg

图 5-14。

Content area

A978-1-4842-0719-2_5_Fig13_HTML.jpg

图 5-13。

Navigation pane  

5.9 UWP 应用中的枢纽导航

问题

您已经确定通用 Windows 平台应用的导航结构本质上是分层的。您希望在应用中使用 Hub 作为导航元素。

解决办法

Hub 控件用于导航,应用的内容可以分成不同的、相关的部分或类别,具有不同的详细程度。这是在诸如关系信息这样的场景中使用的一种常见模式,这种关系信息需要以优选的顺序遍历。导航的层次模式适用于提供各种体验和内容的应用。

它是如何工作的

Open Visual Studio 2015. Select File ➤ New Project ➤ JavaScript ➤ Windows ➤ Universal ➤ Blank App (Windows Universal) template. This creates a Universal Windows app project (see Figure 5-15).

A978-1-4842-0719-2_5_Fig15_HTML.jpg

图 5-15。

New Project dialog   Open default.html, found in the root of the project. Replace the contents after the <body> tag with the following code snippet: <div data-win-control="WinJS.UI.Hub">         <div class="section1" data-win-control="WinJS.UI.HubSection"              data-win-options="{header: 'Images', isHeaderStatic: true}">             <div class="imagesFlexBox">                 <img class="imageItem" src="/img/circle_image1.jpg" />                 <img class="imageItem" src="/img/circle_image3.jpg" />                 <img class="imageItem" src="/img/circle_image2.jpg" />                 <img class="imageItem" src="/img/circle_image1.jpg" />                 <img class="imageItem" src="/img/circle_image3.jpg" />                 <img class="imageItem" src="/img/circle_image2.jpg" />                 <img class="imageItem" src="/img/circle_image1.jpg" />                 <img class="imageItem" src="/img/circle_image3.jpg" />                 <img class="imageItem" src="/img/circle_image2.jpg" />             </div>         </div>         <div id="list" class="section2" data-win-control="WinJS.UI.HubSection"              data-win-options="{header: 'ListView', isHeaderStatic: true}">             <div id="listView"                  class="win-selectionstylefilled"                  data-win-control="WinJS.UI.ListView"                  data-win-options="{                 itemDataSource: HubExample.data.dataSource,                 itemTemplate: smallListIconTextTemplate,                 selectionMode: 'none',                 tapBehavior: 'none',                 swipeBehavior: 'none'             }">             </div>         </div>     </div>     <div id="smallListIconTextTemplate" data-win-control="WinJS.Binding.Template">         <div class="smallListIconTextItem">             <img src="#" class="smallListIconTextItem-Image"                  data-win-bind="src: picture" />             <div class="smallListIconTextItem-Detail">                 <h4 data-win-bind="innerText: title"></h4>                 <h6 data-win-bind="innerText: text"></h6>             </div>         </div>     </div> To create a Hub control, set the data-win-control attribute to a value of WinJS.UI.Hub on a div. To create sections inside the Hub, create a div and set its data-win-control attribute to WinJS.UI.HubSection. Set the header of the sections using the data-win-options attribute. Set the header property on the section.   Section 2 in the preceding code makes use of a ListView. So let’s create a data source for the list view. Open the default.js file in the js folder. Inside the immediately invoked function expression and after 'use strict' statement, copy and paste the following code snippet: var myData = new WinJS.Binding.List([   { title: "Fire Hydrant", text: "Red", picture: "/img/circle_list1.jpg" },   { title: "Fire Hydrant", text: "Yellow", picture: "/img/circle_list2.jpg" },   { title: "Pothole Cover", text: "Gray", picture: "/img/circle_list3.jpg" },   { title: "Sprinkler", text: "Yellow", picture: "/img/circle_list4.jpg" },   { title: "Electrical Charger", text: "Yellow", picture: "/img/circle_list5.jpg" },   { title: "Knob", text: "Red", picture: "/img/circle_list6.jpg" },   { title: "Fire Hydrant", text: "Red", picture: "/img/circle_list1.jpg" },   { title: "Fire Hydrant", text: "Yellow", picture: "/img/circle_list2.jpg" },   { title: "Pothole Cover", text: "Gray", picture: "/img/circle_list3.jpg" },   { title: "Fire Hydrant", text: "Red", picture: "/img/circle_list1.jpg" },   { title: "Fire Hydrant", text: "Yellow", picture: "/img/circle_list2.jpg" },   { title: "Pothole Cover", text: "Gray", picture: "/img/circle_list3.jpg" }   ]);         WinJS.Namespace.define("HubExample", {             data: myData         });   To run the app, press F5 in Visual Studio. Figure 5-16 shows output on a Windows Mobile device.

A978-1-4842-0719-2_5_Fig16_HTML.jpg

图 5-16。

Hub navigation output  

5.10 在 UWP 应用中使用 ListView 进行主/详细信息导航

问题

您已经确定通用 Windows 平台应用的导航结构本质上是分层的。您希望使用主/详细导航模式。您已经将 ListView 标识为导航元素。

解决办法

ListView 是一个在垂直堆栈中显示数据项列表的控件。您可以将数据源数据绑定到列表视图,并为呈现每个项目提供模板。

它是如何工作的

Open Visual Studio 2015. Select File ➤ New Project ➤ JavaScript ➤ Windows ➤ Universal ➤ Blank App (Windows Universal) template. This creates a Universal Windows app project (see Figure 5-17).

A978-1-4842-0719-2_5_Fig17_HTML.jpg

图 5-17。

New Project dialog   Open default.html, which is found in the root of the project. Replace the contents of the body with the following code snippet: <h1>ListView Navigation</h1> <div id="contentHost"></div> You have a div with id contentHost. Use this as a placeholder to show different pages at runtime; that is, render the master and details page inside this content host div element.   Next, open default.js, found in the js folder. Add the following code snippet just before the app.onactivated function definition: WinJS.Navigation.addEventListener("navigating", function (args) {         var url = args.detail.location;         var host = document.getElementById("contentHost");         host.winControl && host.winControl.unload && host.winControl.unload();         WinJS.Utilities.empty(host);         args.detail.setPromise(WinJS.UI.Pages.render(url, host, args.detail.state));     }); Through this code, you are listening to a navigation event that will occur on the page. You have provided a handler for the navigation event. In the event handler, you intercept the navigation call, get the URL of the destination page, get the content host element, and render the destination page inside the content host element.   Next, modify the app.onactivated method, as follows: app.onactivated = function (args) { if (args.detail.kind === activation.ActivationKind.launch) {     if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {     } else {     }     args.setPromise( WinJS.UI.processAll().then(function () {             return WinJS.Navigation.navigate("/master.html");         })    ); }     }; The only thing done in the activated event handler is navigating to a page named master.html (you will create this page next). So when the app loads, master.html, which will hold the ListView control, will be rendered inside the content host.   Add a new HTML file to the project and name it master.html. Replace the contents of the master.html with the following snippet: <!DOCTYPE html> <html> <head>     <title></title>     <link href="/css/default.css" rel="stylesheet" />     <!-- WinJS references -->     <link href="WinJS/css/ui-dark.css" rel="stylesheet" />     <script src="WinJS/js/base.js"></script>     <script src="WinJS/js/ui.js"></script>     <script src="js/data.js"></script>     <script src="js/master.js"></script> </head> <body >     <div class="smallListIconTextTemplate"          data-win-control="WinJS.Binding.Template"          style="display: none">         <div class="smallListIconTextItem">             <img src="#" class="smallListIconTextItem-Image"                  data-win-bind="src: picture" />             <div class="smallListIconTextItem-Detail">                 <h4 class="win-h4" data-win-bind="textContent: title"></h4>                 <h6 class="win-h6" data-win-bind="textContent: text"></h6>             </div>         </div>     </div>     <div class="listView win-selectionstylefilled"          data-win-control="WinJS.UI.ListView"          data-win-options="{                     itemDataSource: ListViewExample.data.dataSource,                     itemTemplate: select('.smallListIconTextTemplate'),                     tapBehavior: WinJS.UI.TapBehavior.directSelect,                     layout: { type: WinJS.UI.ListLayout }             }">     </div> </body> </html> What you have done in master.html is place a list view, provide a data source to the list view, and provide an item template for rendering each item. The template displays an image, title, and text from the data item that will be bound at runtime.   Next, create a new JavaScript file in the js folder and name it master.js. Add the following code snippet to the JavaScript file: (function () {     'use strict';     WinJS.UI.Pages.define("/master.html", {         ready: function (element, options) {             var that = this;             element.addEventListener("iteminvoked", function (evt) {                 evt.detail.itemPromise.then(function (item) {                     WinJS.Application.sessionState.selectedItem = item.data;                     WinJS.Navigation.navigate("/detail.html");                 });             });         }     }) })(); Let’s go over the code for a moment. You have created a logic file for master.html with this new JavaScript file. Define page members with the WinJS.UI.Pages.define() method. You have provided a ready method that will be called when the page is loaded. And you are adding event listener for ListView item tap. When the item is tapped, store the selected item in an application session state and then navigate to detail.html.   Next, create a new JavaScript file named data.js inside the js folder. This file provides the data source for the ListView in master.html. Paste the following code snippet in data.js: var myData = new WinJS.Binding.List([ { title: "Lemon", text: "Sorbet", picture: "/img/60Lemon.png" }, { title: "Mint", text: "Gelato", picture: "/img/60Mint.png" }, { title: "Orange", text: "Sorbet", picture: "/img/60Orange.png" }, ]); WinJS.Namespace.define("ListViewExample", {         data : myData });   Now let’s create the details page. Add a new HTML file to the root of the project and name it detail.html. Paste the following code snippet to the newly created file: <!DOCTYPE html> <html> <head>     <title></title>     <link href="/css/default.css" rel="stylesheet" />     <!-- WinJS references -->     <link href="WinJS/css/ui-dark.css" rel="stylesheet" />     <script src="WinJS/js/base.js"></script>     <script src="WinJS/js/ui.js"></script>     <script src="/js/detail.js"></script> </head> <body>     <div>         <button data-win-control="WinJS.UI.BackButton"></button>         <h3>Item Details </h3>         <h2><span data-win-bind="innerText: title"></span></h2>         <img data-win-bind="src: picture" />         <h2><span data-win-bind="innerText: text"></span></h2>     </div> </body> </html> In this page, just output the details of the selected item in the list view. Notice that you are using data binding expressions to show the title, text, and image of the data item.   Now you need to create the logic file for detail.html. Add a new JavaScript file named detail.js inside the js folder. Paste the following code snippet to the newly created file: (function () {     'use strict';     WinJS.UI.Pages.define("/detail.html", {         processed: function (element, options) {             var that = this;             WinJS.Binding.processAll(element, WinJS.Application.sessionState.selectedItem);         }     }) })(); When the page loads, just call the Binding.processAll() on the page and provide the selected item as the data item that needs to be bound to the elements.   You are done with all the code required for master/detail navigation. Now press F5 and run the app. Figures 5-18 and 5-19 show the output from a Windows Mobile device.

A978-1-4842-0719-2_5_Fig19_HTML.jpg

图 5-19。

Details screen

A978-1-4842-0719-2_5_Fig18_HTML.jpg

图 5-18。

Master screen

六、为不同的屏幕调整用户界面

借助通用 Windows 平台(UWP),您可以在 Windows 系列的任何设备上运行您的应用。设备系列包括手机、平板电脑、笔记本电脑、Xbox 等等。设备系列中有不同的屏幕尺寸。该平台在幕后施展魔法,确保你的应用的用户界面在所有设备上都能正常工作。由于平台在幕后为你处理事情,你不需要对你的应用做任何定制来支持不同的屏幕尺寸。但是在某些情况下,您可能希望为特定的屏幕尺寸提供特定的 UI。例如,当你的应用在手机上运行时,你可能想要一个单列布局;而当同一个应用在平板电脑或个人电脑上运行时,你想要两栏布局。本章着眼于如何让你的用户界面适应不同的屏幕尺寸。

如果 UWP 应用可以在任何屏幕尺寸的任何 Windows 10 设备系列上运行,那么作为应用开发者,你为什么要担心为特定屏幕尺寸量身定制的用户界面呢?

如前所述,该平台负责确保您的应用的用户界面在所有屏幕尺寸下都能正常工作,但您可能仍然需要根据应用运行的屏幕进行定制。以下几点值得注意。它们提供了关于您为什么想要定制的见解。

  • 有效利用可用空间,减少导航。当一个应用的用户界面被设计成在小屏幕上看起来不错时,这个应用也可以在个人电脑上使用。但是可能会有一些浪费的空间。更好的设计是当屏幕尺寸超过一定尺寸时显示更多的内容。在更大的屏幕上显示更多的内容可以减少用户导航的次数。
  • 设备能力。不同的设备有不同的功能。通过为特定设备定制您的应用,您可以更好地利用该特定设备上的可用功能,然后您可以启用/禁用与此相关的功能。
  • 输入优化。UWP 中的控件库适用于所有输入类型,包括触摸、笔、键盘和鼠标。但是您可以针对特定设备优化输入。例如,基于手机的应用通常在屏幕底部提供导航;而 PC 用户期望导航在屏幕的顶部可用。

6.1 为不同屏幕设计断点

问题

由于您需要支持 Windows 10 设备系列下的各种屏幕尺寸,您可能想知道您的应用中的具体目标宽度。

解决办法

断点是 CSS(级联样式表)中使用的术语,用来表示屏幕的大小或宽度,您可以根据它来编写样式规则。Windows 10 设备系列中有大量设备目标和屏幕尺寸可用。但是您不必针对这些设备目标或屏幕尺寸中的每一个来优化您的 UI。相反,你应该设计关键的屏幕宽度。这些关键屏幕宽度也称为断点。让我们看看您应该关注的断点。

  • 小:320epx。这些设备目标/屏幕的有效像素宽度为 320。典型的屏幕尺寸是 4 到 6 英寸。通常,这些设备是电话。
  • 中等:720epx。这些设备目标/屏幕的有效像素宽度为 720。典型的屏幕尺寸是 6 到 12 英寸。通常,这些设备是大屏幕的平板电脑和手机。
  • 大:1024epx。这些设备目标/屏幕的有效像素宽度为 1024 或更高。典型的屏幕尺寸为 13 英寸及以上。通常,这些设备是 PC、笔记本电脑、Surface Hubs 等等。

6.2 自适应 UI 技术:重新定位

问题

您希望您的应用支持所有设备目标/屏幕尺寸。你想让你的用户界面自适应,你想根据你的应用运行的屏幕大小重新定位你的屏幕的某些部分。

解决办法

使你的应用的用户界面适应不同屏幕尺寸的技术之一涉及 CSS 媒体查询,这超出了本书的范围,因此,你不会深入研究它。但是你可以在 W3C 学校在线 http://www.w3schools.com/cssref/css3_pr_mediaquery.asp 阅读和了解更多关于媒体查询的信息。您将编写一个针对屏幕大小的媒体查询规则。该规则将定义样式,以根据应用运行的屏幕重新定位 UI 元素。

它是如何工作的

Open Visual Studio 2015. Select File ➤ New Project ➤ JavaScript ➤ Windows ➤ Universal ➤ Blank App (Universal Windows) template. This creates a universal Windows app template (see Figure 6-1).

A978-1-4842-0719-2_6_Fig1_HTML.jpg

图 6-1。

New Project dialog   Open the default.html, which is found at the root of the project. After the tag, add the following content: <div class="appGrid">         <div class="sectionA">             <h1>A</h1>         </div>         <div class="sectionB">             <h1>B</h1>         </div> </div> You have created a bunch of divs. The outer div (appGrid) acts as a container. Next, you have two sections that are displayed as part of the app. On a small screen, you want to show sectionA and sectionB one on top of the other. When running on medium and large screens, place sectionA and sectionB next to each other.   Next, you create the necessary styles for the app. Open default.css, which is found in the css folder. Replace the contents of default.css with the following style rules. .appGrid {     display: -ms-grid;     -ms-grid-columns: 2fr 1fr;     grid-columns: 2fr 1fr;     -ms-grid-rows: 1fr;     grid-rows: 1fr;     width: 100%;     height: 100%;     margin:24px; } .sectionA {     -ms-grid-row: 1;     -ms-grid-column: 1;     background-color: lightgray;     width: 100%;     height: 100%;     padding: 10px;     color: black; } .sectionB {     -ms-grid-row: 1;     -ms-grid-column: 2;     background-color: lightblue;     width: 100%;     height: 100%;     padding: 10px;     color: black; } Let’s go through the code once. You have just defined three rules. One of the rules is for the outer grid. You are using a grid-based display with two columns and one row. Section A is placed in column 1 and Section B is placed in column 2. Since there is no media query applied yet, these rules are applied all the time.   Now you define a media query for handling the UI when the max width of the screen is 320. When you are running the app on a small screen, you want the layout to be narrow—only one column and two rows. Section A is placed in row 1 and Section B is placed in row 2. You place the CSS rule in the same CSS file (default.css). Here is the code snippet for the media query: @media (max-width:320px){     .appGrid {         display: -ms-grid;         -ms-grid-columns: 1fr;         grid-columns: 1fr;         -ms-grid-rows: 2fr 1fr;         grid-rows: 2fr 1fr;         width: 100%;         height: 100%;         margin:12px;     }     .sectionA {         -ms-grid-row: 1;         -ms-grid-column: 1;         background-color: lightgray;         width: 100%;         height: 100%;     }     .sectionB {         -ms-grid-row: 2;         -ms-grid-column: 1;         background-color: lightblue;         width: 100%;         height: 100%;     } } This shows that when the screen size is 320px and lower, a new style rule is picked up. As part of the style rule, the app grid is now one column and two rows. This repositioned the placement of Section A and Section B.   Next, press F5 and run the app. You can run the app in a Windows 10 Mobile emulator or you can run it on a local machine. Figures 6-2 and 6-3 show the output from a phone and a PC.

A978-1-4842-0719-2_6_Fig3_HTML.jpg

图 6-3。

Output on a Windows PC

A978-1-4842-0719-2_6_Fig2_HTML.jpg

图 6-2。

Output on Windows Mobile  

6.3 自适应 UI 技术:流畅的布局

问题

您已经创建了一个布局,希望在列表视图中显示项目的数量。但是您希望列表视图适应窄屏幕和中或大屏幕。您希望列表视图项目根据屏幕大小做出响应。

解决办法

UWP 中的 ListView 控件用于需要显示大量项目的情况。顾名思义,列表视图在屏幕上呈现一个列表,每个项目都绑定到一个数据项。列表视图的每一项都可以通过提供模板来定制。ListView 还有一个概念叫布局。布局定义了项目应该如何在屏幕上展开。它支持两种现成的布局:ListLayout 和 GridLayout。ListLayout 用于单列/多行渲染。GridLayout 呈现基于网格的布局(即行和列占据屏幕上的可用空间)。

它是如何工作的

Open Visual Studio 2015. Select File ➤ New Project ➤ JavaScript ➤ Windows Universal ➤ Blank App (Universal Windows) template. This creates a universal Windows app template (see Figure 6-4).

A978-1-4842-0719-2_6_Fig4_HTML.jpg

图 6-4。

New Project dialog   Create a new file called weatherData.js in the js folder. Add an immediately invoked function expression. Here is the code snippet: (function (undefined) {     'use strict'     //rest of the code here })();   Next, create an array of weather data after the ‘use strict’ statement inside the function body, as shown earlier. Let’s build fictitious weather data for the purpose of this recipe demo. Here is the code snippet: var weatherData = [         {             date: formatDateString(new Date().getDate()),             imgSrc: "img/tile-snow.png",             hi: "32°",             low: "16°",             temp: "24°",             feelsLike: "28°",             chanceOfSnow: "88%"         }          //add more items here         ];   Next, create a function to format the date. You want to display the date in a “Sun 10” type pattern. You need to customize the date string for display. Here is the code snippet for the formatDateString function: function formatDateString(date) {         var daysOfWeek = ["Sun","Mon","Tues","Wed","Thurs","Fri","Sat"];         var nearDays = ["Yesterday","Today","Tomorrow"];         var dayDelta = date - new Date().getDate() + 1;         var dateString = "";         if (dayDelta < 3) {             dateString = nearDays[dayDelta];         }         else {             dateString = daysOfWeek[date % 7] +             " <span class='day'>" + date + "</span>";         }         return dateString;     };   You need to expose this weatherData array so that you can consume it in default.html and bind it to the list view. Create a namespace and expose the weatherData, as shown here: var weatherDataList = new WinJS.Binding.List(weatherData); WinJS.Namespace.define("FluidApp.Data", {         weatherData : weatherDataList }) You have created a namespace called FluidApp.Data and exposed weatherData as a public property, which can be bound to any win control.   You need to add a reference to weatherData.js. Add the following script tag in head section of default.html. Add this before the default.js script file reference: <script src="/js/weatherData.js"></script>   Next, you need to create the UI. In default.html, remove the default contents, which are found inside the tag. Add the following template, which is used for displaying each item of the list view:     <div id="weatherTemplate" class="dayTemplate" data-win-     control="WinJS.Binding.Template">     <div class="dayGrid">         <div class="win-type-x-large dayDate"                     data-win-bind="innerHTML: date">         </div>         <div class="dayImg">             <img data-win-bind="src: imgSrc" />         </div>         <div class="win-type-x-large dayHighLow">             <span class="dayHigh" data-win-bind="innerHTML: hi"></span>             <span>/</span>             <span class="dayLow" data-win-bind="innerHTML: low"></span>         </div>         <div class="dayFeelsLike">             <span>Feels like </span>             <span data-win-bind="innerHTML: feelsLike"></span>         </div>         <div class="dayChanceOfSnow">             <span>Chance of snow is </span>             <span data-win-bind="innerHTML: chanceOfSnow"></span>         </div>     </div> </div>   Next, let’s add the ListView control. Paste the following code snippet after the template that you created in the previous step: <div id="weatherListView" class="weatherListView"         data-win-control="WinJS.UI.ListView"          data-win-options="{            itemDataSource:FluidApp.Data.weatherData.dataSource,            itemTemplate:select('#weatherTemplate'),           layout:WinJS.UI.GridLayout}"></div> You have created a list view and provided weatherData as the item data source. You have also provided the item template. You have set the layout of the list view to GridLayout by default. You will change this layout property of the list view based on the screen resize.   Open default.js, which is found in the js folder. Modify the app.onactivated function with the following code snippet: app.onactivated = function (args) {                 if (args.detail.kind === activation.ActivationKind.launch) {                         if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {                         } else {                         }                         args.setPromise(WinJS.UI.processAll().then(function ready() {                             var listView = document.querySelector("#weatherListView");                             var listViewLayout = new WinJS.UI.GridLayout();                             if (document.body.clientWidth <= 320) {                                 listViewLayout = new WinJS.UI.ListLayout();                             }                    listView.layout = listViewLayout;                 window.addEventListener("resize", resizeListView, false)                         }));                 }         }; Let’s go over the code here. Once the UI processing is done, you grab the list view first. You then check the width of the screen that you are currently running on. If the width is less than or equal to 320 (i.e., you are on a small screen), you just change the layout of the list view accordingly. If you are on a small screen, you use ListLayout, but when not on a small screen, you set it to GridLayout. You also register an event listener to resize the window. Next, let’s look at what the resize event handler does.   After the app.onactivated function, create a new function, resizeListView, and paste the following code snippet: function resizeListView() {     var listview = document.querySelector("#weatherListView").winControl;     if (document.body.clientWidth <= 320) {         if (!(listview.layout instanceof WinJS.UI.ListLayout)) {             listview.layout = new WinJS.UI.ListLayout();         }     }     else {         if (listview.layout instanceof WinJS.UI.ListLayout) {             listview.layout = new WinJS.UI.GridLayout();         }     } }   Now run the app on a mobile device or on a PC to see the output. Press F5 to run the app. The screenshot shown in Figure 6-5 is on Windows 10 Mobile and the one shown in Figure 6-6 is on a Windows 10 PC.

A978-1-4842-0719-2_6_Fig6_HTML.jpg

图 6-6。

Output on a Windows PC

A978-1-4842-0719-2_6_Fig5_HTML.jpg

图 6-5。

Output on a Windows 10 Mobile  

你刚刚实现的是为不同屏幕尺寸量身定制的 UI。您使用了 ListView 布局技术来显示网格或列表布局,这取决于屏幕的大小。

七、应用生命周期和导航

本章介绍通用的 Windows 应用事件和生命周期,包括启动 Windows 应用时引发的标准事件集以及如何处理这些事件。本章涵盖了开发者如何处理应用的暂停和终止。它还介绍了如何在这些事件期间保存和恢复数据,这有助于开发人员提供更好的用户体验。

本章还介绍了导航模型,并解释了如何构建一个可以包含多个虚拟 HTML 页面的单页面应用。

7.1 应用状态和事件

问题

当用户启动、关闭或恢复应用时,您需要识别和订阅应用中的各种事件。

解决办法

使用通用 Windows 应用项目中的default.js文件来订阅和处理应用生命周期中的各种事件。

它是如何工作的

使用通用 Windows 模板创建一个新的通用 Windows 项目,该模板可以在 Microsoft Visual Studio 2015 的“新建项目”对话框的 JavaScript ➤ Windows ➤通用节点下找到。这将在 Visual Studio 解决方案中创建一个项目,并在其中创建必要的文件。

从项目的js文件夹中,从 Visual Studio 解决方案资源管理器中打开 default.js 文件。您会看到两个默认订阅的重要事件。

  • app.onactivated
  • app.oncheckpoint

这些事件的代码如下所示。

app.onactivated = function (args)

{

if (args.detail.kind === activation.ActivationKind.launch)

{

if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {

} else {

}

args.setPromise(WinJS.UI.processAll());

}

};

app.oncheckpoint = function (args)

{

};

作为开发人员,您应该处理这些事件来描述当各种执行状态被触发时,应用应该如何保存和恢复数据。

在通用 Windows 应用中,有三种应用执行状态:

  • 不运行
  • 运转
  • 暂停的

图 7-1 展示了通用 Windows 应用的生命周期。

A978-1-4842-0719-2_7_Fig1_HTML.jpg

图 7-1。

Application states and events

当一个通用的 Windows 应用启动时,WinJS 应用按下面显示的顺序触发以下事件。

WinJS.Application.loaded   WinJS.Application.activated   WinJS.Application.ready   WinJS.Application.unload  

当你的应用将要被挂起时。Application.checkpoint 事件被触发。

表 7-1 提供了应用事件列表。

表 7-1。

List of Application Events

| 事件名称 | 描述 | | --- | --- | | WinJS。应用.已加载 | 一旦 HTML 文档被完全加载,这个事件就由`DOMContentLoaded`事件触发。 | | WinJS。应用.激活 | 当您的应用被激活时,该事件被触发。 | | WinJS。应用.就绪 | 一旦加载和激活的事件执行完成,就会触发此事件。 | | WinJS。应用.卸载 | 该事件在页面卸载前由`beforeunload`事件触发。 | | WinJS。应用.检查点 | 当应用挂起时,会触发此事件。 |

开发人员应该处理激活事件和检查点事件,以添加他们的自定义逻辑来保存和恢复应用的状态,因此,在创建新项目时,您会看到默认情况下包括这两个事件。

其他事件可以使用WinJS.Application.addEventListener方法订阅。

打开default.js文件,在onactivated事件行上方添加以下代码片段。

WinJS.Application.addEventListener("loaded", function(event) {

console.log("loaded event");

});

WinJS.Application.addEventListener("ready", function (event) {

console.log("ready event");

});

另外,在onactivated事件中添加console.log ("activated");

当您使用本地机器选项在 Windows 桌面上运行应用时,您会看到按指定顺序引发的事件,如图 7-2 所示。

A978-1-4842-0719-2_7_Fig2_HTML.jpg

图 7-2。

Application events in the JavaScript console window Note

在调用WinJS.Application.start事件之前,不会引发这些事件。这个方法在default.js文件中被调用。

Windows 应用必须首先安装在要激活的设备上。这通常可以通过从 Windows 应用商店安装应用或在开发期间使用 Visual Studio 来构建和部署通用应用来实现。

当用户从开始屏幕或应用列表中点击应用磁贴时,Windows 应用被激活。在此阶段,考虑到以下情况,应用处于未运行状态:

  • 该应用首次推出。
  • 应用没有运行,因为它崩溃了。
  • 该应用被暂停,后来被系统终止。

该应用可以通过多种方式激活。ActivationKind枚举可用于找出应用是如何激活的,并确定应用激活的确切原因。

例如,当用户点击应用磁贴时,应用可以被激活。ActivationKind 值在这种情况下启动。同样,当从搜索契约中激活应用时,会搜索 ActivationKind 值。

下面的代码片段展示了如何在onactivated事件中处理这个问题。

if (args.detail.kind === activation.ActivationKind.launch) {

console.log("launch activation kind");

}

else

if (args.detail.kind === activation.ActivationKind.Search) {

console.log("search activation kind");

}

表 7-2 显示了一些最常见的激活方法及其枚举值。

表 7-2。

Activation Methods

| 枚举成员 | 价值 | 描述 | | --- | --- | --- | | 发动 | Zero | 当用户启动应用或点击应用列表中的磁贴时,会收到该值。 | | 搜索 | one | 当用户希望使用搜索契约通过应用进行搜索时,会收到该值。 | | 共享目标 | Two | 当使用共享契约激活应用时,会收到该值。 | | 文件 | three | 当设备中的另一个应用启动一个文件时,会收到该值,该文件的文件类型由该应用注册以进行处理。 | | 草案 | four | 当另一个应用启动其方案名称被注册为由该应用处理的 URI 时,会收到该值。 | | 文件 | five | 当用户选择该应用提供的文件时,会收到该值。 | | 文件保存选择器 | six | 当用户尝试保存文件并选择应用作为位置时,会收到该值。 | | 缓存文件更新程序 | seven | 当用户尝试保存应用提供内容管理的文件时,会收到此值。 | | 联系人选取器 | eight | 该值在用户选择联系人时接收。 | | 设备 | nine | 该应用处理自动播放。 | | 语音命令 | Sixteen | 由于语音命令而激活应用时,会收到该值。 | | 祝酒词 | One thousand and ten | 当用户点击 toast 通知或 toast 通知中的操作后激活应用时,会收到该值。 |

7.2 处理应用中未处理的异常

问题

你的应用崩溃,用户立即被带到 Windows 开始屏幕,没有任何信息。您需要处理这种情况,以提供更好的用户体验。

解决办法

您可以处理default.js文件中的WinJS.Application.error事件来记录错误并向用户显示一条消息。

它是如何工作的

当您的通用 Windows 应用中出现未处理的异常时,会调用MSApp.terminateApp函数,之后应用会关闭,不会向用户提供任何信息。

在这个方法中,让我们处理WinJS.Application.error事件,并在应用中出现未处理的异常时向用户显示一条消息。

使用通用 Windows 模板创建一个新的通用 Windows 项目,该模板可以在 Microsoft Visual Studio 2015 的“新建项目”对话框的 JavaScript ➤ Windows ➤通用节点下找到。这将在 Visual Studio 解决方案中创建一个项目,并在其中创建必要的文件。

js文件夹中打开default.js文件,并用下面的代码片段替换它。

(function () {

"use strict";

var app = WinJS.Application;

var activation = Windows.ApplicationModel.Activation;

WinJS.Application.addEventListener("error", function(eventArgs) {

var errorMessage = new Windows.UI.Popups.MessageDialog("There was an error in the app. Kindly contact the app publisher");

errorMessage.showAsync();

return true;

});

app.onactivated = function (args) {

args.setPromise(WinJS.UI.processAll());

throw new WinJS.ErrorFromName();

};

app.start();

})();

这段代码从onactivated事件中抛出一个异常。WinJS.Application.addEventListener函数用于向错误事件添加一个监听器,以处理未处理的异常并向用户显示一条消息。

使用 Visual Studio 2015 中的本地计算机选项在 Windows 桌面上运行应用。您会看到图 7-3 中显示的信息。

A978-1-4842-0719-2_7_Fig3_HTML.jpg

图 7-3。

Displaying a message on an unhandled exception in the app Note

错误处理函数返回 true。值 true 用于指示错误已被处理,因此应用不应被终止。

7.3 处理应用的终止和恢复

问题

您需要确定应用以前的执行状态,以便在启动时恢复它。

解决办法

您可以使用激活事件的参数的detail.previousExecutionState属性来标识应用的状态。此属性确定应用是作为新应用启动的,还是之前被用户关闭的,或者是在应用暂停或终止后用户正在启动的。

它是如何工作的

previous ExecutionState 包含指示应用先前状态的值。您可以在激活的事件中使用该值来加载以前的状态值或基于该值的初始状态值。

使用通用 Windows 模板创建一个新的通用 Windows 项目,该模板位于 Microsoft Visual Studio 2015 中“新建项目”对话框的 JavaScript ➤ Windows ➤通用节点下。这将在 Visual Studio 解决方案中创建一个项目,并在其中创建必要的文件。

js文件夹中打开default.js文件,用下面的代码片段替换onactivated事件。

app.onactivated = function (args) {

if (args.detail.kind === activation.ActivationKind.launch) {

if (args.detail.previousExecutionState === activation.ApplicationExecutionState.notRunning) {

console.log("Previous state is not running");

}

if (args.detail.previousExecutionState === activation.ApplicationExecutionState.closedByUser) {

console.log("Previous state is closed by the user");

}

if (args.detail.previousExecutionState === activation.ApplicationExecutionState.terminated) {

console.log("Previous state is terminated");

}

args.setPromise(WinJS.UI.processAll());

}

};

这段代码处理onactivated事件,其中args.detail.previousExecutionState用于查找应用的先前状态。ApplicationExecutionState枚举用于检查应用的状态。

当您运行应用时,您会在 JavaScript 控制台窗口中看到之前显示的状态,如图 7-4 所示。

A978-1-4842-0719-2_7_Fig4_HTML.jpg

图 7-4。

PreviousExecutionState displayed in the JavaScript console window

ApplicationExecutionState 枚举包含这些值,如表 7-3 所示。

表 7-3。

ApplicationExecutionState and Enumeration Values

| 状态 | 描述 | | --- | --- | | 不运行 | 当用户第一次启动应用,关闭应用,并在 10 秒内启动应用,或者当应用崩溃时,此应用的先前状态为“未运行”。 | | 运转 | 如果应用已经在运行,并且用户使用契约或辅助磁贴启动应用,则应用的先前状态为“正在运行”。 | | 暂停的 | 如果应用被暂停,并且稍后通过辅助磁贴或合约激活,则应用的先前状态为“暂停”。 | | 终止的 | 如果应用先前被操作系统终止,则应用的先前状态为“已终止”。 | | closedByUser | 如果应用被用户关闭并且在 10 秒内没有重新启动,则应用的先前状态为“closedByUser”。可以使用 Alt+ F4 快捷键或关闭手势来关闭应用。 |

7.4 使用 SessionState 存储状态

问题

您需要存储应用在整个生命周期中的状态,以便可以在应用激活时恢复其价值。

解决办法

使用WinJS.Application.sessionState将数据存储到应用的状态,可用于在应用暂停或终止后恢复应用。

它是如何工作的

WinJS.Application.sessionState对象用于存储应用信息,以便在应用暂停或终止后恢复应用的状态。

当应用暂停然后重新启动时,它不会丢失其状态。但是当应用被挂起,然后操作系统由于内存或其他原因终止应用时,应用的状态就会丢失。再次启动时,该应用将从头开始。

WinJS.Application.sessionState有助于克服这个问题。

让我们用一些代码来演示 SessionState 的用法。

Create a new Universal Windows project using the Universal Windows template, which can be found under the JavaScript ➤ Windows ➤ Universal node of the New Project dialog in Microsoft Visual Studio 2015. This creates a single project in the Visual Studio Solution along with the necessary files in it to start.   Open the default.html page from Visual Studio Solution Explorer and replace it with the following code snippet. <!DOCTYPE html> <html> <head>     <meta charset="utf-8" />     <title>Recipe7.4</title>     <!-- WinJS references -->     <link href="WinJS/css/ui-light.css" rel="stylesheet" />     <script src="WinJS/js/base.js"></script>     <script src="WinJS/js/ui.js"></script>     <!-- Recipe7.4 references -->     <link href="/css/default.css" rel="stylesheet" />     <script src="/js/default.js"></script> </head> <body class="win-type-body" style="margin: 20px"> <div>     <input id="txt1"/>     <input id="txt2"/> </div> </body> </html> The preceding code snippet adds two input controls on the page so that the user can enter data.   Open the default.js file from the js folder and replace the onactivated and oncheckpoint events with the following code snippet. app.onactivated = function (args) {                 if (args.detail.kind === activation.ActivationKind.launch) {                     if (args.detail.previousExecutionState === Windows.ApplicationModel.Activation.ApplicationExecutionState.notRunning) {                         console.log("setting the initial values");                         document.getElementById("txt1").value = '';                         document.getElementById("txt2").value = '';                     }                     if (args.detail.previousExecutionState === Windows.ApplicationModel.Activation.ApplicationExecutionState.closedByUser) {                         console.log("setting the initial values");                         document.getElementById("txt1").value = '';                         document.getElementById("txt2").value = '';                     }                     if (args.detail.previousExecutionState === Windows.ApplicationModel.Activation.ApplicationExecutionState.terminated) {                         console.log("setting the previous state values");                         document.getElementById("txt1").value = WinJS.Application.sessionState.txt1;                         document.getElementById("txt2").value = WinJS.Application.sessionState.txt2;                     }                         args.setPromise(WinJS.UI.processAll());                 }         };         app.oncheckpoint = function (args) {             WinJS.Application.sessionState.txt1 = document.getElementById("txt1").value;             WinJS.Application.sessionState.txt2 = document.getElementById("txt2").value;         };  

代码片段处理onactivatedoncheckpoint事件。当应用暂停时,触发oncheckpoint事件。在此阶段,用户在两个文本框中输入的值保存在sessionState中。

如果应用的先前状态被终止,当应用被重新激活时,sessionState的值被恢复并更新到页面上的文本框中。如果先前的状态不是 Running 或 closedByUser,则恢复默认值。

当你运行已经挂起和终止的应用时,你会在页面中看到恢复的应用数据,如图 7-5 所示。

A978-1-4842-0719-2_7_Fig5_HTML.jpg

图 7-5。

Page displaying the data restored from the sessionState

您可以从 Visual Studio 2015 测试应用状态并模拟挂起和终止的事件。为此,您需要使用调试位置工具栏中的生命周期事件下拉列表,如图 7-6 所示。

A978-1-4842-0719-2_7_Fig6_HTML.jpg

图 7-6。

Tool to test the Application Lifecycle events from Visual Studio

可以通过单击暂停选项在应用运行时暂停应用,然后单击恢复选项恢复应用来模拟暂停和恢复生命周期事件。单击暂停选项时,会引发onCheckpoint事件。通过单击暂停和关闭按钮,可以模拟暂停和终止事件。此时,onCheckpoint事件被引发,您的应用终止。当您再次运行应用时,previousExecutionState包含终止值。

7.5 使用超链接在页面间导航

问题

你需要以一种最简单的方式在通用 Windows 应用的页面间导航。

解决办法

使用 HTML 在应用页面间导航的最简单方法之一是使用超链接。

它是如何工作的

假设你的 app 中有default.htmlnewpage.html文件,如果你想从default.html导航到newpage.html,在default.html中添加以下代码就可以做到这一点。

<p><a href="newpage.html">Navigate to page 2</a></p>

代码中的href是引用newpage.html的相对链接。这是一个 HTML 页面,是您的通用 Windows 应用的一部分,应该位于项目的根目录中。

或者,如果您希望指定作为应用一部分的本地文件的 URI,您可以使用名为 ms-appx 的新包内容 URI 方案。

前面的代码可以重写如下:

<p><a href="ms-appx:///newpage.html">Navigate to New page</a></p>

Note

尽管这个菜谱执行顶层导航,但您必须避免在 Windows 应用中这样做。这是网页的理想选择,但不适合手机或平板电脑应用。有时候,当应用加载下一页时,屏幕可能会变成空白。

微软建议使用单页导航,与顶级导航相比,它提供了更好的性能和更像应用的体验。

7.6 使用单页导航在页面之间导航

问题

您需要使用单页导航模式在通用 Windows 应用的页面之间导航。

解决办法

在通用 Windows 应用中实现单页导航的最简单的解决方案之一是使用必要的文件来执行从 Windows 8.1 导航应用模板到 Windows 10 项目的导航逻辑。

Note

在撰写本文时,Windows 10 没有提供类似于 Windows 8.1 中的导航模板。

它是如何工作的

Visual Studio 2015 没有为 UWP 应用提供导航模板。它只提供空白的应用模板。幸运的是,VS 2015 还包括 Windows 8.1 应用的模板和一个做大量导航逻辑的模板。让我们将 Windows 8.1 项目中的必要文件包含到通用 Windows 应用中。

启动 Visual Studio 2015,使用导航 App 模板新建一个 Windows 8.1 项目,如图 7-7 所示。

A978-1-4842-0719-2_7_Fig7_HTML.jpg

图 7-7。

Creating Windows 8.1 project using Navigation App

这个模板添加了navigator.js文件,并用导航逻辑更新了default.js。这些文件可以在项目的js文件夹下找到。

该模板还创建了一个名为pages的文件夹,并包含一个包含三个文件的home子文件夹:home.csshome.htmlhome.js

pages文件夹包含所有页面控件,就像 Windows 应用中的页面。启动导航应用模板时,默认加载pagecontrol home.html文件。这是在default.html页面中设置的。

加载页面控件的所有细节都由PageControlNavigator处理,它是navigator.js文件的一部分。

下一步是创建一个新的通用 Windows 应用,并对其进行更新,以包含使用 Windows 8.1 导航模板创建的项目中的文件。

A978-1-4842-0719-2_7_Fig8_HTML.jpg

图 7-8。

Including the js folder and pages folder to UWP App in Visual Studio 2015 Create a new Universal Windows Project using the Universal Windows template, which can be found under the JavaScript ➤ Windows ➤ Universal node of the New Project dialog in Microsoft Visual Studio 2015. This creates a single project in the Visual Studio Solution along with the necessary files in it to start.   Copy the complete js folder and the pages folder from the Windows 8.1 Navigation App that was created earlier and paste it into the root of the new Universal Project. Include them in the project from Visual Studio Solution Explorer, as shown in the Figure 7-8.  

现在,您已经准备好修改通用 Windows 应用来执行页面之间的导航。

从 Visual Studio 解决方案资源管理器中打开default.html页面,并用以下代码替换 body 部分。

<div id="contenthost" data-win-control="Application.PageControlNavigator" data-win-options="{home: '/pages/home/home.html'}"></div>

另外,在default.html页面中包含对navigator.js的引用。

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

default.html页面就像一个外壳,用于从pages文件夹中加载不同的页面控件。默认情况下,它从pages / home文件夹中加载home.html页面。

PageControlNavigator 控件具有一个 id,其内容主机值充当从页面控件加载的内容的主机。

下一步是向项目添加一个新的页面控件,这样用户就可以从home.html文件导航到新的页面控件。

Microsoft Visual Studio 提供了页面控件项模板,允许开发人员轻松创建页面控件。

在 Visual Studio 解决方案资源管理器中右击pages文件夹,并从上下文菜单中选择“添加➤新项”。在“添加新项”对话框中,选择“页面控制”并提供名称“page2.html”。单击添加按钮。

这会将三个文件添加到项目中:

  • page1.html
  • page1.css
  • page1.js

接下来显示了page1.html的内容。

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8" />

<title>page1</title>

<link href="page1.css" rel="stylesheet" />

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

</head>

<body>

<div class="page1 fragment">

<header class="page-header" aria-label="Header content" role="banner">

<button class="back-button" data-win-control="WinJS.UI.BackButton"></button>

<h1 class="titlearea win-type-ellipsis">

<span class="pagetitle">Welcome to page1</span>

</h1>

</header>

<section class="page-section" aria-label="Main content" role="main">

<p>The content of page 2</p>

</section>

</div>

</body>

</html>

page1.js文件的内容如下:

// For an introduction to the Page Control template, see the following documentation:

//http://go.microsoft.com/fwlink/?LinkId=232511

(function () {

"use strict";

WinJS.UI.Pages.define("/pages/page1.html", {

// This function is called whenever a user navigates to this page. It

// populates the page elements with the app's data.

ready: function (element, options) {

},

unload: function () {

},

updateLayout: function (element) {

}

});

})();

您将只修改page1.html的 body 标签,以表明导航到第 1 页是成功的。

现在,通用应用中有两个 HTML 页面。第一页是home.html,第二页是page1.html。如何在两个页面之间导航?

您可以使用WinJS.Navigate.navigate()方法在页面之间导航。

让我们通过添加下面的代码片段来修改home.html以包含一个导航到第 1 页的超链接。

<a id="lnkPage2"> Navigate to Page 2</a>

注意,超链接有id属性,但没有href属性。

打开home.js文件,用下面的代码片段替换它。

(function () {

"use strict";

WinJS.UI.Pages.define("/pages/home/home.html", {

// This function is called whenever a user navigates to this page. It

// populates the page elements with the app's data.

ready: function (element, options) {

var page2link = document.getElementById('lnkPage2');

page2link.addEventListener('click', function(eventargs) {

eventargs.preventDefault();

WinJS.Navigation.navigate("/pages/page1.html");

});

}

});

})();

该代码为页面的 ready 事件中的超链接设置 click 事件处理程序。click 事件处理程序首先调用preventDefault函数阻止正常的链接导航,然后调用WinJS.Navigation.navigate方法导航到page1.html

当您使用本地机器选项在 Windows 桌面上运行应用时,您会看到显示有超链接的主页,如图 7-9 所示。

A978-1-4842-0719-2_7_Fig9_HTML.jpg

图 7-9。

home.html page with a hyperlink to navigate to page 2

当点击导航到第 2 页链接时,用户被带到第 2 页,如图 7-10 所示。当您查看page2.html时,请注意后退按钮,它会将您带回上一页。

A978-1-4842-0719-2_7_Fig10_HTML.jpg

图 7-10。

Page1.html with the Back button

表 7-4 显示了WinJS.Navigation类中可用的一些方法。

表 7-4。

WinJS.Navigation Class

| 方法名称 | 描述 | | --- | --- | | WinJS。Navigation.back() | 此方法将用户导航回历史记录中的上一页。 | | WinJS。Navigation.forward() | 这种方法使开发人员能够在历史中向前导航。 | | WinJS。Navigation.navigate() | 该方法使开发人员能够导航到指定的页面。 |

八、全球化和本地化

有机会在近 240 个 Windows 市场中推广您的 Windows 应用。目标受众在文化、地区和语言方面各不相同。您的应用的用户可能位于世界上的任何地方。他们可能说不同的语言,甚至是多种语言。作为应用开发人员,您有必要使您的应用适应多种语言、市场、文化和地区。全球化意味着让您的应用了解文化、语言和地区。本地化是将您的应用的某些方面本地化到它正在运行的文化的能力;例如,日期、数字或货币格式等等。在这一章中,你将看到如何全球化和本地化你的应用的方法。

8.1 使用资源字符串

问题

当你的 app 在非英语文化中使用,文本不适应新的文化和语言;相反,用户看到的是英文文本。

解决办法

为了全球化应用,有必要使用资源字符串来代替静态文本。例如,应用中的任何标签文本都不应该硬编码为静态文本。相反,应该为您需要支持的文化/语言创建资源文件。添加将文本翻译成相应语言的字符串。标签文本也使用资源字符串,而不是硬编码。

它是如何工作的

让我们看看资源字符串在应用中的用法,以及如何添加它们。

Open Visual Studio 2015. Select File ➤ New Project ➤ JavaScript ➤ Windows ➤ Universal and select Blank App (Universal Windows) template. This will create a universal app with the necessary files that can run on machines powered by Windows 10.

A978-1-4842-0719-2_8_Fig1_HTML.jpg

图 8-1。

Choosing the Blank App (Universal Windows) template in the New Project window   Open the package.appxmanifest file from the project in Visual Studio solution explorer. Go to the Application tab and check that the default language is set to en-US. When a new project is created, the language is set to en-US by default (see Figure 8-2).

A978-1-4842-0719-2_8_Fig2_HTML.jpg

图 8-2。

Default language settings in the application manifest   Create a resource folder, as follows:

A978-1-4842-0719-2_8_Fig3_HTML.jpg

图 8-3。

Strings folder in the Windows project In the Solution Explorer, select the Universal Windows project and right-click on it. Select Add ➤ New Folder from the context menu.   Name the new folder strings. You will create different culture/language resource files inside this folder (see Figure 8-3).     Create a subfolder and an English resource file, as follows: Create a folder named en-US in the strings folder (created in step 3).   Right-click the en-US folder and select Add ➤ New Item.   Select Text File from the template and give the name resources.resjson for the file. It is recommended to use this default name when naming the resource file.   Replace the content of the file with the following content: { "greeting"          : "Hello World!", "_greeting.comment" : "Hello World Text comment", "farewell"          : "Goodbye", "_farewell.comment" : "A farewell comment." }   The resource file is nothing but a JSON file with key value pairs. Here, "greeting" and "farewell" identify the strings that will be displayed. The other keys— "_greeting.comment" and "_farewell.comment" are comments that describe the strings itself. It’s a good practice to have meaningful comments for all of your strings.   Use string resource identifiers to do the following: Open the default.js file in the project from the Visual Studio solution explorer. This file can be found in the js folder of the project.   Add the following line of code to the app.onactivated function: WinJS.Resources.processAll(); The completed code should look as follows: app.onactivated = function (args) {         if (args.detail.kind === activation.ActivationKind.launch) {             if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {                 WinJS.Resources.processAll();             } else {             }             args.setPromise(WinJS.UI.processAll());         }     };   To use the resource string in your markup, open the default.html file and add the following content in the body: <h2> <span data-win-res="{textContent: 'greeting'}"></span></h2> <h2> <span data-win-res="{textContent: 'farewell'}"></span> </h2>     Add additional language resource files: In the strings folder of the project, add a folder named de-DE. This is for German culture.   Add a resource file in the de-DE folder. Name the resource file resources.resjson. Replace the content of the file with the following code: {         "greeting"          : "Hallo Welt!",         "_greeting.comment" : " Hello World Text comment.",         "farewell"          : "Auf Wiedersehen",         "_farewell.comment" : "A farewell comment." }   Add another folder in the strings folder of the project and name it fr-FR. This is for French culture.   Add a resource file in the fr-FR folder. This resource file name should also be resources.resjson. Replace the content of the file with the following code: {         "greeting"          : "Salut tout le monde",         "_greeting.comment" : "Hello World Text comment.",         "farewell"          : "Au revoir",         "_farewell.comment" : "A farewell comment." }     Run the app, as follows: Build and run the app by pressing F5.   Greeting and farewell messages are displayed in a user’s preferred language, set on the device. Figure 8-4 shows the output on a Windows screen run using the Local Machine option.

A978-1-4842-0719-2_8_Fig4_HTML.jpg

图 8-4。

English culture resource strings display   Change the language setting on the device and run the app again. Figure 8-5 shows the same text displayed with the language set to French on the device.

A978-1-4842-0719-2_8_Fig5_HTML.jpg

图 8-5。

French culture resource strings display.    

请注意,在 Windows Mobile 上更改语言设置时,您必须重新启动设备。

8.2 格式化日期、时间、数字和货币

问题

当语言更改为非英语区域性/语言时,日期、时间、数字和货币显示英语区域性设置。

解决办法

应用中的静态文本内容可以通过使用资源字符串进行本地化。但是如果您的应用必须显示日期、时间、数字或货币,您就不能使用资源字符串。相反,您需要使用WinJS.Globalization名称空间,它提供了日期时间、数字和货币格式助手方法的功能。

它是如何工作的

格式化日期和时间

以下步骤解释了如何设置日期和时间的格式。

Use DateTimeFormatter in the Windows.Globalization.DateTimeFormatting namespace. Create a DateTimeFormatter instance with a template format. For a list of template formats, you can check the MSDN documentation at http://msdn.microsoft.com/en-us/library/windows/apps/windows.globalization.datetimeformatting.datetimeformatter.aspx .   Add the following markup to default.html file inside the body element: <h1>Formatting Demo</h1> <br /> <h3>Long Date:</h3> <h3 id="spnDate"></h3> <br /> <h3>Long Time:</h3> <h3 id="spnTime"></h3>   Modify the onactivated method in default.js as follows: app.onactivated = function (args) {         if (args.detail.kind === activation.ActivationKind.launch) {             if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {                 var lang = Windows.System.UserProfile.GlobalizationPreferences.languages[0];                 var shortDateFmt = new Windows.Globalization.DateTimeFormatting.DateTimeFormatter("longdate", [lang]);                 var shortTimeFmt = new Windows.Globalization.DateTimeFormatting.DateTimeFormatter("longtime",[lang]);                 var currentDateTime = new Date();                 var shortDate = shortDateFmt.format(currentDateTime);                 var shortTime = shortTimeFmt.format(currentDateTime);                 document.getElementById("spnDate").innerHTML = shortDate;                 document.getElementById("spnTime").innerHTML = shortTime;             } else {                 // TODO: This application has been reactivated from suspension.                 // Restore application state here.             }             args.setPromise(WinJS.UI.processAll());         } };   Run the app and verify the output (see Figure 8-6 and Figure 8-7).

A978-1-4842-0719-2_8_Fig7_HTML.jpg

图 8-7。

Formatted output for French culture

A978-1-4842-0719-2_8_Fig6_HTML.jpg

图 8-6。

Formatted output for English culture  

图 8-6 显示英语文化格式的日期,图 8-7 显示法语文化格式的日期。

格式化数字和货币

以下步骤解释了如何设置数字和货币的格式:

NumberFormatting should be used to display decimal and percentage numbers and currencies.   Add the following markup in default.html and append to the body: <h3>Number Format:</h3> <h3 id="spnCurrency"></h3>   Modify the onactivated method in default.js and append the following code:    var userCurrency = Windows.System.UserProfile                         .GlobalizationPreferences.currencies; var number = 23456.78 var currFmt = new Windows.Globalization.NumberFormatting                         .CurrencyFormatter(userCurrency[0],[lang]); var formattedCurrency = currFmt.format(number); document.getElementById("spnCurrency").innerHTML =                 formattedCurrency;   Run the app by pressing F5. Change the language settings on your phone or tablet to see the number and currency formatting in action (see Figure 8-8 and Figure 8-9).

A978-1-4842-0719-2_8_Fig9_HTML.jpg

图 8-9。

Date and currency formatted output in French culture

A978-1-4842-0719-2_8_Fig8_HTML.jpg

图 8-8。

Date and currency formatted output in English culture  

8.3 本地化 WinJS 控件

问题

当区域性或语言更改时,WinJS 控件不显示本地化字符串。

解决办法

通常,像labeltitle这样的控件属性被设置在标记中。这有点像对字符串值进行硬编码。因此,当区域性或语言更改时,控件不会适应,并按原样显示硬编码的字符串。通过使用资源文件,可以将 WinJS 控件属性绑定到本地化字符串。WinJS 提供了直接将控件属性绑定到资源文件中的资源键的灵活性。

它是如何工作的

下列步骤解释如何本地化 WinJS 控件:

Open Visual Studio 2015. Select File ➤ New Project ➤ JavaScript ➤ Windows ➤ Blank App (Universal Windows). This will create a universal app which is a single project that can run on devices powered by Windows 10.   Add resource files to the project (see Recipe 8.1 on how to add resource files).   Add the following string keys: { "ShowMoreTilesTitle"            : "Show more Tiles", "_ShowMoreTilesTitle.comment"   : "show more tiles toggle title", "VibrateOnCallTitle"            : "Vibrate on Call", "_VibrateOnCallTitle.comment"   : "vibrate on call toggle title", "ShowArtistTitle"            : "Show artist when playing music", "_ShowArtistTitle.comment"   : "Show artist when playing music                         toggle title", "ToggleOn"              : "Yes", "_ToggleOn.comment"     : "show more tiles toggle on label", "ToggleOff"             : "No", "_ToggleOff.comment"    : "show more tiles toggle off label" } Similarly, I have also added strings for other languages.   Add the following code to the default.js onactivated method: WinJS.UI.processAll().then(function(){      WinJS.Resources.processAll(); }); You need to call processAll() on the WinJS.Resource class because you will be referring to the resource strings in the markup.   Add the following markup to default.html inside the body element: <div> <h1>Controls Localization</h1> <br /> <div class="toggle" data-win-control="WinJS.UI.ToggleSwitch"      data-win-res="{winControl: {labelOn:'ToggleOn',                                  labelOff:'ToggleOff',                                  title:'ShowMoreTilesTitle'}}"      data-win-options=""> </div> <br /> <div class="toggle" data-win-control="WinJS.UI.ToggleSwitch"      data-win-res="{winControl: {labelOn:'ToggleOn',                                  labelOff:'ToggleOff',                                  title:'VibrateOnCallTitle'}}"      data-win-options=""> </div> <br /> <div class="toggle" data-win-control="WinJS.UI.ToggleSwitch"      data-win-res="{winControl: {labelOn:'ToggleOn',                                  labelOff:'ToggleOff',                                  title:'ShowArtistTitle'}}"      data-win-options=""> </div> </div> Notice that you use data-win-res and refer the resource strings by their key names, and assign it to a property of the control. Usually, it is of the following pattern: data-win-res="{winControl: {propertyName1:'resourceID1',                                 propertyName2:'resourceID2'}}"   Press F5 and run the app. Initially, you will see an English string used by the control. Change the Language in the device and then run the app again. Now it will pick up the appropriate resource file and use the appropriate resource strings (see Figure 8-9 and Figure 8-10).

A978-1-4842-0719-2_8_Fig11_HTML.jpg

图 8-11。

Formatted output in English culture

A978-1-4842-0719-2_8_Fig10_HTML.jpg

图 8-10。

Formatted output in French culture

九、数据存储和应用数据

本章概述了在 Windows 10 应用中存储应用数据的数据存储技术。您将了解应用设置、如何将应用设置存储和检索为应用数据,以及如何使用应用数据文件夹。

那么什么是 app 数据呢?特定于特定应用的数据称为应用数据。通常,应用数据是用户的偏好、应用的配置、应用的状态等等,这些都是可变的。

当您在设备上安装应用时,应用会在设备上创建应用数据。但是,当你从设备上移除或卸载应用时,会发生什么呢?卸载应用时,应用数据会从设备中移除。所以建议不要将用户的数据(比如用户的图片)存储为 app 数据。

应用创建的另一种数据是用户数据。用户数据由用户创建,例如文档、图片、视频文件、音频文件等等。

9.1 如何创建和删除本地应用数据设置容器

问题

您需要在 Windows 10 应用的应用数据存储中创建和删除本地应用数据存储容器。

解决办法

使用Windows.Storage.ApplicationData.current.LocalSettings.createContainer在本地应用数据存储中创建本地应用设置容器。

它是如何工作的

该容器允许您组织您的应用数据设置;例如,您可以创建一个名为Greeting_Data_Container的容器来存储应用的所有问候语。创建另一个名为UserPreference_Container的容器,为您的应用用户存储所有用户偏好设置。您可以在本地设置和漫游设置中添加容器。一个容器中还可以有另一个容器。通用 Windows 应用允许您创建多达 32 个嵌套级别的容器。

Create a new project using the Windows Universal (Blank App) template in Microsoft Visual Studio 2015. This creates a Windows Universal app, which can be run on PCs, tablets, and Windows Phones running Windows 10.   Open the default.html page from the project in Visual Studio Solution Explorer.   Define an HTML tag for a button within the <body> tag of default.html. <div id="titleBarContent" style="width: 100%;height:30px; background:border-box darkblue; overflow: hidden; z-index: 3">         <i>App Settings Admin Screen</i>     </div> <span> Click on the button to Create a local app data settings container</span>     <input type="button" value="Local Data Demo" id="btnLocalData" />    <h3><span id="msgspan"></span></h3> The complete default.html code will look like this: <!DOCTYPE html> <html> <head>     <meta charset="utf-8" />     <title>Recipe9.1</title>     <!-- WinJS references -->     <link href="WinJS/css/ui-dark.css" rel="stylesheet" />     <script src="WinJS/js/base.js"></script>     <script src="WinJS/js/ui.js"></script>     <!-- Recipe9.1 references -->     <link href="/css/default.css" rel="stylesheet" />     <script src="/js/default.js"></script> </head> <body class="win-type-body">     <div id="titleBarContent" style="width: 100%;height:30px; background:border-box darkblue; overflow: hidden; z-index: 3">         <i>App Settings Admin Screen</i>     </div>         <span> Click on the button to Create a local app data settings container</span>     <input type="button" value="Local Data Demo" id="btnCreateContainer" />    <h3><span id="msgspan"></span></h3> </body> </html>   Right-click the project’s js folder in Solution Explorer and add a js file in the project’s js folder by using the Add ➤ New JavaScript file. Provide a name for the file. In this example, let’s name the file DatastorageDemo.js.   Refer to this file in default.html by adding the following code to the <head> tag: <script src="js/DatastorageDemo.js"></script>   Add the following code to the newly created js file: (function () {     "use strict";     function GetControl() {         WinJS.UI.processAll().done(function () {   var localSettingsButton = document.getElementById("btnCreateContainer"); localSettingsButton.addEventListener("click", btnCreateContainerClick, false);         });     }     document.addEventListener("DOMContentLoaded", GetControl); })();  

这段代码获取按钮元素,并将一个事件侦听器绑定到按钮,当用户单击按钮时,该事件侦听器被触发。现在,使用下面的代码片段在DatastorageDemo.js文件中添加一个事件方法或函数。

function btnCreateContainerClick(mouseEvent) {

var applicationData = Windows.Storage.ApplicationData.current;

var localSettings = applicationData.localSettings;

// Creating a local settings container

var localSettingscontainer = localSettings.createContainer("App_GreetingDataContainer", Windows.Storage.ApplicationDataCreateDisposition.Always);

document.getElementById("msgspan").innerText = "Container Created, the name of the container is :" + localSettingscontainer.name;

}

这段代码声明了一个名为applicationData的变量。它被分配了Windows.Storage.ApplicationData.current,允许您访问 app 数据存储。应用数据存储可以存储应用设置和文件。下一行代码从变量ApplicationData中检索localSettings到变量localSettings

然后它调用createContainer方法来创建一个设置容器。前面的示例创建了一个设置容器App_greetingDatacreateContainer方法有两个参数:设置容器的名称和Windows.Storage.ApplicationDataCreateDisposition.Always,表示总是检查容器是否存在。如果存在,返回指定的容器;否则,创建一个新的。

您也可以使用localSettingscontainer.deleteContainer("<<name>>")方法删除容器。deleteContainer方法从应用数据存储中删除容器。

删除代码片段使用hasKey函数检查容器是否存在,然后使用deleteContainer函数通过传递容器键删除容器。

这里给出了DataStorageDemo.js的完整代码:

(function () {

"use strict";

function GetControl() {

WinJS.UI.processAll().done(function () {

var localSettingsButton = document.getElementById("btnCreateContainer");

localSettingsButton.addEventListener("click", btnCreateContainerClick, false);

});

}

document.addEventListener("DOMContentLoaded", GetControl);

})();

function btnCreateContainerClick(mouseEvent) {

var applicationData = Windows.Storage.ApplicationData.current;

var localSettings = applicationData.localSettings;

// Creating a local settings container

var localSettingscontainer = localSettings.createContainer("App_GreetingDataContainer", Windows.Storage.ApplicationDataCreateDisposition.Always);

document.getElementById("msgspan").innerText = "Container Created, the name of the container is :" + localSettingscontainer.name;

}

现在,让我们构建应用,并在 Windows 10 中运行它。

图 9-1 展示了一个按钮和一个h2标签的外观。当您单击本地数据演示按钮时,它执行btnSaveClick方法,该方法创建容器并显示一条带有容器名称的消息。

A978-1-4842-0719-2_9_Fig1_HTML.jpg

图 9-1。

Creating a local app data settings container

9.2 如何创建和读取本地应用数据设置

问题

您需要在应用数据存储中的应用数据设置容器中创建本地应用数据设置。

解决办法

使用ApplicationDataContainer.values在 Windows 10 应用的本地应用数据存储中的应用数据设置容器中创建本地应用数据设置。

它是如何工作的

应用设置是应用中的个性化数据或自定义设置。用户偏好——例如用户想要启用还是停用定位服务——是一个应用设置。通用 Windows 应用开发公开了 API,允许您在应用数据存储中将应用设置作为应用数据进行存储和检索。

app store 由两个不同的设置位置组成:本地设置和漫游设置。

Create a new project using the Windows Universal (Blank App) template in Microsoft Visual Studio 2015.   Open the default.html page from the project in Visual Studio Solution Explorer.   Define HTML tags for two text boxes, a button, and a <span> tag within the <body> tag of default.html. <div id="titleBarContent" style="width: 100%;height:30px; background:border-box darkblue; overflow: hidden; z-index: 3">   <i>App Settings Admin Screen (Use this Screen to configure app Settings)</i>  </div>     <br />     <i>Welcome Greeting in English</i> <input type="text" value="" id="txtWelcomeGreetingseng" /><br />     <i>Welcome Greeting in French</i><input type="text" value="" id="txtWelcomeGreetingsfr" /><br />   <input type="button" value="Create Local App Data Demo" id="btnLocalData" /> The complete default.html contains should look like this: <!DOCTYPE html> <html> <head>     <meta charset="utf-8" />     <title>Recipe9.2</title>     <!-- WinJS references -->     <link href="WinJS/css/ui-dark.css" rel="stylesheet" />     <script src="WinJS/js/base.js"></script>     <script src="WinJS/js/ui.js"></script>     <!-- Recipe9.2 references -->     <link href="/css/default.css" rel="stylesheet" />     <script src="/js/default.js"></script> </head> <body class="win-type-body">     <div id="titleBarContent" style="width: 100%;height:30px; background:border-box darkblue; overflow: hidden; z-index: 3">         <i>App Settings Admin Screen (Use this Screen to configure app Settings)</i>     </div>     <br />     <i>Welcome Greeting in English</i> <input type="text" value="" id="txtWelcomeGreetingseng" /><br />     <i>Welcome Greeting in French</i><input type="text" value="" id="txtWelcomeGreetingsfr" /><br />     <input type="button" value="Create Local App Data Demo" id="btnLocalData" /> </body> </html>   Right-click the project’s js folder in Solution Explorer and select Add ➤ New JavaScript file. Provide a name for the file. In this example, let’s name the file DataStorageDemo.js. (function () {     "use strict";     function GetControl() {         WinJS.UI.processAll().done(function () {             var submitbutton = document.getElementById("btnLocalData");             submitbutton.addEventListener("click", btnLocalDataClick, false);         }); GetCurrentSettingValues();     }     document.addEventListener("DOMContentLoaded", GetControl); })();   Now, add a function to retrieve the current greeting settings from the container: function GetCurrentSettingValues() {     var applicationData = Windows.Storage.ApplicationData.current;     var localSettings = applicationData.localSettings;     // Creating a local settings container     var containerExists = localSettings.containers.hasKey("App_GreetingDatalocalContainer");     if (containerExists) {         document.getElementById("txtWelcomeGreetingseng").value = localSettings.containers.lookup("App_GreetingDatalocalContainer").values["App_Heading_English"];         document.getElementById("txtWelcomeGreetingsfr").value = localSettings.containers.lookup("App_GreetingDatalocalContainer").values["App_Heading_French"];     } }  

此代码在应用加载时执行;它从容器中检索本地问候语设置。首次运行此应用时,您不会看到任何填充的值,但下次运行时,它会显示存储在容器中的设置。

现在,使用下面的代码片段在DataStorageDemo.js文件中添加一个事件方法或函数:

function btnLocalDataClick(mouseEvent) {

var applicationData = Windows.Storage.ApplicationData.current;

var localSettings = applicationData.localSettings;

// Creating a local settings container

var localSettingscontainer = localSettings.createContainer("App_GreetingDatalocalContainer", Windows.Storage.ApplicationDataCreateDisposition.Always);

if (!localSettingscontainer.values.hasKey("App_Heading_English")) {

localSettingscontainer.values["App_Heading_English"] = document.getElementById("txtWelcomeGreetingseng").value;

}

if (!localSettingscontainer.values.hasKey("App_Heading_French")) {

localSettingscontainer.values["App_Heading_French"] = document.getElementById("txtWelcomeGreetingsfr").value;

}

} //end of function btnLocalDataClick

这段代码创建了applicationDatalocalSettings变量来引用localSettings。之后,它使用createContainer函数创建一个名为App_GreetingDatalocalContainer的容器。然后,它使用localSettingscontainer.values.hasKey("App_Heading_English")来确保名为App_Heading_English的设置存在于应用数据的 localSettings 容器中。如果设置不存在,那么它使用localSettingscontainer.["name"]函数创建一个。它还通过使用以下代码行将文本框中的字符串值赋给它:

localSettingscontainer.values["App_Heading_English"] = document.getElementById("txtWelcomeGreetingseng").value;

对于法语标题的应用也是如此。

当您在 Windows 10 上运行该应用时,您会看到如图 9-2 所示的屏幕。

A978-1-4842-0719-2_9_Fig2_HTML.jpg

图 9-2。

Creating local app data settings for the greetings settings

9.3 如何创建和检索本地复合设置

问题

您需要在 Windows 10 应用的应用数据存储中创建和读取本地复合值。

解决办法

使用ApplicationDataCompositeValue类来创建和读取本地复合值。

它是如何工作的

Windows.Storage.ApplicationDataCompositeValue类允许您创建可存储在本地设置或漫游设置中的复合设置。复合值类允许您将名称值对作为应用数据存储在应用数据存储中。假设您想将图书信息存储为

book["ISBN"] = 12345;

book["BookTitle"] = "Windows 10 Development";

要存储复合信息,您可以使用如下所示的代码:

var applicationData = Windows.Storage.ApplicationData.current;

var localSettings = applicationData.localSettings;

// Create a BookInfo Composite setting

var bookInfo = new Windows.Storage.ApplicationDataCompositeValue();

bookInfo["ISBN"] = 12345;

bookInfo["Title"] = "Windows 10 Development";

localSettings.values["AppLocalSettings"] = bookInfo;

要从复合设置中读取数据,可以使用如下所示的代码:

var bookInfo = localSettings.values["AppLocalSettings"];

if (bookInfo)

{

var ISBN = bookInfo["ISBN"];

var Title = bookInfo["Title"]

}

9.4 如何创建漫游应用数据存储容器

问题

您需要在 Windows 10 应用的应用数据存储中创建一个漫游应用数据存储容器。

解决办法

使用roamingSettings.createContainer在应用数据存储中创建漫游应用设置容器。

它是如何工作的

这和你在 app data store 的本地设置中看到的非常相似,但是你没有使用Windows.Storage.ApplicationData.current.LocalSettings,而是使用了Windows.Storage.ApplicationData.current.roamingSettings

Create a new project using the Windows Universal (Blank App) template in Microsoft Visual Studio 2015.   Open the default.html page from the project in Visual Studio Solution Explorer. Add the following HTML markup within the body tag of default.html: <body class="win-type-body" style="background-color:goldenrod"> <div id="titleBarContent" style="width: 100%;height:30px; background:border-box darkblue; overflow: hidden; z-index: 3">         <i>User Preferences Settings Screen (These settings will be available across devices)</i>     </div>    <br />     <input type="button" value="Save User Preference" id="btnUserPreferenceRoamingData" />     <br />     <span id="spantoDisplayRoamingData" style="color:white"></span> </body>   Right-click the project’s js folder in the Solution Explorer and select Add ➤ New JavaScript file. Provide the name for the file. In this example, let’s name the file DatastorageDemo.js. (function () {     "use strict";     function GetControl() {         WinJS.UI.processAll().done(function () {             var submitbutton = document.getElementById("btnUserPreferenceRoamingData");             submitbutton.addEventListener("click", btnCreateRoamingContainerClick, false);         });     }     document.addEventListener("DOMContentLoaded", GetControl); })();   Now, add an event method or function using the following code snippet in the Datastoragedemo.js file: function btnCreateRoamingContainerClick(mouseEvent) { var ApplicationData = Windows.Storage.ApplicationData.current; var roamingSettings = ApplicationData.roamingSettings; // Creating a local settings container if (!roamingSettings.containers.hasKey("UserPreference_RoamingProfile")) { var roamingSettingscontainer = roamingSettings.createContainer("UserPreference_RoamingProfile", Windows.Storage.ApplicationDataCreateDisposition.Always); document.getElementById("spantoDisplayRoamingData").innerText =roamingSettingscontainer.name +" User Preference Roaming App Data Container Created!!";    } }  

这段代码声明了一个名为ApplicationData的变量。它被分配了Windows.Storage.ApplicationData.current,这表示应用数据存储中应用设置的容器。下一行从ApplicationData中检索roamingSettings,并将其存储在roamingSettings变量中。

然后它调用roamingSettings.containers.hasKey函数来检查容器是否存在。如果它不存在,那么它使用createContainer方法创建一个设置容器。前面的示例创建了一个设置容器App_GreetingDataRoamingContainer,并将其存储在roamingSettingscontainer中。createContainer方法有两个参数:设置容器的名称和Windows.Storage.ApplicationDataCreateDisposition.Always,它指示总是检查容器是否存在;如果存在,返回指定的容器;否则,创建一个新的。

当您在 Windows 10 模拟器或 Windows Mobile 上运行该应用时,它应该如图 9-3 所示。

A978-1-4842-0719-2_9_Fig3_HTML.jpg

图 9-3。

The form to save user preference settings using roaming app data settings

太好了。在前面的代码片段中,您学习了roamingSettings对象的createContainerdeleteContainer方法。

9.5 如何创建和读取漫游应用数据设置

问题

您需要在 Windows 10 应用的应用数据存储中的漫游应用数据设置容器中创建和读取漫游应用数据设置。

解决办法

使用ApplicationDataContainer.values在 Windows 10 应用的应用数据商店的应用数据设置容器中创建漫游应用数据设置。

它是如何工作的

Create a new project using the Windows Universal (Blank App) template in Microsoft Visual Studio 2015.   Open the default.html page from the project in Visual Studio Solution Explorer.   Add a form control to the body section of the page. The form control has a few div controls to display the title bar, input control for email, and a toggle switch control for mobile data and location services. <body class="win-type-body">     <div id="titleBarContent" style="width: 100%;height:30px; background:border-box darkblue; overflow: hidden; z-index: 3">         <i>User Preferences Settings Screen (These settings will be available across devices)</i>     </div>     <br />     <form id="form1">         <fieldset class="formSection">             <legend class="formSectionHeading">User Preferenses</legend>             <div class="twoColumnFormContainer">                 <div id="UserMobileData" data-win-control="WinJS.UI.ToggleSwitch" data-win-options="{title :'Use Mobile Data',labelOff: 'Disabled',belOn:'Enabled',hecked: true}">                 </div>                 <br />                 <div id="LocationServices" data-win-control="WinJS.UI.ToggleSwitch" data-win-options="{title :'Location Services',labelOff: 'Disabled',belOn:'Enabled',hecked: true}">                 </div>                 <br />                 <div id="msg" style="color:red"></div>                 <br />                 <div class="buttons">                         <button type="submit" id="btnSubmit" class="horizontalButtonLayout win-button">                             Submit                         </button>                 </div>             </div>         </fieldset>     </form>     <br /> </body>   Right-click the project in Solution Explorer and select Add ➤ New JavaScript file and provide the name for the file. In this example, let’s name the file DataStorageDemo.js.   Add the following code to the DataStorageDemo.js file: (function () {     "use strict";     function GetControl() {         WinJS.UI.processAll().done(function () {             var submitbutton = document.getElementById("btnSubmit");             submitbutton.addEventListener("click", btnCreateRoamingContainerClick, false);         });     }     document.addEventListener("DOMContentLoaded", GetControl); })();  

前面的代码向btnSubmit按钮添加了一个事件接收器。现在添加一个btnCreateRoamingContainerClick函数,当用户单击用户首选项表单控件上的提交按钮时触发该函数。

function btnCreateRoamingContainerClick(mouseEvent) {

var ApplicationData = Windows.Storage.ApplicationData.current;

var roamingSettings = ApplicationData.roamingSettings;

// Creating a local settings container

if (roamingSettings.containers.hasKey("UserPreference_RoamingProfile")) {

var roamingSettingscontainer = roamingSettings.createContainer("UserPreference_RoamingProfile", Windows.Storage.ApplicationDataCreateDisposition.Always);

var toggleMobileDataButton = document.getElementById("UserMobileData").winControl;

if (toggleMobileDataButton.checked) {

roamingSettingscontainer.values["UseMobileData"] = "Yes";

}

else {

roamingSettingscontainer.values["UseMobileData"] = "No";

}

var toggleLocationDataButton = document.getElementById("LocationServices").winControl;

if (toggleLocationDataButton.checked) {

roamingSettingscontainer.values["LocationServices"] = "Yes";

}

else {

roamingSettingscontainer.values["LocationServices"] = "No";

}

document.getElementById("msg").innerText = "Data Saved!!";

}

}//end of function btnCreateRoamingContainerClick

这段代码声明了一个名为ApplicationData的变量。它被分配了Windows.Storage.ApplicationData.current,表示应用数据存储中应用设置的容器。下一行从ApplicationData中获取roamingSettings,并将其存储在roamingSettings变量中。

然后它调用roamingSettings.containers.hasKey函数来检查容器是否存在。如果它不存在,它使用createContainer方法创建一个设置容器。前面的示例创建了一个设置容器App_GreetingDataRoamingContainer,并将其存储在roamingSettingscontainer中。createContainer方法有两个参数:设置容器的名称和Windows.Storage.ApplicationDataCreateDisposition.Always,它指示总是检查容器是否存在。如果存在,返回指定的容器;否则,创建一个新的。

下一行代码检查切换开关控件的值,基于它是否被选中。如果切换开关选择“是”,它将使用roamingSettingscontainer.values["UseMobileData"]功能存储漫游数据应用设置,该功能使用container.value方法设置值。它也为位置服务拨动开关做同样的事情。

DataStorageDemo.js文件中的完整代码如下所示:

(function () {

"use strict";

function GetControl() {

WinJS.UI.processAll().done(function () {

var submitbutton = document.getElementById("btnSubmit");

submitbutton.addEventListener("click", btnCreateRoamingContainerClick, false);

});

}

document.addEventListener("DOMContentLoaded", GetControl);

function btnCreateRoamingContainerClick(mouseEvent) {

var ApplicationData = Windows.Storage.ApplicationData.current;

var roamingSettings = ApplicationData.roamingSettings;

// Creating a local settings container

if (roamingSettings.containers.hasKey("UserPreference_RoamingProfile")) {

var roamingSettingscontainer = roamingSettings.createContainer("UserPreference_RoamingProfile", Windows.Storage.ApplicationDataCreateDisposition.Always);

var toggleMobileDataButton = document.getElementById("UserMobileData").winControl;

if (toggleMobileDataButton.checked) {

roamingSettingscontainer.values["UseMobileData"] = "Yes";

}

else {

roamingSettingscontainer.values["UseMobileData"] = "No";

}

var toggleLocationDataButton = document.getElementById("LocationServices").winControl;

if (toggleLocationDataButton.checked) {

roamingSettingscontainer.values["LocationServices"] = "Yes";

}

else {

roamingSettingscontainer.values["LocationServices"] = "No";

}

document.getElementById("msg").innerText = "Data Saved!!";

}

}//end of function btnCreateRoamingContainerClick

})();

就这样。漫游数据将在用户访问此 Win 10 应用的所有设备上可用。

当您在 Windows 10 模拟器或 Windows Mobile 上运行该应用时,它将如图 9-4 所示。

A978-1-4842-0719-2_9_Fig4_HTML.jpg

图 9-4。

Form to save user preference settings using roaming app data settings

9.6 如何注册数据变更事件

问题

您需要注册并实现一个数据更改事件处理程序。

解决办法

您可以使用ApplicationData.DataChanged事件注册漫游数据的数据更改事件。

它是如何工作的

当漫游数据在其中一个设备上更改时,通用 Windows 应用会将漫游数据复制到云,然后将漫游数据同步到用户安装了应用的其他设备。假设在同步之后,您希望确保基于漫游数据更新应用数据。例如,您将用户首选项存储在漫游数据中,当用户首选项更改时,您希望根据新的首选项更改用户设备上的设置。嗯,通用 Windows 应用允许您注册一个事件,该事件将在应用数据刚刚完成从云同步后执行。

您可以使用ApplicationData.DataChanged事件来注册一个事件。所以我们来实现这个。

Create a new project using the Windows Universal (Blank App) template in Microsoft Visual Studio 2015. This creates a Windows Universal app that can run on Windows tablets and Windows Phone running Windows 10.   Open the default.html page from the project in Visual Studio Solution Explorer.   Define an HTML div tag for the app title bar.  

<body class="win-type-body">

<div id="titleBarContent" style="width: 100%;height:30px; background:border-box darkblue; overflow: hidden; z-index: 3">

<i>Custom title bar</i>

</div>

</body>

完整的default.html代码将如下所示:

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8" />

<title>_9.7</title>

<!-- WinJS references -->

<link href="WinJS/css/ui-dark.css" rel="stylesheet" />

<script src="WinJS/js/base.js"></script>

<script src="WinJS/js/ui.js"></script>

<!-- _9.7 references -->

<link href="/css/default.css" rel="stylesheet" />

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

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

</head>

<body class="win-type-body">

<div id="titleBarContent" style="width: 100%;height:30px; background:border-box darkblue; overflow: hidden; z-index: 3">

<i>Custom title bar</i>

</div>

</body>

</html>

在解决方案资源管理器中右键单击该项目,然后选择“添加➤新 JavaScript 文件”。提供文件的名称。在本例中,我们将文件命名为 DatastorageDemo.js。

js文件中添加以下代码。代码首先向applicationData对象注册一个数据更改事件。

(function () {

"use strict";

function GetControl() {

WinJS.UI.processAll().done(function () {

});

}

document.addEventListener("DOMContentLoaded", GetControl);

var applicationData = Windows.Storage.ApplicationData.current;

applicationData.addEventListener("datachanged", datachangeHandler);

function datachangeHandler(eventArgs) {

var applicationData = Windows.Storage.ApplicationData.current;

var roamingSettings = applicationData.roamingSettings;

// Creating a roaming settings container

var roamingSettingscontainer = roamingSettings.containers ("App_GreetingDataContainer");

//Read the Data again

document.getElementById("titleBarContent").innerText = roamingSettingscontainer.values["App_Heading_English"];

}

})();

这段代码还在同一个js文件中定义了 data changed 事件处理程序。datachangeHandler事件通过使用loopup函数从applicationData对象中检索漫游容器。然后它读取名为App_Heading_English的漫游设置,并将其分配给标题栏的div选项卡。

9.7 如何创建、写入和读取本地文件

问题

您需要在 Windows 10 应用的应用数据存储中创建文件、写入文件和读取文件。

解决办法

使用Windows.Storage.StorageFile类进行文件处理。

它是如何工作的

Windows.Storage.StorageFile类为通用 Windows 应用中的文件处理提供了必要的方法。您创建的文件可以存储在文件夹、库(图片库)和通用 Windows 应用支持的网络位置中,如 OneDrive 等。

本地文件夹

众所周知,通用的 Windows 应用可以在许多设备上运行:Windows Mobile、运行 Windows 10 的个人电脑、微软 Surface 平板电脑等等。因此,当您在 Windows 应用商店中部署应用时,您的应用用户可以在一台或多台设备上下载并运行您的应用。那么如何在特定设备上管理特定于你的应用的应用设置呢?你可以使用本地文件夹。本地文件夹可用于在特定设备上存储本地应用数据。本地文件夹数据可用于存储该数据的设备。它不能与其他设备同步。通用 Windows 应用将应用数据存储在应用包的LocalState中。

漫游文件夹

现在,假设您想要存储应用的用户首选项,并且想要确保相同的用户首选项在特定用户的所有设备上都可用。嗯,你可以通过使用漫游文件夹来实现这一点。它将应用数据存储在应用包的RoamingState中。当应用用户在多个设备上运行应用时,存储在漫游文件夹中的应用数据会在设备之间同步。

临时文件夹

第三种类型的位置是临时文件夹,它允许您短期存储应用数据。储存在临时文件夹中的应用数据一旦不再使用,就会被删除。所以总是使用这个文件夹来存储不太重要的数据。通用 Windows 应用将应用数据存储在应用包的TemporaryState中。

StorageFolder类公开了许多创建文件、从文件中读取内容以及将内容写入文件的方法。让我们来看看每一种方法。

方法在指定的位置创建一个新文件。如果文件已经存在,它将覆盖该文件。

使用该函数的语法如下:

storageFolder.createFileAsync(desiredName).done(function CreationSuccess(newFileObj))

{

/* Success call back */

}, CreationFailed(error))

{

Failed call back */

});

如您所见,createFileAsync方法必须使用一个成功的回调函数和一个失败的回调函数来定义。

在本地文件夹中创建文件的完整代码如下:

function btnDataToFileClick(mouseEvent) {

var ApplicationData = Windows.Storage.ApplicationData.current;

var localFolder = ApplicationData.localFolder;

var newFilePromise = localFolder.createFileAsync("MyFile.txt");

newFilePromise.done(

function (file) {

// WinJS.log("The file MyFile.txt was created.", "sample", "status");

WinJS.log && WinJS.log("The file MyFile.txt was created.", "sample", "status");

},

function (error) {

});

}

当你在你的 Windows 10 本地设备上运行这个应用时,会在本地文件夹中创建一个名为MyFile.txt的文件,如图 9-5 所示。

A978-1-4842-0719-2_9_Fig5_HTML.jpg

图 9-5。

A file created in local folder

本地文件夹位置为C:\Users\<<UserName>>\AppData\Local\Packages\5342d954-ee4c-413c-8ed8-41b3befa6afc_khbmmdtkmdajr\LocalState。这个文件是空的,因为我们还没有写入这个文件。

太好了。现在让我们编写代码将内容写入文件。为此,我们首先在default.html文件中添加一个文本区域框。default.html的 body 标签看起来像这样:

<body class="win-type-body" style="background-color:goldenrod">

<div id="titleBarContent" style="width: 100%;height:30px; background:border-box darkblue; overflow: hidden; z-index: 3">

<i>File Handling Demo</i>

</div>

<br />

<input type="button" value="Save Data to File" id="btnCreateAFile" />

<textarea rows="10" cols="100" id="textarea" class="win-textarea"></textarea>

<input type="button" value="Save Data to File" id="btnDataToFile" />

</body>

这段代码向default.html文件添加了一个文本区域,以及名为btnDataToFilebtnCreateaFile的按钮。

在解决方案资源管理器中右键单击项目的 js 文件,然后选择“添加➤新 JavaScript 文件”。提供文件的名称。在本例中,我们将文件命名为 DatastorageDemo。js。

(function () {

"use strict";

function GetControl() {

WinJS.UI.processAll().done(function () {

var submitbutton = document.getElementById("btnCreateAFile");

submitbutton.addEventListener("click", btnCreateAFileClick, false);

var submitbutton = document.getElementById("btnDataToFile");

submitbutton.addEventListener("click", btnDataToFileClick, false);

});

}

document.addEventListener("DOMContentLoaded", GetControl);

})();

这段代码将事件接收器添加到两个按钮控件中。现在在 DatastorageDemo.js 文件中添加一个事件代码。

function btnCreateAFileClick(mouseEvent) {

var ApplicationData = Windows.Storage.ApplicationData.current;

var LocalFolder = ApplicationData.localFolder;

var newFilePromise = LocalFolder.createFileAsync("MyFile.txt");

newFilePromise.done(

function (file) {

// WinJS.log("The file MyFile.txt was created.", "sample", "status");

WinJS.log && WinJS.log("The file MyFile.txt was created.", "sample", "status");

},

function (error) {

});

}

这段代码用于在本地文件夹中创建一个新文件。您不会为btnDataToFile的点击事件向DataStorageDemo.js文件添加另一个事件。

function btnDataToFileClick(mouseEvent) {

var ApplicationData = Windows.Storage.ApplicationData.current;

var LocalFolder = ApplicationData.localFolder;

// Open sample file.

var FilePromise = LocalFolder.getFileAsync("MyFile.txt");

FilePromise.then(function (file) {

// If file found …

if (file) {

// Write to file.

var txtarea = document.getElementById("textarea").innerText;

Windows.Storage.FileIO.writeTextAsync(file, txtarea).then(function (contents) {

WinJS.log && WinJS.log("The text was wrttien to file MyFile.txt.", "sample", "status");

});

}

}, function (error) {

// Handle error.

});

}

这段代码通过使用本地文件夹的getFileAsync方法获取文件。如果文件存在,那么它接下来使用Windows.Storage.FileIO.writeTextAsync.writeTextAsync方法将内容写入文件。

当您在 Visual Studio 中使用本地设备运行此应用时,您会看到默认屏幕,如图 9-6 所示。

A978-1-4842-0719-2_9_Fig6_HTML.jpg

图 9-6。

File handling demo’s default screen

“创建文件”按钮会在运行应用的设备的本地文件夹中创建一个文件。创建文件后,在文本区域键入一些文本。单击将数据保存到文件将数据保存到文件。当您在记事本中打开该文件时,您会看到该文件的内容,如图 9-7 所示。

A978-1-4842-0719-2_9_Fig7_HTML.jpg

图 9-7。

The file on the local holder with the content

类似地,您可以使用readTextAsync方法来读取文件的内容。从文件中读取内容的代码如下:

// Read from the file.

Windows.Storage.FileIO.readTextAsync(file).then(function (contents) {

document.getElementById("textarea1").innerText = contents;

});

您可以通过引用正确的位置,以类似的方式在任何位置创建文件,如本地文件夹、漫游文件夹等。

十、共享数据

本章介绍如何在通用 Windows 平台应用之间共享数据。共享数据使开发人员能够构建允许用户共享信息、复制/粘贴以及从一个应用拖放到另一个应用的功能。例如,用户可能想要从一个应用中复制一些文本或图像,并将其粘贴到另一个应用中。用户也寻找共享信息/社交网站的链接。

Windows 10 应用支持以两种不同的方式共享数据。

  • 源应用允许用户将应用中的数据共享给其他应用。
  • 目标应用允许用户将一个应用声明为目标应用,该应用可以从其他应用检索共享数据。声明为目标应用的应用允许用户在共享数据时选择它作为目标应用。

通过使用共享数据选项,用户可以与其他应用共享纯文本、网络链接、照片和视频。在本章中,您将学习如何实现从源应用共享数据和声明目标应用。

共享联系人

为了共享数据或接收数据,每个应用都需要作为源应用或目标应用实现共享契约。数据共享包括以下三个部分:

  • 源 app:源 app 实现共享契约作为源 app 共享数据。此应用注册数据传输管理器,并填充要共享的数据包。
  • Share broker:Share broker 是 Share charm,支持源应用和目标应用之间的通信。
  • 目标应用:目标应用作为目标应用实现共享契约,以接收其他应用共享的数据。

10.1 为源应用的共享选项设置事件处理程序

问题

您需要为源应用的共享选项设置事件处理程序。

解决办法

你需要使用Windows.ApplicationModel.DataTransfer.DataTransferManagerdatarequested事件监听器。

它是如何工作的

当用户调用共享时,Windows.ApplicationModel.DataTransfer.DataTransferManager类的datarequested事件监听器方法被触发。此事件可以由用户操作触发,也可以在特定情况下自动触发;例如,当用户完成一项调查,应用想要分享调查结果。

要用DataTransferManager对象注册事件处理程序,可以使用下面的代码:

var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();

dataTransferManager.addEventListener("datarequested", dataRequested_Share);

在前面的代码中,您做的第一件事是使用DataTransferManager.getForCurrentView()函数获取当前页面的DataTransferManager对象。

接下来的一行代码将为datarequested事件添加一个事件监听器到DataTransferManager对象中。在前面的代码中,dataRequested_Share是监听器,datarequested是事件。然后,您可以定义接收方,如下面的代码所示。

function dataRequested_Share () {

// Your code for the sharing data

}

让我们使用前面的代码创建一个通用的 Windows 应用,为源应用的共享选项设置事件处理程序。

在 Microsoft Visual Studio 2015 中使用 Windows 通用(空白应用)模板创建一个新项目。

在解决方案资源管理器中右键单击项目的js文件夹,并选择添加➤新 JavaScript 文件。提供文件的名称。在本例中,我们将文件命名为 DataSharingDemo。js。

(function () {

"use strict";

function GetControl() {

WinJS.UI.processAll().done(function () {

var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();

dataTransferManager.addEventListener("datarequested", shareDataHandler);

});

}

document.addEventListener("DOMContentLoaded", GetControl);

})();

function shareDataHandler(e) {

// Your code to share the text, image etc

}

前面的代码使用DataTransferManager.getForCurrentView()函数获取当前页面的DataTransferManager对象,然后向其添加一个事件侦听器,如前所述。

现在,将DataSharingDemo.js引用添加到default.html,如下所示:

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8" />

<title>_10.1</title>

<!-- WinJS references -->

<link href="WinJS/css/ui-dark.css" rel="stylesheet" />

<script src="WinJS/js/base.js"></script>

<script src="WinJS/js/ui.js"></script>

<!-- _10.1 references -->

<link href="/css/default.css" rel="stylesheet" />

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

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

</head>

<body class="win-type-body">

<p>Content goes here</p>

</body>

</html>

10.2 将纯文本数据共享给其他应用

问题

你需要开发一个功能,允许用户将纯文本数据分享给其他 Windows 10 应用。

解决办法

使用request.data.setTextrequest.Data.Properties.description共享数据。request.data.title 是必填字段。

它是如何工作的

在前面的菜谱中,您了解到当用户使用 Share charm 开始共享会话时,会触发datarequested事件监听器。所以现在我们将在监听器中编写代码来共享数据。侦听器接受一个参数,该参数是事件参数。首先,您需要从事件参数中检索请求对象,如下所示:

function shareDataHandler(e) {

//Start your code to share data

var request = e.request;

}

您需要为请求对象设置属性。为了共享纯文本,您需要填充以下属性:

request.Data.Properties.Title = "Title of the Data";

request.Data.Properties.Description = "Description of the Data";

太好了,看起来很简单,不是吗?让我们实现它,看看在应用之间共享数据的现场演示。

在 Microsoft Visual Studio 2015 中使用 Windows 通用(空白应用)模板创建一个新项目。

首先,将下面的代码添加到default.html中。您需要用以下代码替换 body 标记:

<body class="win-type-body" style="background-color:white">

<div>

<p style="color:black">Enter Project Title:</p>

<input class="text-box" id="txttitle" value="Project Title" size="40"/>

<p style="color:black">Enter Project Description:</p>

<input class="text-box" id="txtdesc" value="Project Desc" size="40" />

<p style="color:black">Enter Project Weekly Summary:</p>

<textarea id="txttext" maxlength="1000" style="width:50%">Weekly Status: Here is the weekly status</textarea>

<br />

<br />

</div>

</body>

这段代码为共享数据演示定义了一个用户界面。用户界面如图 10-1 所示。

A978-1-4842-0719-2_10_Fig1_HTML.jpg

图 10-1。

Sharing Data user interface

现在,在解决方案浏览器中右键单击项目的js文件夹,并选择添加➤新 JavaScript 文件。提供文件的名称。在本例中,我们将文件命名为 DataSharingDemo.js。

(function () {

"use strict";

function GetControl() {

WinJS.UI.processAll().done(function () {

var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();

dataTransferManager.addEventListener("datarequested", shareDataHandler);

});

}

document.addEventListener("DOMContentLoaded", GetControl);

})();

还将以下代码添加到default.html中,以引用DataSharingDemo.js文件:

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

这样,您就有了想要共享的数据的用户界面。您还为datarequested事件添加了事件监听器。现在,共享数据特性最重要的部分是实现填充请求对象的代码。因此,将函数shareDataHandler的以下代码添加到DataSharingDemo.js文件中。

function shareDataHandler(e) {

var request = e.request;

// The request.data.properties.title is mandatory

var ShareInfoTitle = document.getElementById("txttitle").value;

if (ShareInfoTitle !== "") {

var ShareInfoDesc = document.getElementById("txtdesc").value;

if (ShareInfoDesc !== "") {

request.data.properties.title = ShareInfoTitle;

request.data.properties.description = ShareInfoDesc;

var ShareInfoFullText = document.getElementById("txttext").value;

if (ShareInfoFullText !== "") {

request.data.setText(ShareInfoFullText);

}

} else {

request.failWithDisplayText("Please Enter the Project Details you would like to share.");

}

} else {

request.failWithDisplayText("Error Occured!!");

}

}

前面的代码首先从事件参数中检索请求对象。该函数填充如下:

request.data.properties.title = ShareInfoTitle;

request.data.properties.description = ShareInfoDesc;

最后,添加以下代码:

request.data.setText(ShareInfoFullText);

这个函数检查标题是否为空,因为请求的 title 属性是必需的。

当您在 Windows 10 上运行该应用时,您会看到如图 10-2 所示的屏幕。

A978-1-4842-0719-2_10_Fig2_HTML.jpg

图 10-2。

Sharing Data demo

现在点击共享图标或按下 Windows 徽标键A978-1-4842-0719-2_10_Figa_HTML.jpg + H。您将看到如图 10-3 所示的屏幕。

A978-1-4842-0719-2_10_Fig3_HTML.jpg

图 10-3。

The Share charm windows showing the project title as a sharing data object

现在,从列表中单击 OneNote。你会看到一个屏幕,如图 10-4 所示。

A978-1-4842-0719-2_10_Fig4_HTML.jpg

图 10-4。

OneNote showing the shared data from the Universal Windows app

10.3 共享其他应用的网络链接

问题

你需要开发一个功能,允许用户与其他 Windows 10 应用共享网页链接。

解决办法

使用request.data.setWebLink以及标题和描述字段。

它是如何工作的

要共享 web 链接,需要使用request对象的request.data.setWebLink()方法。

在 Microsoft Visual Studio 2015 中使用 Windows 通用(空白应用)模板创建一个新项目。

首先,将下面的代码添加到default.html中。您需要用以下代码替换 body 标记:

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8" />

<title>_10.3</title>

<!-- WinJS references -->

<link href="WinJS/css/ui-dark.css" rel="stylesheet" />

<script src="WinJS/js/base.js"></script>

<script src="WinJS/js/ui.js"></script>

<!-- _10.3 references -->

<link href="/css/default.css" rel="stylesheet" />

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

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

</head>

<body class="win-type-body" style="background-color:white">

<div>

<p style="color:black">Enter Project Title:</p>

<input class="text-box" id="txttitle" value="Project Title" size="40" />

<p style="color:black">Enter Project Description:</p>

<input class="text-box" id="txtdesc" value="Project Desc" size="40" />

<p>Enter Project Site URL</p>

<input class="text-box" id="txtURL" value="Enter Project Site URL" size="40" />

<br />

<br />

</div>

</body>

</html>

现在,在解决方案资源管理器中右键单击项目的js文件夹,并选择添加➤新 JavaScript 文件。提供文件的名称。在本例中,我们将文件命名为 DataSharingDemo。js。

(function () {

"use strict";

function GetControl() {

WinJS.UI.processAll().done(function () {

var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();

dataTransferManager.addEventListener("datarequested", shareDataHandler);

});

}

document.addEventListener("DOMContentLoaded", GetControl);

})();

还将以下代码添加到default.html中,以引用DataSharingDemo.js文件:

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

DataSharingDemo.js增加一个新的shareDataHandler函数。完整的shareDataHandler函数如下所示。下面使用request.data.setWebLink()功能来设置正在共享的网页链接。

function shareDataHandler(e) {

var request = e.request;

// The request.data.properties.title is mandate

var ShareInfoTitle = document.getElementById("txttitle").value;

if (ShareInfoTitle !== "") {

var ShareInfoDesc = document.getElementById("txtdesc").value;

if (ShareInfoDesc !== "") {

request.data.properties.title = ShareInfoTitle;

request.data.properties.description = ShareInfoDesc;

var ShareURL = document.getElementById("txtURL").value;

if (ShareURL !== "") {

request.data.setWebLink(new Windows.Foundation.Uri(ShareURL));

}

} else {

request.failWithDisplayText("Please Enter the Project Details you would like to share.");

}

} else {

request.failWithDisplayText("Error Occured!!");

}

}

request.data.setWebLink()方法接受一个 URI 类型的参数,这就是为什么在把它传递给函数之前,从字符串到Windows.Foundation.Uri的转换是必要的。

10.4 将图片分享给其他应用

问题

你需要开发一个功能,允许用户与其他 Windows 10 应用共享图像。

解决办法

使用request.data.setStorageItemsrequest.data.setBitmap以及标题和描述字段。您也可以使用这两种方式来共享图像,因为您不知道目标应用支持哪种方式。

它是如何工作的

通用 Windows 应用还允许您与其他应用共享图像。您可以允许用户从本地设备选择图像,然后使用“共享”功能与其他应用共享。让我们看看图片分享的实际应用。

首先,将下面的代码添加到default.html中。您需要用以下代码替换 body 标记:

<body class="win-type-body" style="background-color:white">

<div>

<p style="color:black">Enter Project Title:</p>

<input class="text-box" id="txttitle" value="Project Title" size="40" />

<p style="color:black">Enter Project Description:</p>

<input class="text-box" id="txtdesc" value="Project Desc" size="40" />

<p>Select the image to share</p>

<button class="action" id="selectImageButton">Select image</button>

<br />

<br />

</div>

<div class="imageDiv">

<img class="imageHolder" id="imageHolder" alt="image holder" src="" />

</div>

</body>

前面的代码定义了一个带有图像拾取器按钮的共享图像的用户界面。

现在在DataSharingDemo.js文件中添加以下函数。

(function () {

"use strict";

var objimgFile;

function GetControl() {

WinJS.UI.processAll().done(function () {

var objimgFile = null;

var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();

dataTransferManager.addEventListener("datarequested", shareDataHandler);

document.getElementById("selectImageButton").addEventListener("click", selectImage, false);

});

}

document.addEventListener("DOMContentLoaded", GetControl);

})();

前面的代码向selectImageButton控件添加了一个监听器,如粗体行所示。

除了添加一个侦听器之外,它还声明了一个名为objimgFile的变量,该变量保存您将在下面的函数中使用的图像对象。

添加一个selectImage函数,它是 Select Image 按钮的 click 事件的监听器。该功能使用FileOpenPicker方法,允许用户从设备中选择图像。

function selectImage() {

var picker = new Windows.Storage.Pickers.FileOpenPicker();

picker.fileTypeFilter.replaceAll([".jpg", ".bmp", ".gif", ".png"]);

picker.viewMode = Windows.Storage.Pickers.PickerViewMode.thumbnail;

picker.pickSingleFileAsync().done(function (file) {

if (file) {

// Display the image to the user

document.getElementById("imageHolder").src = URL.createObjectURL(file, { oneTimeOnly: true });

// The imageFile variable will be set to shareValue when the user clicks Set

objimgFile = file;

}

});

}

一旦选取器成功选择了单个文件,它就调用async方法,然后设置src属性。

现在,让我们将shareDataHandler()方法添加到js文件中,如下所示:

function shareDataHandler(e) {

var request = e.request;

// The request.data.properties.title is mandate

var ShareInfoTitle = document.getElementById("txttitle").value;

if (ShareInfoTitle !== "") {

var ShareInfoDesc = document.getElementById("txtdesc").value;

if (ShareInfoDesc !== "") {

request.data.properties.title = ShareInfoTitle;

request.data.properties.description = ShareInfoDesc;

if (objimgFile != null) {

//Create a stream variable and using randomStreamReference.createFromFile //function create the stream for the image file object "objimgFile"

var imageStream = Windows.Storage.Streams.RandomAccessStreamReference.createFromFile(objimgFile);

request.data.setStorageItems([objimgFile]);

// The setBitmap method requires a stream object

request.data.setBitmap(imageStream);

}

} else {

request.failWithDisplayText("Please Enter the Project Details you would like to share.");

}

} else {

request.failWithDisplayText("Error Occured!!");

}

}

shareDataHandler函数首先设置request.data对象的标题和描述。为了设置图像,它使用 stream 对象,如下面的代码所示:

var imageStream = Windows.Storage.Streams.RandomAccessStreamReference.createFromFile(objimgFile);

这一行创建一个流变量,并使用createFromFile函数为图像文件objimgFile对象创建流。

下一行通过传递 image 对象来调用request.data对象的setStorageItems函数。objimgfile对象被设置为selectImage()文件夹中的一个文件。

request.data.setStorageItems([objimgFile]);

前面代码中的下一行是request.data对象的setBitMap方法,它将图像设置为共享的数据对象:

// pass the stream imageStream to the setBitMap method

request.data.setBitmap(imageStream);

当您在 Windows 10 上使用 Share charm 功能运行应用时,您会看到选择图像按钮,如图 10-5 所示。

A978-1-4842-0719-2_10_Fig5_HTML.jpg

图 10-5。

Sharing image demo

点按“选择图像”按钮,并从设备相机文件夹或照片文件夹中选择图像。一旦你选择了照片,你就会在你的default.html文件中看到该图像,如图 10-6 所示。

A978-1-4842-0719-2_10_Fig6_HTML.jpg

图 10-6。

Sharing Image demo

现在单击共享图标或按下 Windows 徽标键A978-1-4842-0719-2_10_Figb_HTML.jpg + H。当您从列表中选择 OneNote 时,应用将与 OneNote 共享数据,您将看到如图 10-7 所示的屏幕。

A978-1-4842-0719-2_10_Fig7_HTML.jpg

图 10-7。

Image shared along with project title

10.5 将应用声明为共享目标

问题

当用户使用 Share charm 功能时,您需要在列表中声明一个应用作为共享目标。

解决办法

package.appxmanifest文件中配置共享目标,使共享契约成为共享目标 app。

它是如何工作的

当用户使用 Share charm 调用共享时,Windows 10 会显示一个可能的目标应用列表。目标共享应用是可以接收其他应用共享的数据的应用。您需要将您的应用声明为目标应用,并实现接收数据并将其存储在正确的位置或显示在屏幕上的功能。例如,您有两个应用:一个用于时间管理报告,另一个用于组织中的项目列表。如果你想把时间管理报告的数据分享给项目列表应用,该怎么办?时间管理报告应用将成为源应用,项目列表应用将成为目标应用。

当您将一个应用声明为可以接收其他应用共享的数据的目标应用时,您的应用遵守共享契约。通过将您的应用声明为目标应用,您可以为您的应用用户提供良好的体验,从而增强与其他应用的社交连接。

如果您希望通过共享选项从其他应用接收数据或支持共享契约,则需要将您的应用声明为共享目标。

要将您的应用声明为共享目标,请遵循以下准则:

Open the manifest file (package.appxmanifest), which is available in the root folder of the project.   Click the Declaration tab.   From the available declarations, select Share Target.   On the right side of the screen, enter Sharing Description.   Select the data format by clicking Add new.   Define two data formats: Text and Image.  

申报设置窗口应如图 10-8 所示。

A978-1-4842-0719-2_10_Fig8_HTML.jpg

图 10-8。

Image shared along with project title

当你在 Windows 10 中运行应用并使用 Share charm 功能时,你会看到你的应用出现在列表中,如图 10-9 所示。

A978-1-4842-0719-2_10_Fig9_HTML.jpg

图 10-9。

Share Charm windows

让我们回顾一下共享契约支持的数据类型。表 10-1 列出了使用共享契约的共享数据中支持的数据类型。

表 10-1。

Data Types in Share Contract

| 数据类型 | 描述 | | --- | --- | | 文本 | 纯文本字符串 | | 上呼吸道感染 | 一个网页链接(例如, [`http://www.apress.com`](http://www.apress.com) ) | | 图像 | 位图图像 | | 超文本标记语言 | HTML 内容 | | 文件 | 作为存储项目的文件 | | 自定义数据 | 使用 JSON 格式的自定义数据 | | 多信息文本 | 富文本格式的字符串 |

10.6 处理共享激活并接收纯文本

问题

您需要将共享激活作为共享目标应用来处理。

解决办法

当您的应用出现在 Share charm 的目标应用列表中时,用户可以从列表中选择您的应用来共享数据。当用户选择您的应用来共享数据时,会触发Application.OnShareTargetActivated事件。

它是如何工作的

当您的应用被选择用于共享数据时,会触发一个 Application.OnShareTargetActivated 事件。您的应用需要实现此事件来接收用户从其他应用共享的数据。

让我们在一个通用的 Windows 应用中实现这个事件,以便从其他应用接收数据。

在 Microsoft Visual Studio 2015 中使用 Windows 通用(空白应用)模板创建一个新项目。

首先,将下面的代码添加到default.html中。您需要用以下代码替换 body 标记:

<body class="win-type-body">

<div>

<br /><br />

<div>The following Data  was received from the source app:</div>

<br />

<h3>Data Properties</h3>

<strong>Title: </strong><span id="txttitle" data-win-automationId="Title">(No title)</span><br />

<strong>Description: </strong><span id="txtdescription" data-win-automationId="Description">(No description)</span><br />

<strong>Data: </strong><span id="txtdata" data-win-automationId="Description">(No data)</span><br />

</div>

</body>

这段代码为共享数据演示定义了一个用户界面。用户界面如图 10-10 所示。

A978-1-4842-0719-2_10_Fig10_HTML.jpg

图 10-10。

Share Charm windows

现在,右键单击解决方案资源管理器中的项目,并选择添加➤新 JavaScript 文件。提供文件的名称。在本例中,我们将文件命名为 DataSharingDemo。js。

(function () {

"use strict";

function GetControl() {

WinJS.UI.processAll().done(function () {

// Initialize the activation handler

WinJS.Application.addEventListener("activated", activatedShareHandler, false);

});

}

document.addEventListener("DOMContentLoaded", GetControl);

})();

上述代码将一个激活的事件侦听器添加到 application 对象中。名为activatedShareHandler的事件监听器函数提供如下:将该函数添加到js文件中。

/// <summary>

/// Function to handle the activation

/// </summary>

/// <param name="eventArgs">

/// Arguments of the event. In the case of the Share contract, it has the ShareOperation

/// </param>

function activatedShareHandler(eventObject) {

// Check if the Share Contract was the cuase of this sharing

if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.shareTarget) {

eventObject.setPromise(WinJS.UI.processAll());

// ShareOperation object in initiliaze with the eventObject event arugment

shareOperation = eventObject.detail.shareOperation;

//check if shareOperation has text Sharing enable or data shared using text

if (shareOperation.data.contains(Windows.ApplicationModel.DataTransfer.StandardDataFormats.text)) {

shareOperation.data.getTextAsync().done(function (text) {

dispReceivedContent(text);

}, function (e) {

displayError("Text: ", "Error retrieving data: " + e);

});

}

}

}

前面的代码接收activatedShareHandler事件监听器的共享数据。这段代码首先检查共享是否是由共享契约触发的。然后,它从事件参数创建一个shareOperation对象,如以下代码所示:

// ShareOperation object in initiliaze with the eventObject event arugment

shareOperation = eventObject.detail.shareOperation;

然后,代码使用shareOperation.data.getTextAsync函数异步获取文本。在异步调用的 success 方法中,接收到的文本然后被传递给dispReceivedContent()函数,该函数然后在目标应用上显示文本。以下是显示共享内容功能的代码:

function dispReceivedContent(content) {

document.getElementById("txttitle").innerText = shareOperation.data.properties.title;

document.getElementById("txtdescription").innerText = shareOperation.data.properties.description;

document.getElementById("txtdata").innerText = content;

}

这里显示了DataSharingDemo.js文件的完整代码:

(function () {

"use strict";

function GetControl() {

WinJS.UI.processAll().done(function () {

// Initialize the activation handler

WinJS.Application.addEventListener("activated", activatedShareHandler, false);

});

}

document.addEventListener("DOMContentLoaded", GetControl);

})();

/// <summary>

/// Function to handle the activation

/// </summary>

/// <param name="eventArgs">

/// Arguments of the event. In the case of the Share contract, it has the ShareOperation

/// </param>

function activatedShareHandler(eventObject) {

// Check if the Share Contract was the cause of this sharing

if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.shareTarget) {

eventObject.setPromise(WinJS.UI.processAll());

// ShareOperation object in initiliaze with the eventObject event argument

shareOperation = eventObject.detail.shareOperation;

//check if shareOperation has text Sharing enable or data shared using text

if (shareOperation.data.contains(Windows.ApplicationModel.DataTransfer.StandardDataFormats.text)) {

shareOperation.data.getTextAsync().done(function (text) {

dispReceivedContent(text);

}, function (e) {

displayError("Text: ", "Error retrieving data: " + e);

});

}

}

}

function dispReceivedContent(content) {

document.getElementById("txttitle").innerText = shareOperation.data.properties.title;

document.getElementById("txtdescription").innerText = shareOperation.data.properties.description;

document.getElementById("txtdata").innerText = content;

}

当您在 Windows 10 中运行一个源应用时(我们正在运行我们在 Recipe 10.2 中开发的另一个应用),您会看到如图 10-11 所示的屏幕。

A978-1-4842-0719-2_10_Fig11_HTML.jpg

图 10-11。

Share charm windows showing the target app

如图 10-11 所示,app 10.6 _ 接收分享数据列在 app 的分享魅力分享目标列表中。当您选择应用时,您会看到从源应用共享的数据(标题、描述和文本),如图 10-12 中突出显示的。

A978-1-4842-0719-2_10_Fig12_HTML.jpg

图 10-12。

Share Charm windows showing the target app

10.7 接收其他应用共享的图像

问题

你需要开发一个功能,允许用户接收另一个 Windows 10 应用共享的图像。

解决办法

使用shareOperation.data.getBitmapAsync接收其他应用共享的图像。

它是如何工作的

源应用也可以与其他应用共享图像,如你在 Recipe 10.4 中看到的。为了共享位图图像,源应用使用request.data.setBitmap(imageStream)方法,通过传递图像流将位图图像设置为正在共享的请求对象。

为了接收位图图像,目标应用使用数据包视图的shareOperation.data.getBitmapAsync()方法。接收位图图像的完整代码如下所示:

// ShareOperation object in initiliaze with the eventObject event arugment

shareOperation = eventObject.detail.shareOperation;

if (shareOperation.data.contains(Windows.ApplicationModel.DataTransfer.StandardDataFormats.bitmap)) {

shareOperation.data.getBitmapAsync().done(function (bitmapStreamReference ) {

bitmapStreamReference.openReadAsync().done(function (bitmapStream) {

if (bitmapStream) {

document.getElementById("imageHolder").src = URL.createObjectURL(bitmapStream, { oneTimeOnly: true });

}

}, function (e) {

displayError("Bitmap: ", "Error reading image stream:  " + e);

});

}, function (e) {

displayError("Bitmap: ", "Error retrieving data: " + e);

});

}

这段代码首先检查shareOperation对象是否包含位图图像。然后它调用getBitmapAsync()方法从请求对象接收图像。最后,在异步调用的成功方法中,调用另一个对bitmapStreamReference.openReadAsync()的异步调用来读取流对象。openReadAsync返回流对象。然后,URL.createObjectURL(bitmapStream, { oneTimeOnly: true })行代码创建对象的 URL,然后将其分配给imageHolder作为 HTML img标签的源。

让我们在一个通用的 Windows 应用中实现这个事件,从其他应用接收位图。

在 Microsoft Visual Studio 2015 中使用 Windows 通用(空白应用)模板创建一个新项目。

首先,将下面的代码添加到default.html中。您需要用以下代码替换 body 标记:

<body class="win-type-body" style="background-color:white" >

<div>

<br /><br />

<div><p style="color:black">The following Data  was received from the source app:</p></div>

<br />

<h3 style="color:black">Data Properties</h3>

<strong style="color:black">Title: </strong><span id="txttitle" data-win-automationId="Title" style="color:black">(No title)</span><br />

<strong style="color:black">Description: </strong><span id="txtdescription" data-win-automationId="Description" style="color:black">(No description)</span><br />

<strong style="color:black">Data: </strong><span id="txtdata" data-win-automationId="Description" style="color:black">(No data)</span><br />

<div class="imageDiv">

<img class="imageHolder" id="imageHolder" alt="image holder" src="" />

</div>

</div>

</body>

现在,右键单击解决方案资源管理器中的项目,并选择添加➤新 JavaScript 文件。提供文件的名称。在本例中,我们将文件命名为 DataSharingDemo.js。

(function () {

"use strict";

function GetControl() {

WinJS.UI.processAll().done(function () {

// Initialize the activation handler

WinJS.Application.addEventListener("activated", activatedHandler, false);

});

}

document.addEventListener("DOMContentLoaded", GetControl);

})();

上述代码将一个激活的事件侦听器添加到 application 对象中。下面是名为activatedShareHandler的事件监听器函数。将这段代码添加到js文件中。

/// <summary>

/// Function to handle the activation

/// </summary>

/// <param name="eventArgs">

/// Arguments of the event. In the case of the Share contract, it has the ShareOperation

/// </param>

function activatedShareHandler(eventObject) {

// Check if the Share Contract was the cuase of this sharing

if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.shareTarget) {

eventObject.setPromise(WinJS.UI.processAll());

// ShareOperation object in initiliaze with the eventObject event arugment

shareOperation = eventObject.detail.shareOperation;

if (shareOperation.data.contains(Windows.ApplicationModel.DataTransfer.StandardDataFormats.bitmap)) {

shareOperation.data.getBitmapAsync().done(function (bitmapStreamReference) {

bitmapStreamReference.openReadAsync().done(function (bitmapStream) {

if (bitmapStream) {

document.getElementById("imageHolder").src = URL.createObjectURL(bitmapStream, { oneTimeOnly: true });

}

}, function (e) {

displayError("Bitmap: ", "Error reading image stream:  " + e);

});

}, function (e) {

displayError("Bitmap: ", "Error retrieving data: " + e);

});

}

}

}

最后,使用package.appxmanifest将该 app 声明为目标 app,如图 10-13 所示。

A978-1-4842-0719-2_10_Fig13_HTML.jpg

图 10-13。

Share Charm windows showing the target app

就是这样。现在,使用 Visual Studio 中的项目部署菜单部署应用。

您不必运行此应用来接收图像,因为这是注册的目标应用。Share charm 将此应用列为 Share charm 窗口中的目标应用,当用户从列表中选择该应用时,将为此应用激活一个新实例。激活的事件将被调用,然后接收源应用共享的图像。

10.8 共享自定义数据类型

问题

您需要开发一个允许用户共享自定义数据格式的特性,比如一个人的姓名和职务。

解决办法

如下使用request.data.setData功能:

request.data.setData("Schema", "ObjectName");

它是如何工作的

共享契约还允许开发人员构建应用来共享定制格式的数据,例如一本书(书名、出版商、作者姓名等)。)或一个人,带有姓名、职务、个人资料图片等属性。

Windows 10 通用 Windows 应用支持的自定义格式可以基于 http://schema.org 定义的模式,如 http://schema.org/personh ttp://schema.org/Book 。set data 方法接受以下两个参数:

request.data.setData("http://schema.org/Person

第一个参数是模式格式 id,第二个参数是对象本身。

让我们在一个通用的 Windows 应用中实现共享自定义数据。

在 Microsoft Visual Studio 2015 中使用 Windows 通用(空白应用)模板创建一个新项目。

首先,在default.html中添加以下代码。您需要用以下代码替换 body 标记:

<body class="win-type-body" style="background-color:white">

<div>

<p style="color:black">Enter Person  Name:</p>

<input class="text-box" id="txttitle" value="Name" size="40" />

<p style="color:black">Enter Person Title:</p>

<input class="text-box" id="txtjobtitle" value="Job Title" size="40" />

<p style="color:black">Enter telephone:</p>

<input class="text-box" id="txtphone" value="Telephone" size="40" />

<br />

<br />

</div>

</body>

现在,右键单击解决方案资源管理器中的项目,并选择添加➤新 JavaScript 文件。提供文件的名称。在本例中,我们将文件命名为 DataSharingDemo.js。

(function () {

"use strict";

function GetControl() {

WinJS.UI.processAll().done(function () {

var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();

dataTransferManager.addEventListener("datarequested", shareDataHandler);

});

}

document.addEventListener("DOMContentLoaded", GetControl);

})();

通过复制以下代码实现shareDataHandler功能:

function shareDataHandler(e) {

var request = e.request;

;

// The request.data.properties.title is mandate

var name = document.getElementById("txttitle").value;

var person = {

"type": "http://schema.org/Person

"properties":

{

"name": name,

"jobtitle": document.getElementById("txtjobtitle").value

}

};

person = JSON.stringify(person);

request.data.properties.title = name;

request.data.properties.description = name;

request.data.setData("http://schema.org/Person

}

上述函数中的代码实现了自定义数据的共享。由于 schema.org 支持 JSON 和 XML 格式,因此person对象被定义为一个 JSON 字符串,用于设置姓名和职位等属性。

现在让我们创建另一个项目并实现代码来接收定制数据。

在 Microsoft Visual Studio 2015 中使用 Windows 通用(空白应用)模板创建一个新项目。

首先,将下面的代码添加到default.html中。您需要用下面的代码替换 body 标记。

<body class="win-type-body">

<textarea class="text-box" id="txtconent" rows="4" cols="50" />

<br />

<br />

</body>

现在,右键单击解决方案资源管理器中的项目,并选择添加➤新 JavaScript 文件。提供文件的名称。在本例中,我们将文件命名为 DataSharingDemo.js。

(function () {

"use strict";

function GetControl() {

WinJS.UI.processAll().done(function () {

// Initialize the activation handler

WinJS.Application.addEventListener("activated", activatedShareHandler, false);

});

}

document.addEventListener("DOMContentLoaded", GetControl);

})();

通过复制以下代码实现activatedShareHandler功能:

function activatedShareHandler(eventObject) {

if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.shareTarget) {

eventObject.setPromise(WinJS.UI.processAll());

// ShareOperation object is initiliaze with the eventObject event argument

shareOperation = eventObject.detail.shareOperation;

if (shareOperation.data.contains("http://schema.org/Person

shareOperation.data.getTextAsync("http://schema.org/Person").done(function.done(function)

var customFormatObject = JSON.parse(customFormatString);

if (customFormatObject) {

// This sample expects the custom format to be of typehttp://schema.org/Person

if (customFormatObject.type === "http://schema.org/Person

customFormatString = "Type: " + customFormatObject.type;

if (customFormatObject.properties) {

customFormatString += "\nName: " + customFormatObject.properties.name,

customFormatString += "\nNjobtitle: " + customFormatObject.properties.jobtitle;

}

}

}

document.getElementById("txtconent").value = customFormatString;

}, function (e) {

});

}

}

}

这段代码首先检查共享操作是否触发了事件。然后它初始化shareOperation = eventObject.detail.shareOperation。然后检查shareOperation.data.contains( http://schema.org/Person )。如果它包含一个 Person 对象,那么它调用shareOperation.data.getTextAsync方法来检索定制数据属性。在 success 方法中,它将 JSON 字符串解析为一个变量,如下所示:

var customFormatObject = JSON.parse(customFormatString);

最后,它读取这些属性并在文本区域显示出来。您需要使用 Visual Studio 部署选项来部署此应用。

现在运行共享自定义数据的应用。出现一个屏幕,如图 10-14 所示。

A978-1-4842-0719-2_10_Fig14_HTML.jpg

图 10-14。

The app UI that shares a custom data format

现在点击 Share charm 或者按下 Windows logo 键A978-1-4842-0719-2_10_Figc_HTML.jpg + H,当你从列表中选择 10.7B_ReceiveCustom_data app 时,你会看到如图 10-15 所示的屏幕。

A978-1-4842-0719-2_10_Fig15_HTML.jpg

图 10-15。

The receiver app for the custom data in the Share charm

当您选择应用时,会在 Share charm 窗口中显示接收者应用的用户界面,如图 10-16 所示。

A978-1-4842-0719-2_10_Fig16_HTML.jpg

图 10-16。

The receiver app with the received custom data