<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Windows 8 应用程序开发人员博客</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/windowsappdev_cn/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/windowsappdev_cn/feed.xml</link>
    <description>有关为 Windows 8 生成 Metro 风格应用程序的见解，来自 Windows 8 工程团队</description>
    <lastBuildDate>Wed, 17 Apr 2019 03:28:43 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>关于 promise（用于使用 JavaScript 编写的 Windows 应用商店应用）</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_cn/promise-javascript-windows</link>
      <pubDate>Wed, 26 Jun 2013 15:51:09 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_cn/2013/06/26/promise-javascript-windows/</guid>
      <description><![CDATA[当您使用 JavaScript 编写 Windows 应用商店应用时，一旦执行涉及异步 API 的操作时，就会遇到称为 promise 的构造。此外，无需很长时间，为顺序异步操作编写 promise...]]></description>
      <content:encoded><![CDATA[<div class="PostContent"> <p>当您使用 JavaScript 编写 Windows 应用商店应用时，一旦执行涉及异步 API 的操作时，就会遇到称为 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/Hh700339.aspx">promise</a><i></i> 的构造。此外，无需很长时间，为顺序异步操作编写 promise 链就会成为您的一种习惯。</p>  <p>但是，在开发的过程中，您可能会遇到其他使用 promise 的情形，此时您可能无法完全了解具体情况。为 ListView 控件优化项目呈现功能就是一个很好的例子，如在 <a href="http://code.msdn.microsoft.com/windowsapps/ListView-performance-39fb71f0">HTML ListView 优化性能示例</a>中所示。我们将在随后的一篇博文中继续探究这一主题。或者，您也可以了解一下 Josh Williams 在 //build 2012 大会的<a href="https://channel9.msdn.com/Events/Build/2012/4-101">深入探讨 WinJS</a> 讨论（略有修改）中展示的以下重要代码段：</p>  <blockquote>   <div id="codeSnippetWrapper">     <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">list.reduce(<span style="color: rgb(0, 0, 255);">function</span> callback (prev, item, i) {<br />    <span style="color: rgb(0, 0, 255);">var</span> result = doOperationAsync(item);<br />    <span style="color: rgb(0, 0, 255);">return</span> WinJS.Promise.join({ prev: prev, result: result}).then(<span style="color: rgb(0, 0, 255);">function</span> (v) {<br />        console.log(i + <span style="color: rgb(0, 96, 128);">&quot;, item: &quot;</span> + item+ <span style="color: rgb(0, 96, 128);">&quot;, &quot;</span> + v.result);<br />    });<br />})<br /></pre>
  </div>
</blockquote>

<p>该代码段将加入到 promise 来完成并行异步操作，并根据 list 中的命令按顺序提供结果<i></i>。如果您一看到该代码就可以立即了解其功能，那么您完全可以跳过本博文！否则，让我们来仔细看一下 promise 的真正工作原理，以及其在 WinJS（Windows JavaScript 库）中的表达式，以便了解这些模式类型。</p>

<h2>promise 究竟是什么？承诺关系</h2>

<p>首先，让我们来了解一下基本情况：promise 实际上无非就是一个代码构造或一个调用约定（如果您愿意）。因此，promise 与异步操作没有内在的关系，它们在这方面非常有用<i></i>实属巧合！promise 只是一个对象，代表着可能会在将来某一时间可用（或现已可用）的值。这样，promise 的含义就像我们在人际关系中使用该术语一样。如果我说，“我承诺投递给您一打甜甜圈”，则很明显我无需即刻得到这些甜甜圈，而毫无疑问我假定自己可以在将来得到它们。<i></i>一旦得到甜甜圈，我就会投递给您。</p>

<p>Promise 故暗示着两个代理方之间的关系：<i></i>承诺发货的发货人与既是承诺的接受方又是收货人的消费者。<i></i>发货人如何获得这些货物是其自己的事情。同样，消费者可以随意处理承诺和投递的货物。消费者甚至可与其他的消费者分享承诺。</p>

<p>在发货人和消费者之间，也存在着两个关系阶段，创建<i></i>和<i></i>履行。全部内容如下图所示。</p>

<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0844.promise_diagram_577C7B9B.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0844.promise_5F00_diagram_5F00_577C7B9B.png"><img title="promise_diagram" style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" border="0" alt="promise_diagram" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0458.promise_diagram_thumb_0FBAF2B4.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0458.promise_5F00_diagram_5F00_thumb_5F00_0FBAF2B4.png" width="700" height="341" /></a></p>

<p>通过该图中显示的流程，我们可以了解到分为两个阶段的关系是 promise 通过异步交货的方式得以履行的原因所在。这个过程的关键部分是一旦消费者认可请求（即 promise），其可以继续生活（异步），而不是等待（同步）。这意味着消费者在等待承诺履行的同时可以做其他的事情，例如响应其他的请求，而这也是异步 API 的初衷。如果已得到货物，情况会怎样？此时，promise 将立即履行，就像是一种同步调用约定。</p>

<p>当然，对于这一关系，我们还要考虑一些额外的东西。在日常生活中，您一定作出过承诺，并且别人也对您作出过承诺。尽管许多承诺已经履行，但许多承诺无法兑现也是事实，例如快递员在为您送披萨的途中发生了交通事故！背弃的承诺是无法改变的事实，无论是在我们的个人生活还是异步编程方面，我们都必须接受。</p>

<p>在承诺关系中，这意味着发货人需要一种表达，“对不起，我无法兑现该承诺”的方式，而消费者也需要一种了解该事实的方式。其次，作为消费者，我们有时会对别人向我们作出的承诺失去耐心！因此，如果发货人可以在履行承诺的过程中跟踪进展情况，消费者也需要一种获得该信息的方式。第三，消费者还可以取消订单，并告知发货人其不再需要该货物。</p>

<p>将这些要求添加到图中之后，我们就会看到完整的关系：</p>

<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/4251.promise_diagram_2_23D40F3D.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/4251.promise_5F00_diagram_5F00_2_5F00_23D40F3D.png"><img title="promise_diagram_2" style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" border="0" alt="promise_diagram_2" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0550.promise_diagram_2_thumb_1CB4D2C5.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0550.promise_5F00_diagram_5F00_2_5F00_thumb_5F00_1CB4D2C5.png" width="700" height="432" /></a></p>

<p>现在，我们来看看这些关系如何通过代码来体现。</p>

<h2>promise 构造和 promise 链</h2>

<p>事实上，promise 有着许多不同的建议或规范。Windows 和 WinJS 中使用的 promise 称为 <a href="http://wiki.commonjs.org/wiki/Promises/A">Common JS/Promises A</a>，表示 promise（由发货人返回并代表一个在未来提供的值）是一个包含 <b>then</b> 函数的对象。消费者通过调用 <b>then</b> 表示订阅承诺的履行。（Windows 中的 promise 还支持称为 <b>done</b> 的类似函数，不久我们将会看到其用于 promise 链。）</p>

<p>消费者可向该函数传递三个作为参数的可选函数，具体顺序如下：</p>

<ol>
  <li><i></i>completed 处理程序。当承诺的值可用时，发货人将调用该函数，而当该值<i></i>现已可用时，completed 处理程序将从 <b>then</b> 中被立即调用（同步）。</li>

  <li>可选的 <i></i>error 处理程序。它将在获取承诺的值失败时调用。对于任何特定的承诺，均无法在调用 error 处理程序的情况下调用 completed 处理程序。</li>

  <li>可选的 <i></i>progress 处理程序。在操作支持的情况下，它将通过中间结果定期调用。（在 WinRT 中，这意味着 API 包含一个 <b>IAsync[Action | Operation]WithProgress</b> 的返回值；而 <b>IAsync[Action | Operation]</b> 则没有该值。）</li>
</ol>

<p>请注意，您可以为任何这些参数传递 null，例如当您只想附加 error 处理程序，而不是 completed 处理程序的时候<i></i>。</p>

<p>就关系中的另一方而言，消费者可通过多次调用 <b>then</b>，对同一个 promise 订阅任意数量的处理程序。另外，消费者还可与其他可随意调用 <b>then</b> 的消费者分享 promise。这完全受支持。</p>

<p>这意味着 promise 必须管理其收到的所有处理程序的列表，并在适当的时候调用它们。Promise 还需要允许取消，如完整关系中所述。</p>

<p><i></i>Promises A 规范中的另一项要求是 <b>then</b> 方法本身返回 promise。当传递给第一个 <b>promise.then</b> 的 completed 处理程序返回时，履行第二个 promise，并且返回值将作为第二个 promise 的结果提供。请看以下代码段：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">var</span> promise1 = someOperationAsync();<br /><span style="color: rgb(0, 0, 255);">var</span> promise2 = promise1.then(<span style="color: rgb(0, 0, 255);">function</span> completedHandler1 (result1) { <span style="color: rgb(0, 0, 255);">return</span> 7103; } );<br />promise2.then(<span style="color: rgb(0, 0, 255);">function</span> completedHandler2 (result2) { });<br /></pre>
  </div>
</blockquote>

<p>此处的执行链是启动 <b>someOperationAsync</b>，并返回 <i></i>promise1。当操作进行时，我们调用 <b>promise1.then</b>，其将立即返回 promise2<i></i>。除非异步操作的结果已经可用，否则 <b>completedHandler1</b> 不会被调用，请务必明确这一点。假定我们仍然在等待，因此我们直接调用 <b>promise2.then</b>，此时 completedHandler2 也不会调用<i></i>。</p>

<p>过了一段时间后，<b>someOperationAsync</b> 完成并提供一个值（例如 14618）。<i></i>现在，promise1 将履行，其将使用该值调用 <b>completedHandler1</b>，因而 <i></i>result1 是 14618。现在，<b>completedHandler1</b> 执行并返回值 7103。此时，<i></i>promise2 将履行，因此其使用等于 7103 的 result2 调用 <b>completedHandler2</b><i></i>。</p>

<p>如果 completed 处理程序返回其他 promise 会怎样呢？这种情况的处理方式有些不同。假设上述代码中的 <b>completedHandler1</b> 返回了以下 promise：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">var</span> promise2 = promise1.then(<span style="color: rgb(0, 0, 255);">function</span> completedHandler1 (result1) {<br />    <span style="color: rgb(0, 0, 255);">var</span> promise2a = anotherOperationAsync();<br />    <span style="color: rgb(0, 0, 255);">return</span> promise2a;<br />});<br /></pre>
  </div>
</blockquote>

<p>在此情况下，<b>completedHandler2</b> 中的 result2 本身将不会成为<i></i> <i></i>promise2a，而是会成为 promise2a 的 <u>fulfillment</u> 值<i></i>。也就是说，由于 completed 处理程序返回了一个 promise，<i></i><b>promise1.then</b> 返回的 promise2 将使用 promise2a<i></i> 的结果履行。</p>

<p>准确地说，正是这一特性使顺序异步操作能够链接在一起，每个操作的结果将提供给下一个操作。在没有中间变量或命名的处理程序的情况下，您通常会看到 promise 链的这一模式：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">operation1().then(<span style="color: rgb(0, 0, 255);">function</span> (result1) {<br />    <span style="color: rgb(0, 0, 255);">return</span> operation2(result1)<br />}).then(<span style="color: rgb(0, 0, 255);">function</span> (result2) {<br />    <span style="color: rgb(0, 0, 255);">return</span> operation3(result2);<br />}).then(<span style="color: rgb(0, 0, 255);">function</span> (result3) {<br />    <span style="color: rgb(0, 0, 255);">return</span> operation4(result3);<br />}).then(<span style="color: rgb(0, 0, 255);">function</span> (result4) {<br />    <span style="color: rgb(0, 0, 255);">return</span> operation5(result4)<br />}).then(<span style="color: rgb(0, 0, 255);">function</span> (result5) {<br />    <span style="color: rgb(0, 128, 0);">//And so on</span><br />});<br /></pre>
  </div>
</blockquote>

<p>当然，每个 completed 处理程序可能会使用其收到的结果执行更多的操作，但在所有的链中，您都可以看到该核心构造。此处一个接一个执行的 <b>then</b> 方法亦是如此，它们所做的一切就是单独保存特定的 completed 处理程序并返回其他 promise。因此，当到达代码结尾处时，<i></i>operation1 将已启动，并且不会调用 completed 处理程序。但是，来自所有 <b>then</b> 调用的大量中间 promise 将被创建出来并彼此连结在一起，以随着顺序操作的进展对链进行管理。</p>

<p>值得一提的是，通过在之前的 completed 处理程序中嵌套各个顺序操作，可以获得相同的顺序，这样您就可以避免使用大量的<i></i> return 语句。但是，这种嵌套会遇到严重的缩进问题，特别是在您开始向 <b>then</b> 添加包含每个调用的 progress 处理程序和 error 处理程序的情况下。</p>

<p>说到这里，WinJS 中的 promise 的特性之一就是，当链中的任一环节发生错误时，错误即会自动传播至链的末端。这意味着您应该在最后一次调用 <b>then</b> 时附加一个单个的 error 处理程序，而不是在每一个级别都附加它。不过需要引起注意的是，出于各种微妙的原因，如果链中的最后一环是 <b>then</b> 调用，则这些错误就会消失。因此，WinJS 还提供了 promise 的 <b>done</b> 方法。该方法可接受与 <b>then</b> 相同的参数，但表明链已结束（它将返回 <i></i>undefined，而不是其他 promise）。因此，附加到 <b>done</b> 的 error 处理程序会针对整个链中的任意错误进行调用。此外，在没有 error 处理程序的情况下，<b>done</b> 将向应用级别抛出异常，以便由 <b>WinJS.Application.onerror</b> 事件的 <b>window.onerror</b> 进行处理。简而言之，理想的情况下，所有链都应结束于 <b>done</b>，以确保出现的异常可以得到妥善的处理。</p>

<p>当然，如果您编写的函数的目的是<i></i>从长长的 <b>then</b> 调用链中返回最后一个 promise，则您仍然需要在结尾处使用 <b>then</b>：接下来，将由可能在其他链中使用该 promise 的调用者来完全负责处理错误。</p>

<h2>创建 promise：WinJS.Promise 类</h2>

<p>尽管您始终可以基于 <i></i>Promises A 规范创建您自己的 promise 类，但实际上要有大量的工作要做，因此最后交给库来完成。为此，WinJS 提供了强大、灵活且经过良好测试的 promise 类，称为 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br211867.aspx"><b>WinJS.Promise</b></a>。这样，您就可以基于不同的值和操作轻松创建 promise，而无需管理发货人/消费者关系的详细信息或 <b>then</b> 的行为。</p>

<p>当需要时，您可以（应该）使用 <b>new WinJS.Promise</b> 或合适的 helper 函数（如下一节中所述）来基于异步操作和现有的（同步）值创建 promise。请记住，promise 只是一个代码构造：没有要求规定 promise 必须封装异步操作或其他任何异步内容<i></i>。同样，仅仅在 promise 中封装一些代码无法使其<i></i>自动异步运行。<i></i>当您自己创建 promise 类时，情况依然如此。</p>

<p>作为直接使用 <b>WinJS.Promise</b> 的简单示例，假设我们希望执行一个长时间的计算（把从 1 到某最大值之间的大量数字加起来），但通过异步方式完成。我们可以为该例程创建我们自己的回调机制，但如果我们将其封装在 promise 中，则允许它被链接起来或加入到其他 API 中的其他 promise。（除此之外，<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br229787.aspx"><b>WinJS.xhr</b></a> 函数还在 promise 中封装了 JavaScript 的异步 <b>XmlHttpRequest</b>，从而您无需处理后者的特定事件构造。）</p>

<p>当然，我们也可以使用 JavaScript worker 进行长时间的计算，但为了便于说明，我们将其保留在 UI 线程上，并使用 <b>setImmediate</b> 将操作拆分为多个步骤。以下显示了在 promise 构造中使用 <b>WinJS.Promise</b> 的具体实施方法：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">function</span> calculateIntegerSum(max, step) {<br />    <span style="color: rgb(0, 128, 0);">//The WinJS.Promise constructor's argument is an initializer function that receives </span><br />    <span style="color: rgb(0, 128, 0);">//dispatchers for completed, error, and progress cases.</span><br />    <span style="color: rgb(0, 0, 255);">return</span> <span style="color: rgb(0, 0, 255);">new</span> WinJS.Promise(<span style="color: rgb(0, 0, 255);">function</span> (completeDispatch, errorDispatch, progressDispatch) {<br />        <span style="color: rgb(0, 0, 255);">var</span> sum = 0;<br /><br />        <span style="color: rgb(0, 0, 255);">function</span> iterate(args) {<br />            <span style="color: rgb(0, 0, 255);">for</span> (<span style="color: rgb(0, 0, 255);">var</span> i = args.start; i &lt; args.end; i++) {<br />                sum += i;<br />            };<br /><br />            <span style="color: rgb(0, 0, 255);">if</span> (i &gt;= max) {<br />                <span style="color: rgb(0, 128, 0);">//Complete--dispatch results to completed handlers</span><br />                completeDispatch(sum);<br />            } <span style="color: rgb(0, 0, 255);">else</span> {<br />                <span style="color: rgb(0, 128, 0);">//Dispatch intermediate results to progress handlers</span><br />                progressDispatch(sum);<br />                setImmediate(iterate, { start: args.end, end: Math.min(args.end + step, max) });<br />            }<br />        }<br />            <br />        setImmediate(iterate, { start: 0, end: Math.min(step, max) });<br />    });<br />}<br /></pre>
  </div>
</blockquote>

<p>当调用 <b>new WinJS.Promise</b> 时，其构造函数中的唯一参数是一个 <i></i>initializer 函数（此时为匿名函数）。initializer 可以封装要执行的操作，但请务必明确该函数将在 UI 线程上同步执行<i></i>。因此，如果我们在不使用 <b>setImmediate</b> 的情况下执行长时间的计算，就会阻塞整个这段时间的 UI 线程。另外，将代码放到 promise 中无法<i></i>使其自动异步运行 — 需要使用 initializer 函数进行相应的设置。</p>

<p>就参数而言，initializer 函数可针对“已完成”、“错误”和“进行中”的情况收到三个调度程序（受 promise 支持）<i></i>。显而易见，在操作过程中，我们可以使用适当的参数在适当的时间调用这些调度程序。</p>

<p>我将这些函数称为“调度程序”，这是因为它们与消费者向 promise 订阅的 <b>then</b> 方法（或 <b>done</b>，但为不会一直提醒您）完全不同。在系统内部，WinJS 管理着大量的处理程序，这正是任意数量的消费者可订阅任意数量的处理程序的原因所在。当您调用这些调度程序之一时，WinJS 通过其内部列表进行迭代，并以您的名义调用所有这些处理程序。<b>WinJS.Promise</b> 也可以确保它的 <b>then</b> 按照链接的要求返回其他 promise。</p>

<p>简而言之，<b>WinJS.Promise</b> 提供了与 promise 有关的所有详细信息。这样，您就可以将精力集中放在 promise 所代表的核心操作上，这在 initializer 函数中得到了很好的体现。</p>

<h2>创建 promise 的 helper</h2>

<p>创建 promise 的主要 helper 函数是静态方法 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br211664.aspx"><b>WinJS.Promise.as</b></a>，其可以将 any 值<i></i>封装到 promise 中。基于已存在的值的封装程序反过来可以调用传递到 <b>then</b> 的任何 completed 处理程序。这样，您就可以明确将任意已知值视为 promise，从而能够混合 promise 并（通过加入或链接）将它们与其他 promise 组合。将 <b>as</b> 用于一个现有的 promise 只能返回该 promise。</p>

<p>另一个静态的 helper 函数是 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br229729.aspx"><b>WinJS.Promise.timeout</b></a>，其可以基于 <b>setTimeout</b> 和 <b>setImmediate</b> 提供便捷的封装程序。当第二个 promise 无法在特定时间（毫秒）内履行时，您也可以创建一个 promise 来将其取消。</p>

<p>请注意，基于 <b>setTimeout</b> 和 <b>setImmediate</b> 的 <b>timeout</b> promise 本身通过 <i></i>undefined 来履行。于是出现了一个常见的问题，“在超时之后，如何使用这些 promise 来提供一些其他的结果？”答案中包含了一个事实，那就是 <b>then</b> 将返回另一个 promise，该 promise 使用 completed 处理程序的返回值来履行。例如，下面这行代码：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">var</span> p = WinJS.Promise.timeout(1000).then(<span style="color: rgb(0, 0, 255);">function</span> () { <span style="color: rgb(0, 0, 255);">return</span> 12345; });</pre>
  </div>
</blockquote>

<p>创建在一秒钟之后使用值 12345 履行的 promise p<i></i>。换言之，<b>WinJS.Promise.timeout(…).then(function () { return &lt;value>} )</b> 是在给定超时之后提供 &lt;value><i></i> 的模式。如果 <i></i>&lt;value> 本身是另一个 promise，则意味着在超时之后的某个时间点提供该 promise 的履行值。</p>

<h2>取消和生成 promise 错误</h2>

<p>在我们刚刚看到的代码中，您可能已经注意到了两个缺陷。首先，操作在开始之后将无法被取消。其次，我们无法非常有效地处理错误。</p>

<p>应对这两种情况的技巧是，生成 promise 的函数（例如 <b>calculateIntegerSum</b> 函数）必须始终返回 promise。如果操作无法完成或最初根本未启动，则该 promise 将处于错误状态<i></i>。这意味着该 promise 没有（也绝不会有）<i></i>可传递给任何 completed 处理程序的结果：它只会调用其 error 处理程序。实际上，如果消费者调用已处于错误状态的 promise 的 <b>then</b> 方法，则该 promise 将立即（同步）调用传递给 <b>then</b> 的 error 处理程序。</p>

<p><b>WinJS.Promise</b> 进入错误状态的原因有两个：消费者调用其 <b>cancel</b> 方法或 initializer 函数中的代码调用 error 调度程序。在发生这种情况时，将会向 error 处理程序传递在 promise 中捕获或传播的错误值。如果您正在 <b>WinJS.Promise</b> 中创建操作，则您也可以使用 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br211689.aspx"><b>WinJS.ErrorFromName</b></a> 的实例。这只是一个 JavaScript 对象，它包含识别错误的 name<i></i> 属性和包含更多信息的 message<i></i> 属性。例如，当取消 promise 时，error 处理程序将会收到一个 <i></i>name 和 <i></i>message 名称均设置为“Canceled” 的错误对象。</p>

<p>但是，如果您无法从一开始就启动操作，情况会怎样？例如，如果您调用包含错误参数（如 0, 0）的 <b>calculateIntegerSum</b>，其甚至无法尝试启动计数，而是返回一个处于错误状态的 promise。这就是静态方法 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br229784.aspx"><b>WinJS.Promise.wrapError</b></a> 的用途。它获取 <b>WinJS.ErrorFromName</b> 的实例，并且在此情况下将返回处于错误状态的 promise，而不是新的 <b>WinJS.Promise</b> 实例。</p>

<p>另一方面，尽管调用 promise 的 <b>cancel</b> 方法会使 promise 本身处于错误状态，但我们如何停止已经开始的异步操作呢？在之前的 <b>calculateIntegerSum</b> 实施中，将一直调用 <b>setImmediate</b>，直到操作完成为止，无论我们创建的 promise 的状态如何。事实上，如果操作在 promise 已取消之后调用 complete 调度程序，则 promise 将忽略操作完成。</p>

<p>因此，需要使用一种方法来让 promise 告知操作其不再需要继续工作。为此，<b>WinJS.Promise</b> 构造函数需要在取消 promise 的情况下调用的第二个函数参数。在我们的示例中，对该函数的调用需要避免接下来调用 <b>setImmediate</b>，从而停止计算的情况。下方显示了具体的情形和正确的处理方法：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">function</span> calculateIntegerSum(max, step) {<br />    <span style="color: rgb(0, 128, 0);">//Return a promise in the error state for bad arguments</span><br />    <span style="color: rgb(0, 0, 255);">if</span> (max &lt; 1 || step &lt; 1) {<br />        <span style="color: rgb(0, 0, 255);">var</span> err = <span style="color: rgb(0, 0, 255);">new</span> WinJS.ErrorFromName(<span style="color: rgb(0, 96, 128);">&quot;calculateIntegerSum&quot;</span>, <span style="color: rgb(0, 96, 128);">&quot;max and step must be 1 or greater&quot;</span>);<br />        <span style="color: rgb(0, 0, 255);">return</span> WinJS.Promise.wrapError(err);<br />    }<br /><br />    <span style="color: rgb(0, 0, 255);">var</span> _cancel = <span style="color: rgb(0, 0, 255);">false</span>;<br /><br />    <span style="color: rgb(0, 128, 0);">//The WinJS.Promise constructor's argument is an initializer function that receives </span><br />    <span style="color: rgb(0, 128, 0);">//dispatchers for completed, error, and progress cases.</span><br />    <span style="color: rgb(0, 0, 255);">return</span> <span style="color: rgb(0, 0, 255);">new</span> WinJS.Promise(<span style="color: rgb(0, 0, 255);">function</span> (completeDispatch, errorDispatch, progressDispatch) {<br />        <span style="color: rgb(0, 0, 255);">var</span> sum = 0;<br /><br />        <span style="color: rgb(0, 0, 255);">function</span> iterate(args) {<br />            <span style="color: rgb(0, 0, 255);">for</span> (<span style="color: rgb(0, 0, 255);">var</span> i = args.start; i &lt; args.end; i++) {<br />                sum += i;<br />            };<br /><br />            <span style="color: rgb(0, 128, 0);">//If for some reason there was an error, create the error with WinJS.ErrorFromName</span><br />            <span style="color: rgb(0, 128, 0);">//and pass to errorDispatch</span><br />            <span style="color: rgb(0, 0, 255);">if</span> (<span style="color: rgb(0, 0, 255);">false</span> <span style="color: rgb(0, 128, 0);">/* replace with any necessary error check -- we don’t have any here */</span>) {<br />                errorDispatch(<span style="color: rgb(0, 0, 255);">new</span> WinJS.ErrorFromName(<span style="color: rgb(0, 96, 128);">&quot;calculateIntegerSum (scenario 7)&quot;</span>, <span style="color: rgb(0, 96, 128);">&quot;error occurred&quot;</span>));<br />            }<br /><br />            <span style="color: rgb(0, 0, 255);">if</span> (i &gt;= max) {<br />                <span style="color: rgb(0, 128, 0);">//Complete--dispatch results to completed handlers</span><br />                completeDispatch(sum); <br />            } <span style="color: rgb(0, 0, 255);">else</span> {<br />                <span style="color: rgb(0, 128, 0);">//Dispatch intermediate results to progress handlers</span><br />                progressDispatch(sum);<br /><br />                <span style="color: rgb(0, 128, 0);">//Interrupt the operation if canceled</span><br />                <span style="color: rgb(0, 0, 255);">if</span> (!_cancel) {<br />                    setImmediate(iterate, { start: args.end, end: Math.min(args.end + step, max) });<br />                }<br />            }<br />        }<br />            <br />        setImmediate(iterate, { start: 0, end: Math.min(step, max) });<br />    },<br />    <span style="color: rgb(0, 128, 0);">//Cancellation function for the WinJS.Promise constructor</span><br />    <span style="color: rgb(0, 0, 255);">function</span> () {<br />        _cancel = <span style="color: rgb(0, 0, 255);">true</span>;<br />    });<br />}<br /></pre>
  </div>
</blockquote>

<p>总而言之，创建 <b>WinJS.Promise</b> 的实例有许多用途。例如，如果您有一个通过一些其他的异步方法与 Web 服务通信的库，则您可以将这些操作封装在 promise 中。您还可能还需要使用新的 promise 将来自不同源的多个异步操作（或其他 promise）合并到一个单一的 promise 中，以便控制所涉及的所有关系。在 <b>WinJS.Promise</b> 的 initializer 的代码中，毫无疑问您可以包含面向其他异步操作及其 promise 的您自有的处理程序。您可以使用这些处理程序封装针对网络超时等情况的自动重试机制、挂接到一般进程更新程序 UI，或者添加底层日志记录或分析。通过这些方式，您的其余代码无需了解详细信息，并且可以直接处理来自消费者一方的 promise。</p>

<p>除此之外，将 JavaScript worker 封装到 promise 中的过程也变得相当简单，这样一来其外观和行为均变得与 WinRT 中的其他异步操作相同。如您所知，worker 通过 <b>postMessage</b> 调用提供他们的结果，该调用将引发应用程序中 worker 对象的 <b>message</b> 事件。以下代码可将该事件与履行的 promise 相关联，无论该消息中提供何种结果：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 128, 0);">// This is the function variable we're wiring up.</span><br /><span style="color: rgb(0, 0, 255);">var</span> workerCompleteDispatch = <span style="color: rgb(0, 0, 255);">null</span>;<br /><br /><span style="color: rgb(0, 0, 255);">var</span> promiseJS = <span style="color: rgb(0, 0, 255);">new</span> WinJS.Promise(<span style="color: rgb(0, 0, 255);">function</span> (completeDispatch, errorDispatch, progressDispatch) {<br />    workerCompleteDispatch = completeDispatch;<br />});<br /><br /><span style="color: rgb(0, 128, 0);">// Worker is created here and stored in the 'worker' variable</span><br /><br /><span style="color: rgb(0, 128, 0);">// Listen for worker events</span><br />worker.onmessage = <span style="color: rgb(0, 0, 255);">function</span> (e) {<br />    <span style="color: rgb(0, 0, 255);">if</span> (workerCompleteDispatch != <span style="color: rgb(0, 0, 255);">null</span>) {<br />        workerCompleteDispatch(e.data.results); <span style="color: rgb(0, 128, 0);">/* event args depends on the worker */</span><br />    }<br />}<br /><br />promiseJS.done(<span style="color: rgb(0, 0, 255);">function</span> (result) {<br />    <span style="color: rgb(0, 128, 0);">// Output for JS worker</span><br />});<br /></pre>
  </div>
</blockquote>

<p>要扩展该代码以处理 worker 中的错误，您需要在其他变量中保存 error 调度程序，使用 <b>message</b> 事件处理程序检查其事件参数中的错误信息，并在适当的时候调用 error 调度程序，而不是 complete 调度程序。</p>

<h2>加入并行的 promise</h2>

<p>由于 promise 通常用来封装异步操作，您完全可以并行执行多个操作。此时，您可能想知道是在履行组中的一个 promise 还是在履行组中的所有 promise 时执行。静态函数 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br229660.aspx"><b>WinJS.Promise.any</b></a> 和 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br211774.aspx"><b>WinJS.Promise.join</b></a> 为此提供了解决方案。</p>

<p>这两种函数可接受一组值或一个包含值属性的对象。这些值可以是 promise 和任何使用 <b>WinJS.Promise.as</b> 封装，从而让整个数组或对象由 promise 组成的非 promise 值。</p>

<p>以下是 <b>any</b> 的特性：</p>

<ul>
  <li><b>any</b> 在履行一个其他的 promise 或在该 promise 遇到错误（逻辑“或”）而失败时，创建一个单一的 promise。本质上，<b>any</b> <i></i>将 completed 处理程序附加到所有 promise，而一旦调用 completed 处理程序，其将调用 <b>any</b> <i></i>promise 本身收到的任何 completed 处理程序。</li>

  <li>在履行 <b>any</b> <i></i>promise 之后（也就是履行列表中的第一个 promise 之后），列表中的其他操作将继续运行，并调用向这些 promise 单独分配的任何 completed、error 或 progress 处理程序。</li>

  <li>如果您从 <b>any</b> 中取消 promise，则列表中的所有 promise 都将被取消。</li>
</ul>

<p>关于 <b>join</b>：</p>

<ul>
  <li><b>join</b> 在履行所有<i></i>其他的 promise 或在这些 promise 遇到错误（逻辑“或”）而失败时，创建一个单一的 promise。本质上，<b>join</b><i> </i>将 completed 和 error 处理程序附加到所有这些 promise，并等待所有处理程序调用完成，然后调用其本身收到的 completed 处理程序。</li>

  <li><b>join</b> promise 还将向您提供的任何 progress 处理程序报告进度。此时，中间结果是来自到目前为止已履行的各个 promise 的一系列结果。</li>

  <li>如果您从 <b>join</b> 中取消 promise，则它将取消仍然处于挂起状态的所有其他的 promise。</li>
</ul>

<p>除 <b>any</b> 和 <b>join</b> 之外，您还需要了解一下可派上用场的其他两种静态 <b>WinJS.Promise</b> 方法：</p>

<ul>
  <li><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br211765.aspx"><b>is</b></a> 确定是否任意值是返回布尔值的 promise。它主要确保对象包含名为“then”的函数；但不会测试“done”。</li>

  <li><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br229727.aspx"><b>theneach</b></a><i> </i>将 completed、error 和 progress 处理程序应用于一组 promise（使用 <b>then</b>），并在 promise 中将结果返回为另一组值。任何处理程序都可以为 null<i></i>。</li>
</ul>

<h2>包含顺序结果的并行 promise</h2>

<p>借助 <b>WinJS.Promise.join</b> 和 <b>WinJS.Promise.any</b>，我们能够处理并行 promise，也就是处理并行异步操作。另外，<b>join</b> 返回的 promise 将在整个数组中的 promise 全部履行之后履行。但是，这些 promise 可能将以随机顺序完成。当您有一组可通过此方式执行的操作，但您想以精心定义的顺序（即按照它们出现在数组中的顺序）处理其结果时，该怎么办？</p>

<p>为此，您需要将每个后续 promise 加入到以前出现的所有 promise 的 <b>join</b> 中，本文开始处的那一小段代码可准确地完成此操作。下方再次显示了该代码，但经过了重新编写以使 promise 变得更加明确。（假定 list<i></i><b> </b>是一组某种类型的值，并作为参数用于假设的生成 promise 的异步调用 <b>doOperationAsync</b>）：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">list.reduce(<span style="color: rgb(0, 0, 255);">function</span> callback (prev, item, i) {<br />    <span style="color: rgb(0, 0, 255);">var</span> opPromise = doOperationAsync(item);<br />    <span style="color: rgb(0, 0, 255);">var</span> join = WinJS.Promise.join({ prev: prev, result: opPromise});<br /><br />    <span style="color: rgb(0, 0, 255);">return</span> join.then(<span style="color: rgb(0, 0, 255);">function</span> completed (v) {<br />        console.log(i + <span style="color: rgb(0, 96, 128);">&quot;, item: &quot;</span> + item+ <span style="color: rgb(0, 96, 128);">&quot;, &quot;</span> + v.result);<br />    });<br />})<br /></pre>
  </div>
</blockquote>

<p>要了解该代码，我们必须首先了解该数组的 <a href="http://msdn.microsoft.com/zh-cn/library/ie/ff679975(v=vs.94).aspx"><b>reduce</b></a> 方法的工作原理。对于数组中的每个项目，<b>reduce</b> 将调用函数参数（此处称为 <b>callback</b>），它将收到四个参数（本代码中只用到三个）：</p>

<ul>
  <li>prev<i></i> 该值是之前对 <i></i><b>callback</b> 的调用的返回值（对于第一个项目，该值为 <i></i>null）。</li>

  <li>item<i></i> 数组中的当前值。</li>

  <li>i<i></i> 列表中的项目索引。</li>

  <li>source<i></i> 原始数组。</li>
</ul>

<p>对于第一个项目，我们将获得我称之为 <b>opPromise<sub>1</sub></b> 的 promise。由于 <b>prev</b> 是 <i></i>null，我们将加入 <b>[WinJS.Promise.as(null), opPromise<sub>1</sub>]</b>。但是，请注意我们将不会返回 <b>join</b> 本身。而是向该 join 附加一个 completed 处理程序（我已调用 <b>completed</b>），并从它的 <b>then</b> 返回 promise。</p>

<p>请记住，从 <b>then</b> 返回的 promise 将在 completed 处理程序返回时履行。这意味着我们从 <b>callback</b> 返回的 promise 将在第一个项目的 <b>completed</b> 处理程序已处理 <b>opPromise<sub>1</sub></b> 的结果之后完成。如果您回顾 join 的结果，就会发现它是通过一个包含原始列表中 promise 的结果的对象实现的。这意味着履行值 v<i></i> 将包含一个 prev<i></i> 属性和一个 result<i></i> 属性，其中后者是 <b>opPromise<sub>1</sub></b> 的结果。</p>

<p>对于 <i></i>list 中的下一个项目，<b>callback</b> 将收到包含上一个 <b>join.then</b> 中的 promise 的 <i></i>prev。接下来，我们创建 <b>opPromise<sub>1</sub>.then</b> 和 <b>opPromise<sub>2</sub></b> 的新的 join。因此，该 join 将在已经履行 <b>opPromise<sub>2</sub></b>，并且<i></i> <b>opPromise<sub>1</sub></b> 的 completed 处理程序已返回之后完成。就是这样！我们向该 <b>join</b> 附加的 <b>completed<sub>2</sub></b> 处理程序将不会被调用，直至 <b>completed<sub>1</sub></b> 已返回。</p>

<p>这种相同的依赖关系将针对 list 中的每个项目而构建 — 项目 n 的 <b>join.then</b> 中的 promise 将不会被履行，直至<i></i> <b>completed<sub>n</sub><i></i></b> 返回。这样可以保证 completed 处理程序以与 <i></i>list 相同的顺序调用。</p>

<h2>结束语</h2>

<p>在本博文中，我们已了解到 promise 本身只是一个代码构造或一个调用约定（尽管其很强大），它代表发货人和消费者之间的特定关系，发货人将在以后的任意时间点提供值，而消费者想知道他们何时可以获得这些值。因而，promise 非常适用于代表异步操作的结果，并可广泛用于使用 JavaScript 编写的 Windows 应用商店应用。promise 的规范还允许将顺序异步操作链接在一起，使每个中间结果从一个链接流向下一个。</p>

<p>Windows JavaScript 库 (WinJS) 提供了可靠的 promise 实现，您可以使用它来封装您的任何类型的操作。另外，它还为常见的应用场景（例如，拼接并行操作的 promise）提供了 helper。借助这些，WinJS 可允许使用更加有效和高效的方式处理异步操作。</p>

<p>Kraig Brockschmidt
  <br />Windows 生态系统团队项目经理

  <br /><a href="http://blogs.msdn.com/b/microsoft_press/archive/2012/10/29/free-ebook-programming-windows-8-apps-with-html-css-and-javascript.aspx">使用 HTML、CSS 和 JavaScript 编程 Windows 8 应用<i></i></a>作者</p>

<!-- Title: “关于 promise（用于使用 JavaScript 编写的 Windows 应用商店应用）” Tags: “JavaScript, Windows 应用商店应用, promise” --></div>]]></content:encoded>
    </item>
    <item>
      <title>优化 ListView 条目呈现</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_cn/listview</link>
      <pubDate>Tue, 25 Jun 2013 14:25:09 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_cn/2013/06/25/listview/</guid>
      <description><![CDATA[对于许多使用 JavaScript 编写的 Windows 应用商店应用（可以处理数据集合），与 WinJS ListView...]]></description>
      <content:encoded><![CDATA[<div class="PostContent"> <p>对于许多<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br211385.aspx" target="_blank">使用 JavaScript 编写的 Windows 应用商店应用</a>（可以处理数据集合），与 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br211837.aspx" target="_blank">WinJS ListView 控件</a>很好地协同工作是获得出色应用性能的基础。这并不奇怪：当处理数以千计的条目的管理和显示时，您对这些条目进行的每个数位的优化可能都发挥着重要的作用。最重要的是每个条目的呈现方式，也就是说，如何呈现以及何时呈现，ListView 控件中的每个条目都会构建于 DOM 中，并成为应用的可见部分。实际上，当用户在列表内快速平移时并且希望列表能够跟进平移的速度时，何时<i></i>呈现就成为一个重要的要素。</p>  <p>通过 HTML 中定义的声明性模板或通过一个自定义的 JavaScript 呈现函数<i></i>（将会为列表中的每个条目调用该函数），就可以在 ListView 中呈现条目。尽管使用声明性模板是最简单的，但是它不能对整个过程提供太多具体的控制。另一方面，呈现函数允许逐条目自定义呈现，并实现一定量的优化，<a href="http://code.msdn.microsoft.com/windowsapps/ListView-performance-39fb71f0" target="_blank">HTML ListView 优化性能示例</a>中演示了其中的一些示例。优化包括：</p>  <ul>   <li>允许异步传输条目数据和已呈现的元素（基本呈现函数支持该功能）。</li>    <li>将条目形状的创建与内部元素的创建分开，条目形状对于 ListView 的整体数据布局是必不可少的要素。这是通过一个占位符呈现器提供对该功能的支持。</li>    <li>重用以前创建的条目元素（及其子集），方法是替换数据、避免大部分元素创建步骤，此功能是通过一个循环占位符呈现器提供支持。</li>    <li>延迟耗费资源的可视性操作（如图片加载和动画显示）直到条目可见，并且 ListView 没有快速平移，此功能是通过一个多级呈现器来完成。</li>    <li>将这些相同的可视性操作同时进行批处理操作，以便利用多级批处理呈现器将重新呈现 DOM 的工作量最小化。</li> </ul>  <p>在本篇博文中，我们将逐一介绍所有这些阶段，并了解它们是如何与 ListView 的条目呈现过程相互配合。您可以想象得到，围绕着何时<i></i>呈现条目所进行的优化涉及大量的异步操作，因此会有大量的承诺。因此在这个过程中，基于先前发表的<a href="http://blogs.msdn.com/b/windowsappdev/archive/2013/06/10/all-about-promises-for-windows-store-apps-written-in-javascript.aspx" target="_blank">关于承诺</a>博文，我们还会对承诺本身有更深入的理解。</p>  <p>作为一条适用于所有呈现器的一般原则，令核心条目呈现时间（不包括延迟的操作）尽量最短非常重要。因为 ListView 的最终性能表现很大程度上取决于它的更新与屏幕刷新间隔的吻合程度，在条目呈现器上花费的额外的几个毫秒可能会使 ListView 的整体呈现时间推迟到下一个刷新间隔，导致丢失帧和视觉效果波动。也就是说，条目呈现器就是一个用于优化您的 JavaScript 代码的位置。</p>  <h2>基本呈现器</h2>  <p>我们先快速了解一下条目呈现函数（我简单地称为呈现器<i></i>）是什么样的。呈现器是您分配给 ListView 的 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh700705.aspx" target="_blank">itemTemplate</a> 属性而不是模板名称的一个函数，将会在必要时为 ListView 希望包括在 DOM 中的条目调用该函数。（另外，您可以在 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh700705.aspx" target="_blank">itemTemplate</a> 页查找到呈现器的基础说明文档，不过它是切实显示优化的示例。）</p>  <p>您可能希望条目呈现函数仅仅获得一个来自 ListView 数据源的条目。然后它将会创建该特定条目所需的 HTML 元素，然后返回根路径元素，ListView 可以将该根路径元素添加到 DOM。这是确凿出现的情况，但是还有两个额外的注意事项。首先，条目数据本身可以异步加载，因此，将元素创建与该数据的可用性相关联起来就很有必要。另外，呈现条目本身的过程可能还涉及其他异步操作，例如从远程 URI 加载图片，或者在条目数据中标识的其他文件中读取数据。事实上，我们将看到不同的优化级别，在条目元素请求和这些元素的实际传输之间允许任意数量的异步操作。</p>  <p>因此，重申一下，您可以看到呈现器会涉及到承诺。举例来说，ListView 并不直接为呈现器提供条目数据，它为该数据提供承诺。并非函数直接返回该条目的根路径元素，而是返回该元素的承诺。这允许 ListView 同时加入许多条目呈现承诺，并且等待（异步）直到整个页面得以呈现。事实上，它执行此操作以智能地管理构建不同页面的方式，首先构建可视条目的页面，然后构建用户最有可能下次向前平移和向后平移到的两个屏幕外页面。此外，具有所有这些承诺意味着，如果用户平移走，则 ListView 可以轻松地取消未完成条目的呈现，这样可以避免不必要的元素创建。</p>  <p>我们可以查看在示例的 <b>simpleRenderer</b> 函数中是如何使用这些承诺的：</p>  <p> </p>  <blockquote>   <div id="codeSnippetWrapper">     <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">function</span> simpleRenderer(itemPromise) {<br />    <span style="color: rgb(0, 0, 255);">return</span> itemPromise.then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />        <span style="color: rgb(0, 0, 255);">var</span> element = document.createElement(<span style="color: rgb(0, 96, 128);">&quot;div&quot;</span>);<br />        element.className = <span style="color: rgb(0, 96, 128);">&quot;itemTempl&quot;</span>;<br />        element.innerHTML = <span style="color: rgb(0, 96, 128);">&quot;&lt;img src='&quot;</span> + item.data.thumbnail +<br />            <span style="color: rgb(0, 96, 128);">&quot;' alt='Databound image' /&gt;&lt;div class='content'&gt;&quot;</span> + item.data.title + <span style="color: rgb(0, 96, 128);">&quot;&lt;/div&gt;&quot;</span>;<br />        <span style="color: rgb(0, 0, 255);">return</span> element;<br />    });<br />}<br /></pre>
  </div>
</blockquote>

<p>此代码首先会将已完成处理程序附加到 <b>itemPromise</b>。当条目数据可用并且有效地创建元素时，将调用该处理程序。不过，再次强调，我们实际上并没有直接返回元素 — 而是返回了利用该元素履行的承诺。也就是说，从 <b>itemPromise.then()</b>的返回值是利用该元素<i></i>履行的承诺（ListView 是否需要以及何时需要）。</p>

<p>返回承诺允许我们在必要时执行其他异步操作。在这种情况下，呈现器可以仅将这些中间承诺链接起来，从链中的最后一个 <b>then</b> 中返回承诺。例如：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">function</span> someRenderer(itemPromise) {<br />    <span style="color: rgb(0, 0, 255);">return</span> itemPromise.then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />        <span style="color: rgb(0, 0, 255);">return</span> doSomeWorkAsync(item.data);<br />    }).then(<span style="color: rgb(0, 0, 255);">function</span> (results) {<br />        <span style="color: rgb(0, 0, 255);">return</span> doMoreWorkAsync(results1);<br />    }).then(<span style="color: rgb(0, 0, 255);">function</span> (results2) {<br />        <span style="color: rgb(0, 0, 255);">var</span> element = document.createElement(<span style="color: rgb(0, 96, 128);">&quot;div&quot;</span>);<br />        <span style="color: rgb(0, 128, 0);">// use results2 to configure the element</span><br />        <span style="color: rgb(0, 0, 255);">return</span> element;<br />    });<br />}<br /></pre>
  </div>
</blockquote>

<p>请注意，这是一种我们在链的末尾不<i></i>使用 <b>done</b> 的情况，因为我们从最后一个 <b>then</b> 调用中返回了承诺。ListView 负责处理这个过程中可能引发的任何错误。</p>

<h2>占位符呈现器</h2>

<p>ListView 优化的下一个阶段使用占位符呈现器<i></i>，可以将元素构建分到两个不同阶段进行。这样将允许 ListView 只要求对于定义列表的整体布局必要的元素，而无需在每个条目内构建所有元素。因此，ListView 可以快速完成其布局过程，并且对后续的输入继续保持高度及时响应的状态。然后它可以在以后要求提供元素的其余部分。</p>

<p>占位符呈现器会返回一个具有两个属性的对象，而不是承诺：</p>

<ul>
  <li>element<i></i> 条目结构中的顶级元素，足以定义条目的大小和形状，它不依赖于条目数据。</li>

  <li>renderComplete<i></i> 当构建其余的元素内容时要履行的承诺，即，依旧会从 <b>itemPromise.then</b> 开始从您拥有的任意链处返回承诺。</li>
</ul>

<p>ListView 足够智能，可以检查您的呈现器是返回一个承诺（如上基本情况），还是返回具有 element<i></i> 和 renderComplete<i></i> 属性的对象（更为高级的情况）。因此先前 <b>simpleRenderer</b> 的同等占位符呈现器（在示例中）如下所示：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">function</span> placeholderRenderer(itemPromise) {<br />    <span style="color: rgb(0, 128, 0);">// create a basic template for the item that doesn't depend on the data</span><br />    <span style="color: rgb(0, 0, 255);">var</span> element = document.createElement(<span style="color: rgb(0, 96, 128);">&quot;div&quot;</span>);<br />    element.className = <span style="color: rgb(0, 96, 128);">&quot;itemTempl&quot;</span>;<br />    element.innerHTML = <span style="color: rgb(0, 96, 128);">&quot;&lt;div class='content'&gt;...&lt;/div&gt;&quot;</span>;<br /><br />    <span style="color: rgb(0, 128, 0);">// return the element as the placeholder, and a callback to update it when data is available</span><br />    <span style="color: rgb(0, 0, 255);">return</span> {<br />        element: element,<br /><br />        <span style="color: rgb(0, 128, 0);">// specifies a promise that will be completed when rendering is complete</span><br />        <span style="color: rgb(0, 128, 0);">// itemPromise will complete when the data is available</span><br />        renderComplete: itemPromise.then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />            <span style="color: rgb(0, 128, 0);">// mutate the element to include the data</span><br />            element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;.content&quot;</span>).innerText = item.data.title;<br />            element.insertAdjacentHTML(<span style="color: rgb(0, 96, 128);">&quot;afterBegin&quot;</span>, <span style="color: rgb(0, 96, 128);">&quot;&lt;img src='&quot;</span> +<br />                item.data.thumbnail + <span style="color: rgb(0, 96, 128);">&quot;' alt='Databound image' /&gt;&quot;</span>);<br />        })<br />    };<br />}<br /></pre>
  </div>
</blockquote>

<p>请注意，element.innerHTML<i></i> 分配可以移到 <b>renderComplete</b> 内，因为示例的 css/scenario1.css 文件中的itemTempl<i></i> 类会直接指定条目的宽度和高度。在 element<i></i> 属性中包括该类的原因是它在占位符中提供默认的 “…” 文本。您可以就像轻松地使用一个引用了在所有条目间共享的小型包内资源的 img<i></i> 元素那样（因此可以快速呈现）。</p>

<h2>循环占位符呈现器</h2>

<p>下一个优化是循环占位符<i></i>呈现器，不会在涉及承诺的位置添加任何新的内容。它会将第二个参数的感知度添加到名为 recycled<i></i> 的呈现器，它是以前呈现过但现在不再可见的条目的根路径元素。即，循环的元素已经有自己的子元素，因为您可以简单地替换该数据，可能需要对一些元素进行微调。对于全新条目所需的调用，可以避免大部分成本高昂的其他方式的元素创建调用，这样在呈现的过程中可以节省不少时间。</p>

<p>当它的 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br211835.aspx" target="_blank">loadingBehavior</a><b></b> 设置为“randomaccess”时，ListView 可能会提供一个循环的元素。如果指定循环<i></i>，则您可以将数据清除出元素（及其子元素），将其返回为占位符，然后在 renderComplete<i></i> 内填充数据并创建所有其他子元素（如果需要）。如果未提供循环的元素（当 ListView 首次创建或者当 <b>loadingBehavior</b> 为“incremental”时），您将再次创建该元素。下面是针对此种变化情况的示例代码：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">function</span> recyclingPlaceholderRenderer(itemPromise, recycled) {<br />    <span style="color: rgb(0, 0, 255);">var</span> element, img, label;<br />    <span style="color: rgb(0, 0, 255);">if</span> (!recycled) {<br />        <span style="color: rgb(0, 128, 0);">// create a basic template for the item that doesn't depend on the data</span><br />        element = document.createElement(<span style="color: rgb(0, 96, 128);">&quot;div&quot;</span>);<br />        element.className = <span style="color: rgb(0, 96, 128);">&quot;itemTempl&quot;</span>;<br />        element.innerHTML = <span style="color: rgb(0, 96, 128);">&quot;&lt;img alt='Databound image' style='visibility:hidden;'/&gt;&quot;</span> +<br />            <span style="color: rgb(0, 96, 128);">&quot;&lt;div class='content'&gt;...&lt;/div&gt;&quot;</span>;<br />    }<br />    <span style="color: rgb(0, 0, 255);">else</span> {<br />        <span style="color: rgb(0, 128, 0);">// clean up the recycled element so that we can reuse it </span><br />        element = recycled;<br />        label = element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;.content&quot;</span>);<br />        label.innerHTML = <span style="color: rgb(0, 96, 128);">&quot;...&quot;</span>;<br />        img = element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;img&quot;</span>);<br />        img.style.visibility = <span style="color: rgb(0, 96, 128);">&quot;hidden&quot;</span>;<br />    }<br />    <span style="color: rgb(0, 0, 255);">return</span> {<br />        element: element,<br />        renderComplete: itemPromise.then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />            <span style="color: rgb(0, 128, 0);">// mutate the element to include the data</span><br />            <span style="color: rgb(0, 0, 255);">if</span> (!label) {<br />                label = element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;.content&quot;</span>);<br />                img = element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;img&quot;</span>);<br />            }<br />            label.innerText = item.data.title;<br />            img.src = item.data.thumbnail;<br />            img.style.visibility = <span style="color: rgb(0, 96, 128);">&quot;visible&quot;</span>;<br />        })<br />    };<br />}<br /></pre>
  </div>
</blockquote>

<p>在 renderComplete<i></i> 中，请确保检查存在着您不是为新占位符创建的元素，例如 label<i></i>，如果需要请在此处创建他们。</p>

<p>如果您希望以更常规地方式清除循环的条目，请注意，您可以为 ListView 的 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br211846.aspx" target="_blank">resetItem</a><b></b> 属性提供一个函数。此函数将包含与上面所示类似的代码。对于 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh700726.aspx" target="_blank">resetGroupHeader</a><b></b> 属性也是同样，因为您可以对组标头以及条目使用模板功能。我们尚未涉及太多此话题，是因为组标头非常少，并且通常没有相同的性能影响。不过，此功能仍然强大。</p>

<h2>多级呈现器</h2>

<p>现在我们来看倒数第二级的优化，多级呈现器<i></i>。这会令循环占位符呈现器延迟加载图片和其他媒体，直到条目的其他部分已经在 DOM 中完全呈现。它还会延迟诸如动画显示之类的效果，直到条目确实完全显示在屏幕上。这是意识到用户会经常在 ListView 内快速平移，所以异步延迟很多耗费资源的操作直到 ListView 达到一种稳定的状态和位置，这是非常有意义的。</p>

<p>ListView 提供必要的挂钩作为来自 <b>itemPromise</b> 的 <b>item</b> 结果的成员：名为 <b>ready</b> 的属性（承诺）和两个方法 <b>loadImage</b> 和 <b>isOnScreen</b>，都会返回（更多！）承诺。即：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">renderComplete: itemPromise.then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />    <span style="color: rgb(0, 128, 0);">// item.ready, item.loadImage, and item.isOnScreen available</span><br />})<br /></pre>
  </div>
</blockquote>

<p>下面是它们的使用方式：</p>

<ul>
  <li><b>ready</b> 从链内第一个已完成处理程序中返回此承诺。当元素的整体结构已经被呈现并且可见时，就履行了此承诺。这意味着您可以利用一个已完成处理程序来链接另一个 <b>then</b>，在该处理程序中您可以执行其他可视化后操作，诸如加载图片。</li>

  <li><b>loadImage</b> 从 URI 中下载图片，并在指定的 img<i></i> 元素中显示该图片，然后返回一个利用同一元素履行的承诺。您可以将已完成处理程序附加到此承诺，它本身会从 <b>isOnScreen</b> 返回承诺。请注意，<b>loadImage</b> 将创建一个 img<i></i> 元素（如果未提供该元素的话），并且将其提供给您的已完成处理程序。</li>

  <li><b>isOnScreen</b> 返回一个承诺，其履行值为一个布尔值，指示该条目是否可见。在当前的实施中，这是一个已知值，因此将同步履行承诺。尽管是打包在一个承诺中，但是该值可以用在一个较长的链中。</li>
</ul>

<p>我们会在示例的 <b>multistageRenderer</b> 函数中查看到所有情形，其中图片加载的完成用于启动一个淡出动画。下面的内容是从 renderComplete<i></i> 承诺中返回的结果：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">renderComplete: itemPromise.then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />    <span style="color: rgb(0, 128, 0);">// mutate the element to update only the title</span><br />    <span style="color: rgb(0, 0, 255);">if</span> (!label) { label = element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;.content&quot;</span>); }<br />    label.innerText = item.data.title;<br /><br />    <span style="color: rgb(0, 128, 0);">// use the item.ready promise to delay the more expensive work</span><br />    <span style="color: rgb(0, 0, 255);">return</span> item.ready;<br />    <span style="color: rgb(0, 128, 0);">// use the ability to chain promises, to enable work to be cancelled</span><br />}).then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />    <span style="color: rgb(0, 128, 0);">// use the image loader to queue the loading of the image</span><br />    <span style="color: rgb(0, 0, 255);">if</span> (!img) { img = element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;img&quot;</span>); }<br />    <span style="color: rgb(0, 0, 255);">return</span> item.loadImage(item.data.thumbnail, img).then(<span style="color: rgb(0, 0, 255);">function</span> () {<br />        <span style="color: rgb(0, 128, 0);">// once loaded check if the item is visible</span><br />        <span style="color: rgb(0, 0, 255);">return</span> item.isOnScreen();<br />    });<br />}).then(<span style="color: rgb(0, 0, 255);">function</span> (onscreen) {<br />    <span style="color: rgb(0, 0, 255);">if</span> (!onscreen) {<br />        <span style="color: rgb(0, 128, 0);">// if the item is not visible, don't animate its opacity</span><br />        img.style.opacity = 1;<br />    } <span style="color: rgb(0, 0, 255);">else</span> {<br />        <span style="color: rgb(0, 128, 0);">// if the item is visible, animate the opacity of the image</span><br />        WinJS.UI.Animation.fadeIn(img);<br />    }<br />})<br /></pre>
  </div>
</blockquote>

<p>尽管有很多内容，我们仍然还有一个基本承诺链在这里。呈现器中的第一个异步操作更新了条目元素结构的简单部分，比如文本。然后它会在 <b>item.ready</b> 中返回承诺。当履行该承诺时，或者更准确地说，如果该承诺被履行后，我们使用条目的异步 <b>loadImage</b> 方法开始图片的加载，从已完成处理程序中返回 <b>item.isOnScreen</b> 承诺。这意味着 onscreen<i></i> 可见性标志会在链中传递到最终的已完成处理程序。是否履行以及何时履行了 <b>isOnScreen</b> 承诺，意味着该条目确实可见，我们可以执行类似动画等相关操作。</p>

<p>我强调是否履行了承诺，是因为当所有这一切正发生的时候，很可能又是用户在 ListView 内平移。将所有这些承诺链在一起，令 ListView 可能在这些条目平移出视图和/或任何缓存页面时取消异步操作。只要说 ListView 控件已经经历了很多<i></i>性能测试，这就足矣！</p>

<p>另外一点也很重要，就是要再次提醒我们自己在所有这些链中使用 <b>then</b>，因为我们仍会在 renderComplete<i></i> 属性内的呈现函数中返回了承诺。我们永远不会是这些呈现器中链的末尾，因此我们永远不会在末尾使用 <b>done</b>。</p>

<h2>缩略图批处理</h2>

<p>最后的优化确实是 ListView 控件的杀手锏<i></i>。在名为 <b>batchRenderer</b> 的函数中，我们发现了 renderComplete<i></i> 的此结构（已省略了大部分代码）：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">renderComplete: itemPromise.then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />    <span style="color: rgb(0, 128, 0);">// mutate the element to update only the title</span><br />    <span style="color: rgb(0, 0, 255);">if</span> (!label) { label = element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;.content&quot;</span>); }<br />    label.innerText = item.data.title;<br /><br />    <span style="color: rgb(0, 128, 0);">// use the item.ready promise to delay the more expensive work</span><br />    <span style="color: rgb(0, 0, 255);">return</span> item.ready;<br />    <span style="color: rgb(0, 128, 0);">// use the ability to chain promises, to enable work to be cancelled</span><br />}).then(<span style="color: rgb(0, 0, 255);">function</span> (item) {<br />    <span style="color: rgb(0, 128, 0);">// use the image loader to queue the loading of the image</span><br />    <span style="color: rgb(0, 0, 255);">if</span> (!img) { img = element.querySelector(<span style="color: rgb(0, 96, 128);">&quot;img&quot;</span>); }<br />    <span style="color: rgb(0, 0, 255);">return</span> item.loadImage(item.data.thumbnail, img).then(<span style="color: rgb(0, 0, 255);">function</span> () {<br />        <span style="color: rgb(0, 128, 0);">// once loaded check if the item is visible</span><br />        <span style="color: rgb(0, 0, 255);">return</span> item.isOnScreen();<br />    });<br />}).then(<span style="color: rgb(0, 0, 255);">function</span> (onscreen) {<br />    <span style="color: rgb(0, 0, 255);">if</span> (!onscreen) {<br />        <span style="color: rgb(0, 128, 0);">// if the item is not visible, don't animate its opacity</span><br />        img.style.opacity = 1;<br />    } <span style="color: rgb(0, 0, 255);">else</span> {<br />        <span style="color: rgb(0, 128, 0);">// if the item is visible, animate the opacity of the image</span><br />        WinJS.UI.Animation.fadeIn(img);<br />    }<br />})<br /></pre>
  </div>
</blockquote>

<p>这与 <b>multistageRenderer</b> 基本相同，除了在 <b>item.loadImage</b> 调用和 <b>item.isOnScreen</b> 检查之间对名为 <b>thumbnailBatch</b> 函数的神秘调用以外。在链中放置 <b>thumbnailBatch</b> 指示其返回值必须是已完成处理程序，其本身会返回另一个承诺。</p>

<p>有点糊涂了？好，我们现在就来探个究竟！不过首先需要一些有关我们将要完成内容的更多背景内容。</p>

<p>如果我们的 ListView 只具有一个条目，则各种不同的加载优化的效果可能不会那么显著。但 ListViews 通常具有很多条目，并且将会为每个条目调用呈现函数。在前面的 <b>multistageRenderer</b> 部分中，每个条目的呈现都会启动异步 <b>item.loadImage</b> 操作，从任意 URI 下载其缩略图，每个操作所需花费的时间都不固定。因此对于整个表，我们可能会同时调用一批 <b>loadImage</b>，等待其特定缩略图完成时会呈现每个条目。到目前为止一切顺利。</p>

<p>在 <b>multistageRenderer</b> 中并不是全部可见的一个重要特征是缩略图的 img<i></i> 元素已经<i></i>在 DOM 中，下载一经完成，<b>loadImage</b> 函数会设置该图片的 src<i></i> 属性。当我们一经从承诺链的其余部分返回时，这反过来也会触发呈现引擎的更新，会在该点后同步。</p>

<p>然后，在可能的情况下，一批缩略图也会在很短的时间内返回到 UI 线程。这会导致呈现引擎的过度繁忙，以及低劣的视觉效果。为了避免这种混乱，我们希望在这些 img<i></i> 元素位于 DOM 之前<i></i>完全创建它们，然后成批添加它们，以便可以在单一的呈现过程中处理这些元素。</p>

<p>该示例利用名为 <b>createBatch</b> 的函数通过一些承诺魔力代码来完成上述操作。仅会为整个应用调用一次 <b>createBatch</b>，其结果（另一个函数）会存储在名为 <b>thumbnailBatch</b> 的变量中：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">var</span> thumbnailBatch;<br />thumbnailBatch = createBatch();<br /></pre>
  </div>
</blockquote>

<p>对此 <b>thumbnailBatch</b> 函数的调用（就像我在这里引用它一样）会再次插入到呈现器的承诺链中。考虑到我们马上要看到的批处理代码的性质，此插入的目的就是将一组已加载的 img<i></i> 元素分组到一起，然后在适合的间隔释放它们以便进一步处理。只需要在呈现器中查看承诺链，对 <b>thumbnailBatch()</b> 的调用就必须返回已完成处理程序函数，该函数会返回一个承诺，该承诺的履行值（查看链中的下一步）必须是 img<i></i> 元素，可以添加到 DOM。在进行批处理后<i></i>将这些图片添加到 DOM，我们会将整个组添合并到同一呈现过程。</p>

<p>这是 <b>batchRenderer</b> 和上述 <b>multistageRenderer</b> 之间的重要区别：后来，缩略图的 img<i></i> 元素已经存在于 DOM 中，会作为第二个参数传递给 <b>loadImage</b>。因此当 <b>loadImage</b> 设置了图片的 src<i></i> 属性，将会触发呈现更新。不过，在 <b>batchRenderer</b> 内，该 img<i></i> 元素是在 <b>loadImage</b> 内单独创建的（其中 src<i></i> 也得到了设置），但是 img<i></i> 尚未在 DOM 中设置。它只是在 <b>thumbnailBatch</b> 步骤完成后添加到 DOM，令其成为单一布局过程内的一个组。</p>

<p>因此现在让我们来看一下批处理是如何工作的。下面是整个 <b>createBatch</b> 函数：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">function</span> createBatch(waitPeriod) {<br />    <span style="color: rgb(0, 0, 255);">var</span> batchTimeout = WinJS.Promise.<span style="color: rgb(0, 0, 255);">as</span>();<br />    <span style="color: rgb(0, 0, 255);">var</span> batchedItems = [];<br /><br />    <span style="color: rgb(0, 0, 255);">function</span> completeBatch() {<br />        <span style="color: rgb(0, 0, 255);">var</span> callbacks = batchedItems;<br />        batchedItems = [];<br />        <span style="color: rgb(0, 0, 255);">for</span> (<span style="color: rgb(0, 0, 255);">var</span> i = 0; i &lt; callbacks.length; i++) {<br />            callbacks[i]();<br />        }<br />    }<br /><br />    <span style="color: rgb(0, 0, 255);">return</span> <span style="color: rgb(0, 0, 255);">function</span> () {<br />        batchTimeout.cancel();<br />        batchTimeout = WinJS.Promise.timeout(waitPeriod || 64).then(completeBatch);<br /><br />        <span style="color: rgb(0, 0, 255);">var</span> delayedPromise = <span style="color: rgb(0, 0, 255);">new</span> WinJS.Promise(<span style="color: rgb(0, 0, 255);">function</span> (c) {<br />            batchedItems.push(c);<br />        });<br /><br />        <span style="color: rgb(0, 0, 255);">return</span> <span style="color: rgb(0, 0, 255);">function</span> (v) {<br />            <span style="color: rgb(0, 0, 255);">return</span> delayedPromise.then(<span style="color: rgb(0, 0, 255);">function</span> () {<br />                <span style="color: rgb(0, 0, 255);">return</span> v;<br />            });<br />        };<br />    };<br />}<br /></pre>
  </div>
</blockquote>

<p>再强调一次，<b>createBatch</b> 仅调用一次<i></i>，将会为列表中的每个<i></i>已呈现条目<i></i>调用一次其 <b>thumbnailBatch</b> 结果。然后将会调用 <b>thumbnailBatch</b> 生成的已完成处理程序，不论何时 <b>loadImage</b> 操作完成。</p>

<p>就好像已完成处理程序已经直接轻松地插入呈现函数一样，但是我们在这里要执行的是跨多个条目<i></i>之间的协调活动，而不是按条目执行。通过在 <b>createBatch</b> 开始处创建和初始化的两个变量来实现协调：batchedTimeout<i></i>,（初始化为一个空承诺）和 batchedItems<i></i>（初始化一个最初为空的函数数组）。<b>createBatch</b> 还会声明一个函数，<b>completeBatch</b> 只是清空 batchedItems<i></i> 在数组中调用每个函数：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">function</span> createBatch(waitPeriod) {<br />    <span style="color: rgb(0, 0, 255);">var</span> batchTimeout = WinJS.Promise.<span style="color: rgb(0, 0, 255);">as</span>();<br />    <span style="color: rgb(0, 0, 255);">var</span> batchedItems = [];<br /><br />    <span style="color: rgb(0, 0, 255);">function</span> completeBatch() {<br />        <span style="color: rgb(0, 0, 255);">var</span> callbacks = batchedItems;<br />        batchedItems = [];<br />        <span style="color: rgb(0, 0, 255);">for</span> (<span style="color: rgb(0, 0, 255);">var</span> i = 0; i &lt; callbacks.length; i++) {<br />            callbacks[i]();<br />        }<br />    }<br /><br />    <span style="color: rgb(0, 0, 255);">return</span> <span style="color: rgb(0, 0, 255);">function</span> () {<br />        batchTimeout.cancel();<br />        batchTimeout = WinJS.Promise.timeout(waitPeriod || 64).then(completeBatch);<br /><br />        <span style="color: rgb(0, 0, 255);">var</span> delayedPromise = <span style="color: rgb(0, 0, 255);">new</span> WinJS.Promise(<span style="color: rgb(0, 0, 255);">function</span> (c) {<br />            batchedItems.push(c);<br />        });<br /><br />        <span style="color: rgb(0, 0, 255);">return</span> <span style="color: rgb(0, 0, 255);">function</span> (v) {<br />            <span style="color: rgb(0, 0, 255);">return</span> delayedPromise.then(<span style="color: rgb(0, 0, 255);">function</span> () {<br />                <span style="color: rgb(0, 0, 255);">return</span> v;<br />            });<br />        };<br />    };<br />}<br /></pre>
  </div>
</blockquote>

<p>现在让我们看 <b>thumbnailBatch</b>（从 <b>createBatch</b> 返回的函数）内发生的情况，将会为每个要呈现的条目再次调用该函数。首先，我们取消<i></i>所有现有 batchedTimeout<i></i> 并立即重新创建它：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">batchTimeout<span style="color: rgb(255, 0, 0);">.</span>cancel();<br />        batchTimeout = WinJS<span style="color: rgb(255, 0, 0);">.</span>Promise<span style="color: rgb(255, 0, 0);">.</span>timeout(waitPeriod || 64)<span style="color: rgb(255, 0, 0);">.</span>then(completeBatch);<br /></pre>
  </div>
</blockquote>

<p>第二行显示在“关于承诺”博文 &lt;TODO:link>中介绍的未来的传输/履行模式：它会在延迟 waitPeriod<i></i> 毫秒后（默认值为 64ms）调用 <b>completeBatch</b>。这意味着只要在前一次调用的 waitPeriod<i></i> 内再次调用 <b>thumbnailBatch</b>，<b>batchTimeout</b> 将被重设为另一个 waitPeriod<i></i>。因为仅在 <b>item.loadImage</b> 调用完成之后<i></i>才会调用 <b>thumbnailBatch</b>，我们可以有把握地说，在上次调用的 waitPeriod<i></i> 内完成的任何 <b>loadImage</b> 操作都将包括在同一批处理中。当时间超过 waitPeriod<i></i> 时，就会处理该批处理 — 图片会添加到 DOM — 下一批处理开始。</p>

<p>在处理此超时业务后，<b>thumbnailBatch</b> 创建了一个新的承诺，只是将完成的调度程序函数推送到 batchedItems<i></i> 数组：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">var</span> delayedPromise = <span style="color: rgb(0, 0, 255);">new</span> WinJS.Promise(<span style="color: rgb(0, 0, 255);">function</span> (c) {<br />         batchedItems.push(c);<br />     });<br /></pre>
  </div>
</blockquote>

<p>请注意，在“关于承诺”&lt;TODO:link>中承诺只是一个代码构造，如此而已。新创建的承诺内及其自身没有异步行为：我们只是将完成的调度程序函数 c<i></i> 添加到 batchedItems<i></i>。不过，当然，我们在 batchedTimeout<i></i> 异步完成前不会利用该调度程序执行任何操作，因此事实上这里存在着异步关系。当发生超时时，我们清除了批处理（在 <b>completeBatch</b> 内），我们会将在别处指定的已完成处理程序调用到 <b>delayedPromise.then</b>。</p>

<p>这将会为我们带来 <b>createBatch</b> 中的最后一行代码，是 <b>thumbnailBatch</b> 实际返回的函数。此函数正是已完成处理程序，将会插入到呈现器的整个承诺链：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">return</span> <span style="color: rgb(0, 0, 255);">function</span> (v) {<br />           <span style="color: rgb(0, 0, 255);">return</span> delayedPromise.then(<span style="color: rgb(0, 0, 255);">function</span> () {<br />               <span style="color: rgb(0, 0, 255);">return</span> v;<br />           });<br />       };<br /></pre>
  </div>
</blockquote>

<p>事实上，让我们把这段代码直接放到承诺链中，因为我们可以看到产生的关系：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">return</span> item.loadImage(item.data.thumbnail);<br />          }).then(<span style="color: rgb(0, 0, 255);">function</span> (v) {<br />              <span style="color: rgb(0, 0, 255);">return</span> delayedPromise.then(<span style="color: rgb(0, 0, 255);">function</span> () {<br />                  <span style="color: rgb(0, 0, 255);">return</span> v;<br />              });<br />          ).then(<span style="color: rgb(0, 0, 255);">function</span> (newimg) {<br /></pre>
  </div>
</blockquote>

<p>现在，我们可以看到参数 v<i></i> 是 <b>item.loadImage</b> 的结果，是 img<i></i> 元素为我们创建的。如果我们不想执行批处理，我们可以就说 <b>return WinJS.Promise.as(v)</b>，整个链将仍可以工作：然后将同步传递 v<i></i>，并且在下一步骤中显示为 newimg<i></i>。</p>

<p>尽管如此，我们将会从 <b>delayedPromise.then</b> 返回承诺，将不会利用 v <i></i> 履行该承诺，直到履行了当前的 batchedTimeout<i></i>。这时 — 当 <b>loadImage</b> 完成之间存在 waitPeriod<i></i> 的间隙时 — 那些 img<i></i> 元素将会传输到链中的下一步，在那里它们会被添加到 DOM。</p>

<p>就先介绍这么多内容！</p>

<h2>结语</h2>

<p><a href="http://code.msdn.microsoft.com/windowsapps/ListView-performance-39fb71f0" target="_blank">HTML ListView 优化性能示例</a>中演示的五个不同的呈现函数都有一个共同的通性：他们显示 ListView 和呈现器之间的异步关系 — 通过承诺表示 — 为呈现器在如何以及何时为列表中的条目生成元素方面提供了极大的灵活性。在编写自己的应用时，您为 ListView 优化使用的策略很大程度上取决于您数据源的大小、条目本身的复杂性，以及您为这些条目异步获取的数据量（例如下载远程图片）。很明显，您希望在达到性能目标的前提下将条目呈现器尽量保持简单。但是，无论如何，您现在具有帮助 ListView — 和您的应用 — 达到性能最佳所需的所有工具。</p>

<p>Kraig Brockschmidt
  <br />Windows 生态系统团队项目经理

  <br /><a href="http://blogs.msdn.com/b/microsoft_press/archive/2012/10/29/free-ebook-programming-windows-8-apps-with-html-css-and-javascript.aspx">使用 HTML、CSS 和 JavaScript 编程 Windows 8 应用<i></i></a>作者</p>
<!-- Title: “优化 ListView 条目呈现” Tags: “Windows 应用商店应用, 呈现器, 批处理, 批, 呈现, ListView” --></div>]]></content:encoded>
    </item>
    <item>
      <title>参与Game On Virtual Event</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_cn/game-on-virtual-event</link>
      <pubDate>Wed, 12 Jun 2013 11:36:26 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_cn/2013/06/12/game-on-virtual-event/</guid>
      <description><![CDATA[游戏软件是 Windows 应用商店的重要组成部分。如果您计划在 Windows...]]></description>
      <content:encoded><![CDATA[<div class="PostContent"> <p>游戏软件是 Windows 应用商店的重要组成部分。如果您计划在 Windows 应用商店发布您的游戏，我们将非常荣幸的为您推荐这次有关于游戏开发的虚拟活动。无论您是游戏制作新手还是专业人士，这次的活动都将会是免费的和开放的。</p>  <p>我们将会使用 YoYo Game 的<a href="https://aka.ms/yoyogamesgamemaker" target="_blank">GameMaker: Studio</a> 来告诉您如何在最短的时间里发布一个高质量的游戏。GameMaker:Studio 拥有强大的内建设计功能和抓取工具，即便您没有相关的游戏开发经验它也能让您以最快的速度进入角色。如果您已经是拥有一定经验的游戏制作人，那么它的高级工具将会更加丰富您的经验，让您能够制作出独一无二的游戏。</p>  <p><a href="https://aka.ms/gameonevent"><img title="clip_image002" style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" border="0" alt="clip_image002" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/8666.clip_image002_0385EAB3.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/8666.clip_5F00_image002_5F00_0385EAB3.jpg" width="500" height="154" /></a></p>  <p>网上活动将会在太平洋时间的6月15日星期六启动，其将包括一个发布会和在线交流。在带您探索如何去开发游戏的同时，您将会有机会了解到更多形式的游戏以及如何加入图片，关卡以及游戏背景音乐等等。我们还会采用一对一的模式在整周连续不断的通过 GameMaker 论坛向您提供帮助。</p>  <p>无论您是一个初学者或者是第一次经历开发，又或者您是一个经验丰富的专业人士，这次的活动将会协助您开始游戏的制作，上传并最终发布到Windows 应用商店。想要了解更多的信息或是查看免费的资源和工具，您可以登录 <a href="http://msdnevents.com/gameon/" target="_blank">http://msdnevents.com/gameon/</a>.</p>  <p>--Robert Rodriguez-Lawson, 高级内容发布经理，Windows 内容制作人</p> <!-- Title: “参与Game On Virtual Event” Tags: “Windows 应用商店游戏, 游戏, 游戏启动, 活动” --></div>]]></content:encoded>
    </item>
    <item>
      <title>凭据保险箱：在 Windows 应用商店应用中处理用户和密码的解决方案</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_cn/windows-20915</link>
      <pubDate>Tue, 04 Jun 2013 19:40:00 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_cn/2013/06/04/windows-20915/</guid>
      <description><![CDATA[如果您还未听说过 Windows 应用商店应用可用的 凭据保险箱，现在就来了解一下相关信息吧。为什么呢？因为不仅可以简化存储和检索用户凭据的任务，它还可以安全地存储用户凭据，并且凭据可以随着用户的...]]></description>
      <content:encoded><![CDATA[<div class="PostContent">
<p>如果您还未听说过 Windows 应用商店应用可用的 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh465069.aspx" target="_blank">凭据保险箱</a>，现在就来了解一下相关信息吧。为什么呢？因为不仅可以简化<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh465060.aspx" target="_blank">存储</a>和<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/jj839731.aspx" target="_blank">检索用户凭据</a>的任务，它还可以安全地存储用户凭据，并且凭据可以随着用户的 Microsoft 帐户一起&ldquo;免费&rdquo;漫游。</p>
<p>假设您有一个应用，该应用会连接到一个服务以访问受保护的资源（如媒体文件、社交网络等）。您的服务需要每个用户的登录信息。因此，您已经在应用内构建了 UI，可以获取用户的用户名和密码，以便使用户登录到服务。这一切都运行流畅无误。</p>
<p>现在您希望为您的用户提供更好的功能，安全地存储他们的登录信息，这样，他们不必在每次使用您的应用时都需要登录一次。输入凭据保险箱。只需几次简单地调用凭据保险箱 API，您可以为用户存储用户名和密码，并可方便地检索用户名和密码，在下次用户打开应用时帮助用户方便地登录。</p>
<h2>安全存储</h2>
<p>凭据保险箱的一个显著优势就在于可以实现，应用将用户凭据存储在一个安全的位置，当存储在磁盘上时，凭据信息是加密保存的。您当然可以将应用的用户凭据存储在本地存储中的一个文件内，但是以纯文本的形式存储用户凭据会带来潜在的安全漏洞。如果用户的系统因某种方式受到了损害，则用户的用户名和密码就会被轻易地访问和控制。如果使用凭据保险箱来存储用户名和密码，则恶意来源最多就是得到一个加过密的文件。</p>
<p>每个 Windows PC 用户的凭据保险箱都是独一无二的，只有存储凭据的应用才可以访问凭据。也就是说，您只能检索为您的应用存储的凭据。类似地，其他任何应用都不能获取您的应用在保险箱中存储的凭据。</p>
<h2>漫游凭据</h2>
<p>用户可以获得的另一项好处是，当您使用凭据保险箱存储用户的用户名和密码时，存储的凭据可以随用户的 Microsoft 帐户漫游到他们使用该 Microsoft 帐户的任何其他可信机器。这可以令安全应用对您的用户来说更为方便，因为您的应用可以自动为用户登录 - 无需重新提示用户输入凭据 - 在他们已经利用 Microsoft 帐户安装了您的应用并且有相关 Microsoft 帐户的任何可信电脑上。</p>
<p>其工作方式与域帐户有一点不同。如果存在利用 Microsoft 帐户存储的凭据，并且您将该帐户与域帐户相关联（例如您工作时使用的帐户），则您的凭据将漫游到该域帐户。不过，利用域帐户登录时添加的任何新凭据将不会漫游。这可以确保该域的私有凭据不会在域以外公开。</p>
<h2>存储用户凭据</h2>
<p>在凭据保险箱中存储用户凭据是一个快捷的两步过程。首先，您使用来自 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.security.credentials.aspx" target="_blank">Windows.Security.Credentials</a> 命名空间中的 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.security.credentials.passwordvault.aspx" target="_blank">PasswordVault</a> 对象获取对凭据保险箱的引用。然后，您创建一个 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.security.credentials.passwordcredential.aspx" target="_blank">PasswordCredential</a> 对象，其中包含您的应用的标识符、用户名和密码，并将这些信息传递给 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh701231.aspx" target="_blank">PasswordVault.Add()</a> 方法以便将凭据添加到保险箱。</p>
<p><strong>C#</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;">var vault = <span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordVault();<br />vault.Add(<span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordCredential(<br />    <span style="color: #006080;">"My App"</span>, username, password));</pre>
</div>
</blockquote>
<p><strong>JavaScript</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">var</span> vault = <span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordVault();<br />vault.add(<span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordCredential(<br />    <span style="color: #006080;">"My App"</span>, username, password));</pre>
</div>
</blockquote>
<h2>检索用户凭据</h2>
<p>在拥有对 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.security.credentials.passwordvault.aspx" target="_blank">PasswordVault</a> 对象的引用后，您可以有几种方式来从凭据保险箱检索用户凭据。</p>
<ul>
<li>您可以利用 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.security.credentials.passwordvault.retrieveall.aspx" target="_blank">PasswordVault.RetrieveAll()</a> 方法在保险箱中检索用户为您的应用提供的所有凭据。</li>
<li>如果您知道已存储凭据的用户名，则可以利用 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.security.credentials.passwordvault.findallbyusername.aspx" target="_blank">PasswordVault.FindAllByUserName()</a> 方法为该用户名检索所有凭据。</li>
<li>如果您知道已存储凭据的资源名称，则可以利用 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.security.credentials.passwordvault.findallbyresource.aspx" target="_blank">PasswordVault.FindAllByResource()</a> 方法为该资源名称检索所有凭据。</li>
<li>最后，如果您既知道凭据的用户名又知道资源名称，则可以利用 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.security.credentials.passwordvault.retrieve.aspx" target="_blank">PasswordVault.Retrieve()</a> 方法只检索该凭据。</li>
</ul>
<p>让我们来看一个我们在应用中全局存储资源名称，并且如果我们为用户发现了凭据就会为用户自动登录的示例。如果我们为用户查找发现了多个凭据，我们会询问用户选择一个在登录时使用的默认凭据。</p>
<p><strong>C#</strong></p>
<blockquote>
<div>
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">string</span> resourceName = <span style="color: #006080;">"My App"</span>;<br /><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">string</span> defaultUserName;<br /><br /><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span> Login()<br />{<br />    var loginCredential = GetCredentialFromLocker();<br /><br />    <span style="color: #0000ff;">if</span> (loginCredential != <span style="color: #0000ff;">null</span>)<br />    {<br />        <span style="color: #008000;">// There is a credential stored in the locker.</span><br />        <span style="color: #008000;">// Populate the Password property of the credential</span><br />        <span style="color: #008000;">// for automatic login.</span><br />        loginCredential.RetrievePassword();<br />    }<br />    <span style="color: #0000ff;">else</span><br />    {<br />        <span style="color: #008000;">// There is no credential stored in the locker.</span><br />        <span style="color: #008000;">// Display UI to get user credentials.</span><br />        loginCredential = GetLoginCredentialUI();<br />    }<br /><br />    <span style="color: #008000;">// Log the user in.</span><br />    ServerLogin(loginCredential.UserName, loginCredential.Password);<br />}<br /><br /><br /><span style="color: #0000ff;">private</span> Windows.Security.Credentials.PasswordCredential GetCredentialFromLocker()<br />{<br />    Windows.Security.Credentials.PasswordCredential credential = <span style="color: #0000ff;">null</span>;<br /><br />    var vault = <span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordVault();<br />    var credentialList = vault.FindAllByResource(resourceName);<br />    <span style="color: #0000ff;">if</span> (credentialList.Count &gt; 0)<br />    {<br />        <span style="color: #0000ff;">if</span> (credentialList.Count == 1)<br />        {<br />            credential = credentialList[0];<br />        }<br />        <span style="color: #0000ff;">else</span><br />        {<br />            <span style="color: #008000;">// When there are multiple usernames,</span><br />            <span style="color: #008000;">// retrieve the default username. If one doesn&rsquo;t</span><br />            <span style="color: #008000;">// exist, then display UI to have the user select</span><br />            <span style="color: #008000;">// a default username.</span><br /><br />            defaultUserName = GetDefaultUserNameUI();<br /><br />            credential = vault.Retrieve(resourceName, defaultUserName);<br />        }<br />    }<br /><br />    <span style="color: #0000ff;">return</span> credential;<br />}</pre>
</div>
</blockquote>
<div><br /><strong>JavaScript</strong></div>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">var</span> resourceName = <span style="color: #006080;">"My App"</span>;<br /><span style="color: #0000ff;">var</span> defaultUserName;<br /><br /><span style="color: #0000ff;">function</span> login() {<br />    <span style="color: #0000ff;">var</span> loginCredential = getCredentialFromLocker();<br /><br />    <span style="color: #0000ff;">if</span> (loginCredential != <span style="color: #0000ff;">null</span>) {<br />        <span style="color: #008000;">// There is a credential stored in the locker.</span><br />        <span style="color: #008000;">// Populate the Password property of the credential</span><br />        <span style="color: #008000;">// for automatic login.</span><br />        loginCredential.retrievePassword();<br />    } <span style="color: #0000ff;">else</span> {<br />        <span style="color: #008000;">// There is no credential stored in the locker.</span><br />        <span style="color: #008000;">// Display UI to get user credentials.</span><br />        loginCredential = getLoginCredentialUI();<br />    }<br /><br />    <span style="color: #008000;">// Log the user in.</span><br />    serverLogin(loginCredential.userName, loginCredential.password);<br />}<br /><br /><br /><span style="color: #0000ff;">function</span> GetCredentialFromLocker() {<br />    <span style="color: #0000ff;">var</span> credential = <span style="color: #0000ff;">null</span>;<br /><br />    <span style="color: #0000ff;">var</span> vault = <span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordVault();<br />    <span style="color: #0000ff;">var</span> credentialList = vault.findAllByResource(resourceName);<br />    <span style="color: #0000ff;">if</span> (credentialList.length &gt; 0) {<br />        <span style="color: #0000ff;">if</span> (credentialList.length == 1) {<br />            credential = credentialList[0];<br />        } <span style="color: #0000ff;">else</span> {<br />            <span style="color: #008000;">// When there are multiple usernames,</span><br />            <span style="color: #008000;">// retrieve the default username. If one doesn&rsquo;t</span><br />            <span style="color: #008000;">// exist, display UI to have the user select</span><br />            <span style="color: #008000;">// a default username.</span><br /><br />            defaultUserName = getDefaultUserNameUI();<br />            credential = vault.retrieve(resourceName, defaultUserName);<br />        }<br />    }<br /><br />    <span style="color: #0000ff;">return</span> credential;<br />}</pre>
</div>
</blockquote>
<h2>删除用户凭据</h2>
<p>在凭据保险箱中删除用户凭据也是一个快捷的两步过程。再一次，您使用来自 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.security.credentials.aspx" target="_blank">Windows.Security.Credentials</a> 命名空间中的 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.security.credentials.passwordvault.aspx" target="_blank">PasswordVault</a> 对象获取对凭据保险箱的引用。然后，您将要删除的凭据传递给 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh701242.aspx" target="_blank">PasswordVault.Remove()</a> 方法。</p>
<p><strong>C#</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;">var vault = <span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordVault();<br />vault.Remove(<span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordCredential(<br />    <span style="color: #006080;">"My App"</span>, username, password));</pre>
</div>
</blockquote>
<p><strong>JavaScript</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">var</span> vault = <span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordVault();<br />vault.remove(<span style="color: #0000ff;">new</span> Windows.Security.Credentials.PasswordCredential(<br />    <span style="color: #006080;">"My App"</span>, username, password));</pre>
</div>
</blockquote>
<h2>安全且易用</h2>
<p>正如您所见，凭据保险箱是一种易于使用的功能，可以实现验证用户身份以及存储用户凭据以便将来使用这些任务的简化 - 所有这些都是在非常安全的方式下实现。</p>
<p>其他 Windows 8 应用身份验证主题包括 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh465281.aspx" target="_blank">Web 身份验证代理</a>，您可以使用该代理从网站（例如 OAuth）检索身份验证标记，<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh770853.aspx" target="_blank">并基于用户的 Microsoft 帐户来个性化您的应用</a>。</p>
<p>有关更多信息，请参阅：</p>
<ul>
<li><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br211367.aspx" target="_blank">管理用户信息 (JavaScript)</a></li>
<li><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/xaml/BR229572(v=win.10).aspx" target="_blank">管理用户信息（使用 C#/VB/C++ 和 XAML 的 Windows 应用商店应用）</a></li>
<li><a href="http://code.msdn.microsoft.com/windowsapps/PasswordVault-f01be74a" target="_blank">凭据保险箱示例</a></li>
<li><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.security.credentials.aspx" target="_blank">Windows.Security.Credentials</a></li>
</ul>
<p>&nbsp;</p>
<p>--Windows 开发者内容，高级内容开发者 Doug Rothaus</p>
<p>特别感谢 Yashar Bahman 对本篇博文撰写所作出的贡献。</p>
<!-- Title: “凭据保险箱：在 Windows 应用商店应用中处理用户和密码的解决方案” Tags: “检索凭据, 存储凭据, 安全存储, 凭据保险箱” --></div>]]></content:encoded>
    </item>
    <item>
      <title>使用地理位置和 Bing 地图创建位置感知应用</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_cn/bing</link>
      <pubDate>Tue, 28 May 2013 11:01:44 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_cn/2013/05/28/bing/</guid>
      <description><![CDATA[通过将用户的位置一并键入您的应用的业务逻辑，您可以方便地为用户提供其当前所在地理位置的天气、优惠券、体育活动及赛事或音乐会等相关信息。您可以使用 Windows 8 的地理位置功能以及 Bing...]]></description>
      <content:encoded><![CDATA[<div class="PostContent"> <p>通过将用户的位置一并键入您的应用的业务逻辑，您可以方便地为用户提供其当前所在地理位置的天气、优惠券、体育活动及赛事或音乐会等相关信息。您可以使用 Windows 8 的<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br229921.aspx" target="_blank">地理位置</a>功能以及 Bing Maps SDK，创建能够感知位置的出色 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh465148.aspx" target="_blank">Windows 应用商店应用</a>。</p>  <p>让我们来看一下，您的应用如何利用 IP 地址、WiFi 网络或 GPS 数据使用<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br225603.aspx" target="_blank">位置服务</a>来为用户提供特定于位置的信息。一旦您有了位置信息，就可以使用 Bing 地图 SDK 向用户显示他们所在的位置。</p>  <h2>检索位置</h2>  <p><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.devices.geolocation.aspx">Windows.Devices.Geolocation</a> 命名空间用于检索您的位置。您使用 GPS、WiFi 网络还是 IP 地址来检索用户的位置取决以下两个主要因素：</p>  <ol>   <li>所要求的位置精确度级别。这是符合您的应用需求的理想精确度级别。</li>    <li>位置数据的可用性。就是说，并不是所有的设备都内置 GPS。或者，当请求位置时，网络或 Wi-Fi 连接可能不可用。</li> </ol>  <h3><a>所需的精确度</a></h3>  <p>请求位置时，您的应用应指定<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.devices.geolocation.geolocator.desiredaccuracy.aspx" target="_blank">需要的精确度</a>，以便尽快地返回给定范围内的位置数据。每种位置类型具有不同的精确程度：</p>  <ul>   <li><a href="http://zh.wikipedia.org/wiki/Gps" target="_blank">GPS</a> – GPS 提供的位置精确度最高，可以精确到大约 10 米。不过，如果 GPS 传感器需要“唤醒”并获取卫星数据，然后再对数据进行三角化处理，可能需要一到两分钟返回位置详细信息。不过，GPS 一经获得卫星连接，它就可以快速地获取下一个位置点。当然，使用 GPS 越多，消耗的电量就越大。<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.devices.geolocation.geolocator.desiredaccuracy.aspx" target="_blank">Geolocator.DesiredAccuracy</a> 属性必须设置为“高”，这样 GPS 才能获取数据。</li>    <li><a href="http://zh.wikipedia.org/wiki/Wi-Fi" target="_blank">Wi-Fi</a> – Wi-Fi 连接可以提供 300-500 米的精确度。如果连接正常，则返回位置数据的速度要快于 GPS 获取卫星连接的速度。将 DesiredAccuracy 属性设置为“默认”，可以利用 Wi-Fi 获取位置数据。</li>    <li><a href="http://zh.wikipedia.org/wiki/IP_address" target="_blank">IP</a> – IP 级别的分辨力可以达到 1 公里级别的精确度。如果网络连接正常，则返回位置数据的速度会比 GPS 实现卫星连接的速度要快。您还可以将 DesiredAccuracy 属性设置为默认，这样可以利用 IP 获取位置数据。位置服务返回它能获取的最高精度位置数据，并且您可以通过 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.devices.geolocation.geocoordinate.accuracy.aspx" target="_blank">Geocoordinate.Accuracy</a> 属性知道精确度。</li> </ul>  <p>请仔细考虑您所需的精确度值。它会影响您的应用的性能和耗电量。例如，如果您的应用提供天气预报，您几乎无需 GPS 的高精度。还要确保基于返回数据的精确度显示错误半径，以显示您的数据的精确度。下面是 C# 代码来设置您所需的精确度：</p>  <p><strong>C#</strong></p>  <blockquote>   <div id="codeSnippetWrapper">     <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">Geolocator geolocator = <span style="color: rgb(0, 0, 255);">new</span> Geolocator();<br />geolocator.DesiredAccuracy = Windows.Devices.Geolocation.PositionAccuracy.High;<br /></pre>
  </div>
</blockquote>

<br />不论您需要何种精确程度，获取您的位置的数据调用为：

<p><strong>C#</strong></p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">Geoposition pos = await geolocator.GetGeopositionAsync().AsTask(token);</pre>
  </div>
</blockquote>

<p>下面是 JavaScript 代码创建 Geolocator 类、设置所需精确度，然后获取当前位置的实例。请注意，下面的 ‘promise’ 变量是 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br211867.aspx" target="_blank">WinJS.Promise</a> 对象 – 您需要使用 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh701079.aspx" target="_blank">done</a> 方法来从对 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br225537.aspx" target="_blank">getGeopositionAsync</a> 的调用中访问数据。</p>

<p><strong>JavaScript</strong></p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">var</span> geolocator = Windows.Devices.Geolocation.Geolocator();<br />geolocator.desiredAccuracy =<br />Windows.Devices.Geolocation.PositionAccuracy.high;<br /><br /><span style="color: rgb(0, 128, 0);">// Get the geoposition, capturing the request in a 'promise' object.</span><br /><span style="color: rgb(0, 0, 255);">var</span> promise = geolocator.getGeopositionAsync();<br /></pre>
  </div>
</blockquote>

<br />请记住，如果您将所需的精确度设置为“高”，不能保证用户的设备就可以返回高精确度。他们的电脑可能没有 GPS，也可能关闭了该功能，或者未连接到 Wi-Fi 等。请始终准备好小心地处理这些较低精确度数据的实例。您如何处理取决于您的应用的功能。您要继续使用低精确度的数据吗？您要获取 GPS 级别的精确度吗？请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh465148.aspx" target="_blank">开发位置感知应用的指导性原则</a>了解更多详细信息。

<h3>Bing 地图</h3>

<p><a href="http://apps.microsoft.com/webpdp/app/97a2179c-38be-45a3-933e-0d2dbf14a142" target="_blank">Bing 地图应用</a>是使自己适应所获得位置数据的精确度的应用。在一台桌面电脑上，通常不会有 GPS 或 Wi-Fi，它会基于 IP 地址查找位置，<a>然后在绘制当前位置的地图时放大到地区级别。</a>笔记本电脑上通常具有一个无线卡，它使用 Wi-Fi 来获取更精确的位置，然后在显示当前位置时进一步放大。如果用户放得更大，则当前位置图标会显示一个错误半径。在具有 GPS 功能的设备上，Bing 地图从 GPS 获取高精确度的位置数据，并显示一个更为精准的位置。</p>

<h3>必应 Bing 天气</h3>

<p>如上所述，像<a href="http://apps.microsoft.com/webpdp/app/421ba874-f903-4965-9b82-d60f3ba3cae0" target="_blank">必应 Bing 天气</a>这样的应用只需要区域信息。因此，它可以指定，它仅需低精确度数据，从而可以更快地获取位置数据值，这使得该应用的响应速度更快。其他只需要区域级别信息的应用的示例包括电台指南、购物信息或当地新闻资讯等。</p>

<h2>利用 Bing 地图 SDK 显示位置数据</h2>

<p>您调用 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.devices.geolocation.geolocator.getgeopositionasync.aspx" target="_blank">GetGeopositionAsync</a> 获得位置信息后，可能希望在地图上显示该位置信息。</p>

<ul>
  <li>首先，请将 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.devices.geolocation.geoposition.aspx" target="_blank">Geoposition</a> 转换为 Bing 地图可以使用的 <a href="http://msdn.microsoft.com/zh-cn/library/hh846500.aspx" target="_blank">位置</a>，使用代码如下：</li>
</ul>

<p> </p>

<p><strong>C#</strong></p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">Location location = <span style="color: rgb(0, 0, 255);">new</span> Location(pos.Coordinate.Latitude, pos.Coordinate.Longitude);</pre>
  </div>
</blockquote>

<p><strong>JavaScript：</strong></p>

<blockquote>
  <div>
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">promise.done(<br />  <span style="color: rgb(0, 0, 255);">function</span> (pos) {<br />    <span style="color: rgb(0, 128, 0);">// Get the coordinates of the current location.</span><br />    <span style="color: rgb(0, 0, 255);">var</span> coord = pos.coordinate,<br />    location = <span style="color: rgb(0, 0, 255);">new</span> Microsoft.Maps.Location(coord.latitude, coord.longitude);<br /> <br />  },<br />  <span style="color: rgb(0, 0, 255);">function</span> (err) {<br />    <span style="color: rgb(0, 128, 0);">// Handle the error.</span><br />  });<br /></pre>
  </div>
</blockquote>

<p> </p>

<ul>
  <li>接下来，使用 Bing 地图显示位置。您可以在<a href="http://visualstudiogallery.msdn.microsoft.com/bb764f67-6b2c-4e14-b2d3-17477ae1eaca">此处</a>获取 Bing 地图 SDK。您还需要在 Bing 地图门户上注册以获取 Bing 地图密钥，并按照<a href="http://msdn.microsoft.com/zh-cn/library/ff428642.aspx">获取 Bing 地图密钥</a>中的说明操作。注册时，请确保您查看了每个类型的密钥的使用限制，选择一个适合您的应用的密钥。

    <br />为您的设计工具添加 Bing 地图控件，并将密钥插入凭据中。</li>
</ul>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">Page</span><br />    <span style="color: rgb(255, 0, 0);">xmlns</span><span style="color: rgb(0, 0, 255);">=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;</span><br />    <span style="color: rgb(255, 0, 0);">xmlns:x</span><span style="color: rgb(0, 0, 255);">=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;</span><br />    <span style="color: rgb(255, 0, 0);">xmlns:local</span><span style="color: rgb(0, 0, 255);">=&quot;using:SimpleMapping&quot;</span><br />    <span style="color: rgb(255, 0, 0);">xmlns:d</span><span style="color: rgb(0, 0, 255);">=&quot;http://schemas.microsoft.com/expression/blend/2008&quot;</span><br />    <span style="color: rgb(255, 0, 0);">xmlns:mc</span><span style="color: rgb(0, 0, 255);">=&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;</span><br />    <span style="color: rgb(255, 0, 0);">xmlns:Maps</span><span style="color: rgb(0, 0, 255);">=&quot;using:Bing.Maps&quot;</span><br />    <span style="color: rgb(255, 0, 0);">x:Class</span><span style="color: rgb(0, 0, 255);">=&quot;SimpleMapping.MainPage&quot;</span><br />    <span style="color: rgb(255, 0, 0);">mc:Ignorable</span><span style="color: rgb(0, 0, 255);">=&quot;d&quot;</span><span style="color: rgb(0, 0, 255);">&gt;</span><br /><br />    <span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">Grid</span> <span style="color: rgb(255, 0, 0);">Background</span><span style="color: rgb(0, 0, 255);">=&quot;{StaticResource ApplicationPageBackgroundThemeBrush}&quot;</span><span style="color: rgb(0, 0, 255);">&gt;</span><br />        <span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">Maps:Map</span> <span style="color: rgb(255, 0, 0);">x:Name</span><span style="color: rgb(0, 0, 255);">=&quot;Map&quot;</span> <span style="color: rgb(255, 0, 0);">Margin</span><span style="color: rgb(0, 0, 255);">=&quot;0,120,0,0&quot;</span> <span style="color: rgb(255, 0, 0);">Credentials</span><span style="color: rgb(0, 0, 255);">=&quot;Insert Your Bing Maps Key Here&quot;</span><span style="color: rgb(0, 0, 255);">/&gt;</span><br />    <span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">Grid</span><span style="color: rgb(0, 0, 255);">&gt;</span><br /><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">Page</span><span style="color: rgb(0, 0, 255);">&gt;</span></pre>
  </div>
</blockquote>

<ul>
  <li>现在您可以在应用内显示地图的页面中使用以下代码。这会设置地图的缩放级别，并显示位置。请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/jj850315.aspx" target="_blank">如何在 Bing 地图上显示您的位置</a>了解关于缩放级别的更多详细信息：</li>
</ul>

<p><strong>C#</strong></p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">double</span> zoomLevel = 13.0f;<br /><br />Map.SetView(location, zoomLevel);<br /></pre>
  </div>
</blockquote>

<br />使用 JavaScript 和 HTML 向您的应用添加 Bing 地图控件所需的工作更多一些。将以下 &lt;script&gt; 标记添加到您的应用的 HTML：

<div> </div>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">script</span> <span style="color: rgb(255, 0, 0);">type</span><span style="color: rgb(0, 0, 255);">=&quot;text/javascript&quot;</span> <br />   <span style="color: rgb(255, 0, 0);">src</span><span style="color: rgb(0, 0, 255);">=&quot;ms-appx:///Bing.Maps.JavaScript//js/veapicore.js&quot;</span><span style="color: rgb(0, 0, 255);">&gt;</span><br />/script<span style="color: rgb(0, 0, 255);">&gt;</span><br /></pre>
  </div>
</blockquote>

<p>然后，您需要创建 &lt;div&gt; 标记以包含地图，设置 id 特性以便您可以访问地图。</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">div</span> <span style="color: rgb(255, 0, 0);">id</span>=’<span style="color: rgb(255, 0, 0);">map-div</span>’<span style="color: rgb(0, 0, 255);">&gt;&lt;/</span><span style="color: rgb(128, 0, 0);">div</span><span style="color: rgb(0, 0, 255);">&gt;</span></pre>
  </div>
</blockquote>

<p>最后，您需要在 HTML 页面中初始化地图控件。以下是可以实现上述功能的一些 JavaScript 代码：</p>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 128, 0);">// Load the Microsoft.Maps.Map module and then call getMap when the </span><br /><span style="color: rgb(0, 128, 0);">// module has loaded.</span><br /><span style="color: rgb(0, 0, 255);">function</span> initMap() {<br />  Microsoft.Maps.loadModule(<span style="color: rgb(0, 96, 128);">'Microsoft.Maps.Map'</span>, <br />    { <br />      callback: getMap, <br />      culture: <span style="color: rgb(0, 96, 128);">&quot;en&quot;</span>, <br />      homeRegion: <span style="color: rgb(0, 96, 128);">&quot;US&quot;</span> <br />  });<br />}<br /><br /><span style="color: rgb(0, 128, 0);">// Get the div to contain the map from the interface of the app.</span><br /><span style="color: rgb(0, 0, 255);">function</span> getMap() {<br /><br />  <span style="color: rgb(0, 128, 0);">// Create a new instance of the Map class, passing in the app ID.</span><br />  <span style="color: rgb(0, 0, 255);">var</span> map = <span style="color: rgb(0, 0, 255);">new</span> Microsoft.Maps.Map(<br />    document.getElementById(mapDivId), <br />    { credentials: “Insert your credentials here” });<br /><br />  <span style="color: rgb(0, 128, 0);">// Set the initial zoom level and type of map.</span><br />  map.setView({<br />    mapTypeId: Microsoft.Maps.MapTypeId.road,<br />    zoom: 3<br />  });<br />}<br /></pre>
  </div>
</blockquote>

<ul>
  <li>最后，使用位置的任何应用必须遵守用户的权限设置（该设置可以随时变更）。天气应用使用位置来获取当前位置的天气。但是在任何时候，用户可以转至“设置”，将位置权限变为关闭。您的应用始终需要能够处理位置权限处于禁用状态下的情形。在这种情况下，天气应用会显示“找不到您的位置。将您的权限更改为允许天气使用您的位置。”消息。请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/Hh465148(v=win.10).aspx" target="_blank">位置感知应用的指导原则</a>中的用户体验原则来查看处理位置权限的示例。

    <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0882.bing-weather_2FEEBCEF.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0882.bing_2D00_weather_5F00_2FEEBCEF.jpg"><img title="必应 Bing 天气应用" style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" border="0" alt="必应 Bing 天气应用使用位置来获取当前位置的天气" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/7356.bing-weather_thumb_084840C5.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/7356.bing_2D00_weather_5F00_thumb_5F00_084840C5.jpg" width="600" height="450" /></a></li>
</ul>

<p> </p>

<p>有关使用地理位置和 Bing 地图 SDK 的详细信息，<a></a><a>我们推出了</a><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/jj850315.aspx" target="_blank">视频、帮助主题</a>和<a href="http://code.msdn.microsoft.com/windowsapps/Simple-geolocation-with-7b385c3c" target="_blank">代码示例</a>，可以指导您完成异步获取位置然后使用 Bing 地图 SDK 以可见方式显示所处位置的步骤。视频、帮助主题和示例指导您启用位置功能、异步获取位置数据、将地图设置到指定的位置以及根据位置数据的精确度来设置位置图标。您还可以在<a href="http://msdn.microsoft.com/zh-cn/library/hh846481.aspx" target="_blank">适用于 Windows 应用商店应用的 Bing 地图</a>中查看更多信息。</p>

<h2>结束语</h2>

<p>位置是强大且省时的功能，可以添加到您的应用。您可以基于用户所在的位置为他们提供更为切实有用的信息。不论是当地电台、购物信息、交通信息或天气信息，基于位置对信息进行筛选和定制可以让您的应用发挥更大的作用。并且这些功能可以自动实现，无需用户介入，节省了用户时间，令他们更为喜欢使用您的应用。</p>

<p>--Windows 开发者内容，高级内容开发者 Ross Heise</p>
<p>   特别感谢 Jon Kay 和 Eric Schmidt 对本篇博文撰写所作出的贡献。</p>
<!-- Title: “使用地理位置和 Bing 地图创建位置感知应用” Tags: “Bing 地图, 地理位置, Windows.设备。地理位置, 位置服务” --></div>]]></content:encoded>
    </item>
    <item>
      <title>介绍一种新的 Windows 开发人员中心体验</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_cn/windows</link>
      <pubDate>Thu, 16 May 2013 09:53:00 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_cn/2013/05/16/windows/</guid>
      <description><![CDATA[自 2011 年 10 月推出 Windows...]]></description>
      <content:encoded><![CDATA[<div class="PostContent">
<p>自 2011 年 10 月推出 <a href="http://msdn.microsoft.com/zh-cn/windows/default.aspx" target="_blank">Windows 开发人员中心</a>以来，我们已经与全球范围内的众多开发人员和设计人员紧密协作，让他们了解我们如何为应用构建者提供出色的指导、激发其创作灵感并提供示例。通过网站数以百万计的访客以及用户的反馈，我们评测了工作的成效如何以及哪些功能和体验还可以继续改进。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0172.windows_dev_center_1EC86298.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0172.windows_5F00_dev_5F00_center_5F00_1EC86298.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="Windows 开发人员中心" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0184.windows_dev_center_thumb_1DEFFCAE.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0184.windows_5F00_dev_5F00_center_5F00_thumb_5F00_1DEFFCAE.png" alt="Windows 开发人员中心" width="600" height="338" border="0" /></a></p>
<h2>最近更新</h2>
<p>Windows 团队致力于构建一个充满生机和活力的应用生态系统。有三种方式可以展示应用生态系统的好坏：设计出色的应用、良好的经济收益以及广泛的传播机会。新的开发人员中心为应用构建者、设计人员以及企业提供他们在开发出色的应用以及与 Windows 构建业务关系时所需的资源。</p>
<p>为了支持这些目标，开发人员中心涉及以下几个方面：</p>
<ol>
<li>导航模式已经变化，您可以查找到您在应用开发生命周期过程中所需的所有相关内容</li>
<li>网站设计突出显示了优先级较高的任务、应用场景和新内容</li>
</ol>
<p>除了提供开发人员需要的内容、示例和下载内容外，设计人员和企业访问资源比以前更为方便。</p>
<p>取决于您正在构建的内容，可以导航到以下四个区域之一：<a href="http://msdn.microsoft.com/zh-cn/windows/apps/br229512" target="_blank">Windows 应用商店应用</a>、<a href="http://msdn.microsoft.com/zh-cn/ie/" target="_blank">Internet Explorer</a>、<a href="http://msdn.microsoft.com/zh-cn/windows/desktop" target="_blank">Windows 桌面</a>和 <a href="http://msdn.microsoft.com/zh-cn/windows/hardware" target="_blank">硬件</a>。上述每个区域都提供了一种解决上面所涉及问题的新体验。在本篇博文中，我们将详细地了解对 Windows 应用商店应用所做的具体更改。</p>
<h2>内容适合您的职务角色</h2>
<p>如果找不到想要的内容，网站信息量再大对您而言也是没用的。开发人员中心中有数以万计的主题信息，内容的条理性以及可发现性至关重要。对于 Windows 应用商店应用开发，这意味着要更加紧密关注职务角色和应用场景，从将您着手所需的一切内容放置于一个位置到更好地支持设计人员和企业。以下是一些特色功能介绍。</p>
<h3>开发人员</h3>
<p>我们已经对构建出色的 Windows 应用商店应用所需的内容进行了精简。当访问 <a href="http://msdn.microsoft.com/zh-cn/windows/apps/" target="_blank">Windows 应用商店应用主页</a>时，将会看到：</p>
<ul>
<li>位于显著位置的&ldquo;主要任务和内容&rdquo;可以帮助您跳转到自己所需的内容。您所在区域的&ldquo;本地活动&rdquo;以及最近添加到网站的最新资源会以突出形式显示。</li>
<li>&ldquo;下载&rdquo;可以帮助您进一步深入了解代码、着手行动所需的一切资源以及有关如何将应用放置到 Windows 应用商店的详细指导。</li>
<li>重要的代码示例会突出显示，您在关注开发生命周期的同时可以更方便地浏览帮助指南。</li>
<li>网站内的&ldquo;<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br211360.aspx" target="_blank">One Dev Minute</a>&rdquo;视频，令您可以快速地浏览如何向应用中添加功能。</li>
</ul>
<h3>设计人员</h3>
<p>我们重新打造了网站的<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh779072.aspx" target="_blank">设计部分</a>，令其更易于使用，同时更加全面。用户可以更方便地查找有用的下载内容和可重复使用的设计资产，设计灵感和案例研究为您提供在 Windows 上获得独一无二的体验所需工具。有了这个触摸友好并且易于使用的页面设计，您构建出色应用所需的一切内容都可以手到擒来。</p>
<h3>业务</h3>
<p>Windows 应用商店带来的商机巨大，其经济模式比竞争平台的更为出色，灵活性令您可以打造自己的业务。网站新的<a href="http://go.microsoft.com/fwlink/?LinkID=285998" target="_blank">市场部分</a>可以为您推销自己的应用、评估遥测数据以及通过应用认证提供更多提示和技巧。我们已经了解到如何更容易地通过认证的很多内容，我们对相关内容进行了改进以帮助您加快这个过程。</p>
<p>当然，您也可能担任多项职务角色，我们认为新网站的结构同样也可以提供良好的支持。不论是业余爱好者还是创新型公司乃至大型企业软件开发商，新的网站的结构都可以帮助您利用 Windows 实现您的创新灵感以及业务愿景。</p>
<h2>未来前景更为广阔，我们期待您的加入</h2>
<p>现在距离 <a href="http://www.buildwindows.com">BUILD 2013</a> 还有一个多月的时间，我们还有很多内容需要分享和发布。我们正在不断努力工作，准备更多新的内容在这个年度盛会上与大家分享。除了阅读这篇博文外，您还可以在 Twitter 上关注 <a href="https://twitter.com/#!/windevs" target="_blank">@windevs</a> 或者订阅 <a href="http://www.windowsstore.com/newsletter-signup" target="_blank">Windows 应用商店新闻稿</a>保持对我们的关注。</p>
<p>我们推出更多的内容来帮助您设计、开发和营销您的 Windows 应用商店应用，希望不断改进您的开发人员中心体验！为了让您更方便地反馈您的想法，我们在每个页面的底部提供了两种新的反馈选择。请提供反馈 &ndash; 我们愿意倾听您的意见并始终努力解决您遇到的困惑和问题。</p>
<p>&nbsp;</p>
<p>Windows 开发人员中心内容发布经理 Keith Boyd</p>
<!-- Title: “介绍一种新的 Windows 开发人员中心体验” Tags: “开发人员中心, 开发者信息, 应用构建, Windows 应用商店应用, 构建, Windows 开发人员中心” --></div>]]></content:encoded>
    </item>
    <item>
      <title>共享到您的目标应用</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_cn/83</link>
      <pubDate>Wed, 15 May 2013 09:07:00 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_cn/2013/05/15/83/</guid>
      <description><![CDATA[在博文从您的应用共享内容中，我们介绍了将与好友聚餐示例应用中的内容共享到多个目标应用，从而允许用户实现丰富的端到端应用场景的概念。Windows 8...]]></description>
      <content:encoded><![CDATA[<div class="PostContent">
<p>在博文<a href="http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/12/20/10379931.aspx" target="_blank">从您的应用共享内容</a>中，我们介绍了将<a href="http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/07/17/metro-from-the-crowd.aspx" target="_blank">与好友聚餐</a>示例应用中的内容共享到多个目标应用，从而允许用户实现丰富的端到端应用场景的概念。Windows 8 中的应用提供了您的客户会希望与好友或其他应用<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh465251.aspx" target="_blank">共享</a>的有趣内容。引人注目、设计精良的共享目标将使您的应用对于客户的社交和连接体验变得至关重要。</p>
<p>在本篇博文中，我们来看一下如何对您的应用使用&ldquo;共享目标&rdquo;合约，尤其是&ldquo;与好友聚餐&rdquo;等应用。使用此合约，您的应用可以从其他应用接收内容<em></em>，并使用这些内容来改进其质量。我们希望帮助您将您的应用变得出类拔萃！</p>
<p>目标应用使用&ldquo;共享目标&rdquo;<a href="http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/03/30/windows-8-activating-contracts.aspx" target="_blank">合约</a>构建，并且专为快速、轻松的共享体验而设计。并非 Windows 中的每个应用都将成为共享目标，但如果您的应用可以发布、转换、存储或通过某种方式为从其他应用接收的内容增加价值，则创建共享目标将为您带来许多好处。</p>
<ul>
<li>您的应用将始终显示于目标应用列表中。它可与共享您需要的数据的其他应用即时关联并建立连接。</li>
<li>&ldquo;共享&rdquo;合约创建了您的应用和任何目标应用之间的连接，而无需对两个应用进行一对一的集成。</li>
<li>由于&ldquo;共享&rdquo;超级按钮始终存在于操作系统中并可供使用，它可以使您的目标应用存在于操作系统中，从而拓宽了您的应用的范围。这样可以引导更多的客户使用您的应用！</li>
</ul>
<h2>接收共享内容</h2>
<p>让我们一起来看几个示例，其中显示了如何通过共享目标应用提供引人入胜的使用体验。</p>
<p>您可能已对 Windows 8 中的邮件和人脉应用非常熟悉。邮件应用创建了接收并与一个或多个好友共享内容的轻松个人共享目标体验。人脉应用允许您与社交网络上更广泛的受众共享内容。除社交共享外，您还可以进一步使用应用到应用共享。SkyDrive 等生产效率应用可以从 Windows 中的任何位置接收文件，并将它们存储到云；笔记应用可以接收文本、链接和各种其他格式的数据，并对这些数据进行归档以供日后参考。发挥创意的空间十分巨大。</p>
<p>请看以下两张照片，其中展示了将 Photos 应用中的照片共享到 PuzzleTouch（一款将照片转变为视觉拼图的游戏）的共享流程。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/8637.puzzletouch1_6E4332DA.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/8637.puzzletouch1_5F00_6E4332DA.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="源应用位于左侧；可接收照片的已筛选目标应用列表位于右侧的共享窗格中。" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/1307.puzzletouch1_thumb_2681A9F3.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/1307.puzzletouch1_5F00_thumb_5F00_2681A9F3.png" alt="源应用位于左侧；可接收照片的已筛选目标应用列表位于右侧的共享窗格中。" width="700" height="394" border="0" /></a></p>
<p align="center">图 1：源应用位于左侧；可接收照片的已筛选目标应用列表位于右侧的共享窗格中。 <br />PuzzleTouch 是可从 Photos 应用接收照片的共享目标之一。<em></em></p>
<p align="left"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/6661.puzzletouch2_463083BB.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/6661.puzzletouch2_5F00_463083BB.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="目标应用预览可让用户在右侧选择他们要构建的拼图类型" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/2474.puzzletouch2_thumb_65DF5D83.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/2474.puzzletouch2_5F00_thumb_5F00_65DF5D83.png" alt="目标应用预览可让用户在右侧选择他们要构建的拼图类型" width="700" height="394" border="0" /></a></p>
<p align="center">图 2：目标应用预览可让用户在右侧选择他们<br />要构建的拼图类型。当用户切换回该应用时，拼图将准备就绪。<em></em></p>
<h2>什么是共享目标应用？</h2>
<p>Windows 中的端到端体验由两部分组成。首先，必须具有在 Windows 中能够以不同格式来提供内容的源应用。其次，还必须具有注册为能够识别特定格式并作为共享内容的目标的共享目标应用。使用&ldquo;共享&rdquo;合约，源应用和共享目标应用可以进行相互通信。</p>
<p>当考虑您的应用的共享体验时，应想到共享目标应用是在托管共享窗口中呈现的。请确保设计了这种体验，从而您的用户可以快速预览内容并在此视图中确认共享。以下示例显示了邮件共享目标应用视图，其允许用户从浏览器与他/她的邮件联系人之一快速共享链接。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/8561.Share-Target-Image_3ED44139.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/8561.Share_2D00_Target_2D00_Image_5F00_3ED44139.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="共享目标图像" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/6011.Share-Target-Image_thumb_4FD86F1C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/6011.Share_2D00_Target_2D00_Image_5F00_thumb_5F00_4FD86F1C.png" alt="共享目标图像" width="700" height="435" border="0" /></a></p>
<p align="center">图 3：使用邮件目标应用来从 Photos 应用共享链接<em></em></p>
<h2>&ldquo;与好友聚餐&rdquo;的应用场景</h2>
<p>现在，我们来考虑一下<a href="http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/07/17/metro-from-the-crowd.aspx" target="_blank">与好友聚餐</a>应用是如何创建引人入胜的共享目标应用体验的。如果您对&ldquo;与好友聚餐&rdquo;应用非常熟悉，将会了解到用户可在该应用中采取的两个主操作 &ndash; 创建计划<em></em>和<em></em>向其备选列表添加餐馆。当然，还有编写评审、发表评论和查看好友的备选列表等操作，但这些操作在主应用程序的上下文中才有意义。对于这两个突出的核心应用场景，让我们来进行一次快速的健全性检查，以查看是否这些应用场景可通过&ldquo;与好友聚餐&rdquo;目标应用有效地完成。</p>
<h3>创建今晚的计划</h3>
<p>尽管这是主应用程序的核心应用场景，但并不是共享目标体验的最佳应用场景。以下是详细原因：共享目标体验应具有轻松快捷的特点。创建计划是一项更为复杂的任务 &ndash; 用户要了解好友希望尝试的其他餐馆，比较他们的备选列表中的一些餐馆并回顾评分等。由于计划涉及利用在主应用程序视图中以最佳方式呈现的详细信息，因此将这些内容压缩到共享目标应用将会缺乏吸引力。</p>
<p>&nbsp;</p>
<h3>添加到备选列表</h3>
<p>作为共享目标中的简短指令任务，这一应用场景的效果确实非常不错。该应用体验中的一些亮点包括：<strong></strong></p>
<ul>
<li>用户会在其他应用中遇到餐馆，无论是旅行计划应用、包含建议的地方城市应用、美食博客还是其他应用。&ldquo;与好友聚餐&rdquo;应用是个出色的目标，它可以将餐馆整合到&ldquo;我的备选列表&rdquo;区域。</li>
<li>应用的目标应用视图旨在帮助用户在不同应用的上下文中快速进行操作。因此，向备选列表中添加餐馆是在共享目标中显示餐馆的极佳选择。</li>
<li>当然，用户也可以通过切换应用、重复搜索来查找餐馆，然后将餐馆添加到他们的备选列表中，而快速点击&ldquo;共享&rdquo;要轻松的多。</li>
</ul>
<p>&ldquo;与好友聚餐&rdquo;备选列表似乎是我们希望设计和构建的出色的共享目标应用体验。以下是我们想像中的端到端应用场景：</p>
<p>当浏览显示本地移动餐车的应用时，我找到了听起来非常有趣的移动餐车建议。我希望将该移动餐车从该应用添加到&ldquo;与好友聚餐&rdquo;应用中的我的备选列表以供今后制定计划时参考。</p>
<p align="center"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/7658.contoso_truck_6C263411.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/7658.contoso_5F00_truck_5F00_6C263411.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="浏览显示本地移动餐车并建议用户感兴趣的地方的应用" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/3022.contoso_truck_thumb_4B9EF45F.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/3022.contoso_5F00_truck_5F00_thumb_5F00_4B9EF45F.png" alt="浏览显示本地移动餐车并建议用户感兴趣的地方的应用" width="700" height="394" border="0" /></a>图 4：浏览显示本地移动餐车并建议用户感兴趣的地方的应用<em></em></p>
<p>此图显示了我们要使用的共享目标应用视图：</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/3835.contoso_truck2_565C4BB4.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/3835.contoso_5F00_truck2_5F00_565C4BB4.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="共享目标应用视图" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/8637.contoso_truck2_thumb_760B257C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/8637.contoso_5F00_truck2_5F00_thumb_5F00_760B257C.png" alt="共享目标应用视图" width="700" height="394" border="0" /></a></p>
<p align="center">图 5：共享目标应用视图<em></em></p>
<p>通过在共享目标应用中完成共享操作，用户可在&ldquo;与好友聚餐&rdquo;应用中呈现餐馆，而无需在其当前的沉浸式应用体验中切换上下文。</p>
<h2>实施共享目标</h2>
<p>现在您已了解了一些关于在何时实施共享目标的示例，接下来我们来看看如何实施共享目标。首先，要决定您的应用可以接收哪些格式的数据。</p>
<h3>您应该支持哪些数据格式？</h3>
<p>通过&ldquo;共享&rdquo;，您可以支持文本、URI、HTML、位图和文件等标准数据格式，也可以支持诸如 <a href="http://schema.org" target="_blank">http://schema.org</a> 中列出的自定义数据格式。Windows 按照其支持的数据格式来筛选目标应用，以在&ldquo;共享&rdquo;窗格中显示已筛选的目标应用的列表。您的应用应支持对于应用体验至关重要的数据格式。例如，如果您的应用是关于查看照片的，则支持文本格式未必有用。如果您声明支持多种数据格式，请决定哪种格式可帮助您的以最佳方式呈现共享数据，以便您可以从数据包中首先选择这种格式。</p>
<p>由于&ldquo;与好友聚餐&rdquo;应用实施了可让用户与应用共享餐馆的共享目标体验，我们将使用 <a href="http://schema.org" target="_blank">http://schema.org</a> 中列出的<a href="http://schema.org/Restaurant" target="_blank">餐馆</a>数据格式。&ldquo;与好友聚餐&rdquo;可以注册&ldquo;共享目标&rdquo;合约以接收这种数据格式。然后，使用接收到的包中的数据，该应用可在其目标应用视图中构建引人注目的体验，用户可以将餐馆添加到他们的备选列表。</p>
<p>一些源应用可能专门共享<a href="http://schema.org/Restaurant" target="_blank">餐馆</a>数据格式，而其他源应用可能会使用 <a href="http://schema.org/" target="_blank">schema.org</a> 中列出的<a href="http://schema.org/Place" target="_blank">地点</a>、<a href="http://schema.org/LocalBusiness" target="_blank">LocalBusiness</a> 和 <a href="http://schema.org/FoodEstablishment" target="_blank">FoodEstablishment</a> 等其他数据格式呈现类似的内容。因此，&ldquo;与好友聚餐&rdquo;应用可支持接收&ldquo;共享&rdquo;合约中的多种数据格式，以满足众多源应用的需求。</p>
<p>&ldquo;与好友聚餐&rdquo;应用支持应用程序清单的共享目标声明中的以下格式：</p>
<ul>
<li><span style="text-decoration: underline;"><a href="http://schema.org/Restaurant" target="_blank">餐馆</a></span></li>
<li><span style="text-decoration: underline;"><a href="http://schema.org/Place" target="_blank">地点</a></span></li>
<li><span style="text-decoration: underline;"><a href="http://schema.org/LocalBusiness" target="_blank">LocalBusiness</a></span></li>
<li><span style="text-decoration: underline;"><a href="http://schema.org/FoodEstablishment" target="_blank">FoodEstablishment</a></span></li>
</ul>
<p>&nbsp;</p>
<p>为简洁起见，我们重点关注<a href="http://schema.org/Restaurant" target="_blank">餐馆</a>数据类型，但这些概念也适用于其他数据类型。</p>
<h3>您应该关注哪些数据格式和应用场景？</h3>
<p>&ldquo;与好友聚餐&rdquo;应用将仅在接收到的餐馆信息与该应用已知的餐馆信息相符时支持将餐馆添加到用户的备选列表。这样做是为了有意避免在应用中出现多个版本的餐馆。</p>
<p>我们不支持标准数据类型，因为大多数从源应用共享的文本、文件、照片、URI 或 HTML 将不会映射到餐馆或当地企业。例如，如果&ldquo;与好友聚餐&rdquo;应用支持接收文本，则它将在笔记源应用的目标应用列表中显示。在此情况下，&ldquo;与好友聚餐&rdquo;将需要提供错误消息，告知用户该应用无法与他们的源应用配合使用的原因。总而言之，数据格式是连接为目标应用提供内容的源应用的纽带。如果数据格式不同，共享的内容将变得毫无价值。</p>
<p>请确保考虑有助于识别您所关注的内容的所有数据格式，同时还要注意到会影响您的共享体验的数据格式。</p>
<h3>支持&ldquo;共享目标&rdquo;合约</h3>
<p>要支持数据格式，请在 Visual Studio 清单设计工具中对它们进行声明。以下是用户选择您的应用作为共享操作目标的情形：</p>
<ol>
<li>通过源应用在共享目标窗口中启动应用的新实例。</li>
<li><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.applicationmodel.activation.sharetargetactivatedeventargs.aspx" target="_blank">共享目标激活</a>事件触发，您的应用最终接收到 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br205977.aspx" target="_blank">ShareOperation</a><strong></strong> 对象。</li>
<li>您的应用可以检索用户从该对象共享的数据。</li>
<li>您可以使用此数据呈现目标应用视图，并完成用户的共享。</li>
</ol>
<p>有关如何创建该体验的更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh465255.aspx" target="_blank">接收共享内容</a><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh465255.aspx"><span style="text-decoration: underline;">快速入门</span></a>指南。</p>
<p>另外，Visual Studio 还支持共享目标模板。右键单击您的项目，选择&ldquo;添加项目&rdquo;<strong></strong>、&ldquo;添加新项目&rdquo;<strong></strong>并单击列表中的&ldquo;共享目标&rdquo;合约<strong></strong>，然后添加支持该合约的代码。<a href="http://code.msdn.microsoft.com/windowsapps/Sharing-Content-Target-App-e2689782" target="_blank">共享目标代码示例</a>展示了如何接收共享内容。</p>
<h2>设计量身定制的目标应用视图</h2>
<p>当用户从&ldquo;共享&rdquo;窗格中的应用列表选择目标应用时，Windows 将在该应用的共享目标视图中启动该应用。目标应用视图是托管在&ldquo;共享&rdquo;窗格中的特殊视图，与应用的主视图有所不同。<a href="http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/03/30/windows-8-activating-contracts.aspx" target="_blank">在您的应用程序中激活 Windows 8 合约</a>提供了关于托管视图和处理这些视图的激活的更多信息。该目标应用视图旨在帮助完成用户启动的共享操作。在该视图中，用户应能够快速预览他们的内容、执行简单的操作和确认他们的共享内容。</p>
<p>在查看&ldquo;与好友聚餐&rdquo;的目标应用设计之前，让我们一起来考虑一个您可能已了解的目标应用。下图并排显示了邮件应用的目标应用视图和 Windows 8 中的应用的完整视图。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/1307.target-app_4AC68E75.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/1307.target_2D00_app_5F00_4AC68E75.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="将 Photos 应用中的内容共享到右侧的邮件目标应用" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0724.target-app_thumb_5583E5CA.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0724.target_2D00_app_5F00_thumb_5F00_5583E5CA.png" alt="将 Photos 应用中的内容共享到右侧的邮件目标应用" width="700" height="438" border="0" /></a></p>
<p align="center">图 6：将 Photos 应用中的内容共享到右侧的邮件目标应用<em></em></p>
<p align="left"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/1385.mail_154DCC50.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/1385.mail_5F00_154DCC50.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="在邮件应用的主应用视图中撰写新邮件" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/2055.mail_thumb_510D6503.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/2055.mail_5F00_thumb_5F00_510D6503.png" alt="在邮件应用的主应用视图中撰写新邮件" width="700" height="438" border="0" /></a></p>
<p align="center">图 7：在邮件应用的主应用视图中撰写新邮件<em></em></p>
<p>请注意邮件应用的共享目标应用视图与其完整应用视图相比更为简单。它为用户要以缩略图以及相关文本和 HTML 格式共享的内容提供了简洁和最小化的预览。另外，它还提供了一个用于添加收件人的 [To]（收件人）<strong></strong>行、一个快速撰写消息的输入区域，以及通过 [Send]（发送）按钮<strong></strong>确认发送的简便方法。</p>
<p>尽管该视图更为简单，但该应用的目标应用视图作为邮件应用的虚拟形象可以明确识别，这增强了用户对共享内容的目标的信心。</p>
<h2>&ldquo;与好友聚餐&rdquo;应用作为共享目标应用</h2>
<p>现在您已对目标应用视图的设计有了大致的了解，接下来让我们来讨论一下&ldquo;与好友聚餐&rdquo;应用如何创建帮助用户向&ldquo;我的备选列表&rdquo;添加餐馆的目标应用。</p>
<p>当&ldquo;与好友聚餐&rdquo;应用接收到包含源应用中的<a href="http://schema.org/Restaurant">餐馆</a>的数据包时，其可以使用<a href="http://schema.org/Restaurant">餐馆</a>数据格式的详细信息来识别其现有数据库中的哪个餐馆与接收到的数据包匹配。然后，该应用可以在目标应用视图中显示该餐馆的预览。这增强了用户对共享内容的完整性的信心。</p>
<p>此图显示了&ldquo;与好友聚餐&rdquo;应用的目标应用视图 &ndash;</p>
<p align="center"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/3857.contoso_truck3_0305058E.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/3857.contoso_5F00_truck3_5F00_0305058E.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="&ldquo;与好友聚餐&rdquo;应用的目标应用视图" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/5584.contoso_truck3_thumb_1B94A2DE.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/5584.contoso_5F00_truck3_5F00_thumb_5F00_1B94A2DE.png" alt="&ldquo;与好友聚餐&rdquo;应用的目标应用视图" width="700" height="394" border="0" /></a>图 8：&ldquo;与好友聚餐&rdquo;应用的目标应用视图<em></em></p>
<p>如您所见，&ldquo;与好友聚餐&rdquo;应用的这一视图非常简洁，并且专为共享目标应用而量身定制 - 显示用户希望共享的餐馆的预览，以及一个用来明确主应用中共享内容的目标的简单可操作按钮 [Add to Wishlist]（添加到备选列表）<strong></strong>。当用户点击或单击该按钮时，其将确认共享操作。通过在此处要求点击或单击，我们可以帮助避免意外共享并建立用户对整个共享体验的信心。</p>
<p>如果应用找到了与数据包内容中的信息匹配的多个餐馆，&ldquo;与好友聚餐&rdquo;应用将使用共享目标应用视图来显示选项并让用户选择要共享的餐馆。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/5582.contoso_truck4_70500BD6.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/5582.contoso_5F00_truck4_5F00_70500BD6.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="目标应用视图显示选项以供用户选择要共享的项目" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/6175.contoso_truck4_thumb_6FE3D8E1.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/6175.contoso_5F00_truck4_5F00_thumb_5F00_6FE3D8E1.png" alt="目标应用视图显示选项以供用户选择要共享的项目" width="700" height="394" border="0" /></a></p>
<p align="center">图 9：目标应用视图显示选项以供用户选择要共享的项目<em></em></p>
<p>&nbsp;</p>
<p>如果应用无法根据接收到的数据包准确地识别餐馆或一组有效的选项，请使用目标应用视图来传递该消息。</p>
<h2>创建 QuickLink</h2>
<p><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.applicationmodel.datatransfer.sharetarget.quicklink.aspx" target="_blank">QuickLink</a> 是共享 API 的一项设置，它允许用户将内容快速共享到您的目标应用中的熟悉的目标。作为您的目标视图的自定义链接，QuickLink 提供了一些特定的操作。当 Windows 筛选用户可用的目标应用列表时，QuickLink 列表显示于&ldquo;共享&rdquo;窗格的顶部，作为可用目标列表。这些熟悉的目标或用户操作可能包含通过目标应用经常共享的地点或人员。例如，邮件应用返回以前向其发送过邮件的联系人的 QuickLink。同样，您也可以将 QuickLink 添加到您经常与目标应用共享的文件夹、组、笔记本和播放列表中。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/5001.quicklink_2FADBF67.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/5001.quicklink_5F00_2FADBF67.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="&ldquo;共享&rdquo;窗格显示邮件目标应用提供的以前使用目标应用向其发送过邮件的联系人的 QuickLink" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/1374.quicklink_thumb_334BDA44.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/1374.quicklink_5F00_thumb_5F00_334BDA44.png" alt="&ldquo;共享&rdquo;窗格显示邮件目标应用提供的联系人的 QuickLink" width="700" height="438" border="0" /></a></p>
<p align="center">图 10：&ldquo;共享&rdquo;窗格显示邮件目标应用提供的以前使用目标应用 <br />向其发送过邮件的联系人的 QuickLink<em></em></p>
<p>该目标应用可通过调用 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br205977.aspx" target="_blank">ShareOperation</a><strong></strong> 对象的 <strong></strong><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.applicationmodel.datatransfer.sharetarget.shareoperation.reportcompleted.aspx" target="_blank">ReportCompleted</a><strong></strong><strong></strong> 方法来提供 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.applicationmodel.datatransfer.sharetarget.quicklink.aspx" target="_blank">QuickLink</a>。QuickLink 必须包含标题、图标和 ID。当用户点击&ldquo;共享&rdquo;超级按钮时，会显示标题（如&ldquo;给母亲的电子邮件&rdquo;）和图标。ID 是您的应用用于访问自定义信息（例如，电子邮件地址、文件夹位置等）的标识符。要了解关于此工具的更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh758299.aspx" target="_blank">如何创建 QuickLink</a>。&nbsp;</p>
<p>注意：QuickLink 在应用支持目标应用的多个操作时非常有用。由于&ldquo;与好友聚餐&rdquo;应用仅支持共享目标中的单个操作，即&ldquo;添加到备选列表&rdquo;<strong></strong>，该应用未实施 QuickLink。</p>
<h2>扩展共享和错误报告</h2>
<p>有时源应用可以共享大量的数据，例如照片、视频或文件，这使数据传输时间变得更长。这些类型的共享操作称为&ldquo;扩展共享&rdquo;<em></em>，您的目标应用可能需要更多的时间来接收和处理数据。</p>
<p>由于共享是快速而流畅的过程，目标应用视图是主应用中的轻型托管视图，用户可能需要点击目标应用视图以返回到他们的主应用。因此，在共享开始之后，目标应用不应依赖于或期望与目标应用视图的用户交互。目标应用可以依赖于使用 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br205977.aspx" target="_blank">ShareOperation</a><strong></strong> 对象及其方法来让 Windows 知道应用正在处理数据。</p>
<ul>
<li><strong><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.applicationmodel.datatransfer.sharetarget.shareoperation.reportstarted.aspx" target="_blank">reportStarted</a></strong> &ndash; 调用此方法来让 Windows 知道您的应用已开始在源应用的 DataPackage 对象中检索数据。</li>
<li><strong><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.applicationmodel.datatransfer.sharetarget.shareoperation.reportdataretrieved.aspx" target="_blank">reportDataRetrieved</a></strong><strong></strong>&ndash; 调用此方法来让 Windows 知道您的应用已完成检索包中的数据。</li>
<li><strong><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.applicationmodel.datatransfer.sharetarget.shareoperation.reportcompleted.aspx" target="_blank">reportCompleted</a></strong><strong><span style="text-decoration: underline;"> &ndash; </span></strong>调用此方法来让 Windows 知道您的应用已完成共享操作。<strong></strong><strong></strong></li>
</ul>
<p>当您的应用处理扩展共享的数据时，请在目标应用视图中为用户提供进度 UI。这样可以通知用户他们的请求正在处理，并在用户返回到您的应用并检查共享操作的进度时增强他们的信心。要了解关于 Windows 8 中的进度控件的更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh465487.aspx" target="_blank">快速入门：添加进度控件</a>。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0702.progress-bar_52FAB40C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0702.progress_2D00_bar_5F00_52FAB40C.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="进度栏" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/3438.progress-bar_thumb_40B1ED4A.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/3438.progress_2D00_bar_5F00_thumb_5F00_40B1ED4A.png" alt="进度栏" width="700" height="438" border="0" /></a></p>
<p align="center">图 11：邮件目标应用在完成扩展共享（例如发送超大附件）时显示进度环<em></em></p>
<p>如果在数据传输期间出现问题，您的目标应用必须终止共享操作，您可以调用 <strong><span style="text-decoration: underline;">reportError</span></strong> 来向 Windows 发送错误消息。当用户在&ldquo;共享&rdquo;窗格中查看共享状态时，他们将看到该消息。在您报告错误之后，您的应用将关闭并且共享将终止。用户需要重新启动来与您的应用共享内容。</p>
<p>在&ldquo;与好友聚餐&rdquo;共享目标示例中，如果该应用无法识别其数据库中的某个餐馆，而该餐馆与通过<a href="http://schema.org/Restaurant" target="_blank">餐馆</a>数据格式从源应用共享的餐馆相匹配，则该应用将报告错误。</p>
<h2>让我们回顾一下本博文的内容！</h2>
<p>应用到应用共享提供了共享目标应用的绝好机会。通过允许您的应用接收共享内容，您的用户甚至可以在进行其他活动时从您的应用获得价值。我们希望您对设计原则，以及您可以用来创建出色的共享目标的数据类型、类和属性有了更加深刻的理解。我们将对了解您的应用如何传输共享内容并创建更广泛、更丰富的最终用户体验而感到激动不已。</p>
<p>如果您要了解关于共享的更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh464923.aspx" target="_blank">共享和交换数据</a>和<a href="http://code.msdn.microsoft.com/windowsapps/Sharing-Content-Target-App-e2689782" target="_blank">共享内容目标应用示例</a>来获得入门知识！</p>
<p>Ishita Kapur</p>
<p>项目经理</p>
<p>Windows 用户体验</p>
<!-- Title: “共享到您的目标应用” Tags: “共享应用, 目标应用, 共享内容, 共享目标, 共享目标应用” --></div>]]></content:encoded>
    </item>
    <item>
      <title>使用 NFC 开发前沿应用</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_cn/nfc</link>
      <pubDate>Wed, 24 Apr 2013 15:37:52 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_cn/2013/04/24/nfc/</guid>
      <description><![CDATA[近场通信 (NFC) 是一种新兴的短距离无线通信技术。在 2...]]></description>
      <content:encoded><![CDATA[<div class="PostContent"> <p>近场通信 (NFC) 是一种新兴的短距离无线通信技术。在 2 厘米的精确范围内，人们可以通过物理方式点击连接设备来发送/接收内容。通过点击可以快速选择人或物。这种信息交流方式不仅速度快，而且易于理解。操作方法一目了然，无需阅读任何手册。</p>  <p>例如，假设您与好友正在共同欣赏一些照片，并且她想要您的照片；使用 NFC，您只需对着她的 PC 点击您的设备就可以发送照片。是否觉得过于简单了？也许是，不过此项技术的初衷就是为了简单地在设备之间共享内容。</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/1732.NFC-image_04E645BB.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/1732.NFC_2D00_image_5F00_04E645BB.png"><img title="&#39;此图显示 Nokia 360 音箱与 Windows Phone 共享蓝牙配对信息" style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" border="0" alt="&#39;此图显示 Nokia 360 音箱与 Windows Phone 共享蓝牙配对信息" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/3566.NFC-image_thumb_38420917.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/3566.NFC_2D00_image_5F00_thumb_5F00_38420917.png" width="400" height="348" /></a></p>  <p align="center">图 1：此图显示 Nokia 360 音箱   <br />与 Windows Phone 共享蓝牙配对信息</p>  <p>与蓝牙或 Wi-Fi 类似，NFC 是由 <a href="http://www.nfc-forum.org/home/" target="_blank">NFC 论坛</a><u></u> (NFC Forum) 定义的一种标准无线传输协议。<u></u>您的 PC 需要 NFC 无线通信装置。许多 Windows 8 PC 中直接集成了 NFC 无线通信装置。如果您的 PC 未集成 NFC，可以购买 NFC 收发器，并将其插入您的 PC。</p>  <p>NFC 提供了一些非常酷的功能。人们可以点击并发送照片，在餐馆点击菜谱并订餐，甚至可以通过点击与蓝牙设备配对。这些场景彼此有很大不同，但一个共同点是通过“点击”启动体验。NFC 可用于各种设备，如 PC、电话、音箱、耳机、无线显示器等，这使得将设备连接在一起成为一种真正直观的体验。此外，NFC 还使用 RFID 标签；它们是真正便宜、轻巧的无源天线，可容纳大量的数据，并可以粘贴在几乎任何物体上，最常用于海报。例如，购买电影票如同点击电影海报一样简单！这就是 NFC 标签。我们将通过点击 NFC 标签这样一个基本示例来演示 NFC API 的一些关键概念。</p>  <p>但是，首先我们仔细看一下点击设备进行连接意味着什么。</p>  <h2>定义“点击”</h2>  <p>无论是点击将蓝牙鼠标配对还是点击共享照片，重要的是用户需要以相同的方式点击设备进行连接。尽管点击是一个很好理解的概念，但对着另一设备点击您的 PC 对于大多数人而言还是一个新概念。下面是有关点击的一些指导原则，以便让用户知道：</p>  <ol>   <li>     <div align="center"><b>在何处点击设备进行连接</b> – 通过使用触摸标记（也称为“轻触并执行”视觉标记）指示 NFC 天线的位置。根据 PC 型号的不同，您需要点击 PC 的不同部分。例如，在平板电脑上需要点击背面，但在多功能一体机上则需要点击正面。下面是该标记在所有启用 NFC 的 Windows 8 PC 上的图标：        <br /></div>     <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/0830.NFC-logo_66E85720.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/0830.NFC_2D00_logo_5F00_66E85720.png"><img title="“轻触并执行”视觉标记" style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" border="0" alt="“轻触并执行”视觉标记" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/5611.NFC-logo_thumb_3FAE0DEB.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/5611.NFC_2D00_logo_5F00_thumb_5F00_3FAE0DEB.png" width="200" height="147" /></a>      <p align="center">图 2：“轻触并执行”视觉标记</p>   </li>    <li><b>设备正在相互通信</b> - 在点击过程中，用户相信正在执行某些操作；即使他们看不到数据正在传输。因此，当设备在感应范围内并且正在相互通信时，Windows 将播放声音。</li> </ol>  <p>Windows 会自动执行这些操作，因此您无需为它们操心。有关这些用户体验要素的更多信息，请参阅 <a href="http://msdn.microsoft.com/zh-cn/library/windows/hardware/hh770524.aspx" target="_blank">Windows 8 近场邻近感应实施规范</a><u></u>。<u></u>了解上述内容之后，让我们看一些使用 NFC 可以体验的超酷场景。</p>  <h2>何时使用 NFC</h2>  <p>当用户需要在您的应用中选择人或物时使用 NFC。NFC 为您提供了一种直观的选择方式；它通常比手动搜索速度要快（而且更酷！）。点击是一种启动体验的触发操作；根据应用的不同，得到的体验也不同，范围从接收照片到启动播放列表，不一而足。点击之后发生的操作实际上由您的应用决定。因此，为简单起见，我们将此系列体验归类为“轻触并执行”体验。</p>  <p>下面是一些使用 NFC 在应用中选择内容的示例。您可以点击并…</p>  <ul>   <li><u>从海报获取信息</u>：<a href="http://www.nfc-forum.org/news/pr/view?item_key=2c0cb92de7d47bbbe7c99f13912b3307fc03c1c6" target="_blank">NFC 标签</a>是轻巧、便宜的 RFID 标签；它们的成本介于 0.15 - 1.00 美元之间（价格因印刷成本而异）。它们相当于二维条码，但使用起来更方便、速度更快。点击标签比拍摄条码（需要将相机调到正确角度）感觉更舒服。制造商越来越多地将标签嵌入在人流密集的地方（如机场、地铁站和公交车站）张贴的海报中。它们可以保存 48 B – 4 KB 的数据。您可以对标签编程，以启动您的应用并打开特定的页面。</li> </ul>  <ul>   <li><u>交换联系信息</u>：只需点击设备进行连接即可交换联系信息，而不必再向您的好友拼读联系信息，也不必担心对方出现任何拼写错误。与以上类似，您可以通过编程方式将自己的信息写入 NFC 名片/标签；也可以通过应用直接交换信息。</li>    <li><u>播放您喜爱的音乐电台</u>：无论您是在外工作、开车出游还是在家休息 – 使用 NFC 标签即可启动广播电台。您甚至可以在不同的标签上以编程方式写入不同的音乐电台；例如，一个标签用于健身房，一个用于休闲，一个用于睡眠。</li>    <li><u>在繁忙的餐厅订餐</u>：在受欢迎的餐厅就餐时，仅为了订餐就可能需要花很长时间等待。而使用这种标签，只需在您的餐桌上点击标签就可以预订您的菜品。</li>    <li><u>与好友玩多人游戏：</u>通过点击设备进行连接，您可以轻松连接多人游戏，与好友玩战舰、国际象棋或拼字游戏等。点击之后，此连接将持续保持，支持更大范围和更高吞吐量的带外传输（如蓝牙或 Wi-Fi Direct）。</li> </ul>  <p>到目前为止，我们对何时使用 NFC 有了相当不错的想法；现在对于娱乐内容而言 – 构建一个使用我们的 Windows 8 邻近感应 (NFC) API 的应用。</p>  <h2>如何实施 NFC</h2>  <p>您可以看到，使用 NFC，最终用户可以更加轻松地处理许多日常任务。我们以设置闹铃为例进行说明。我敢肯定，每个人都有多次这样的经历，他们将闹铃错设成早晨的其他时间。夜深人困时，您只想着尽快入睡，思维并非总是处于最活跃状态。使用 NFC 就轻松多了，您只需点击一个预设的标签，再确认一下，然后就可以安然入睡了。因此，为了帮助用户完成这个日常任务，让我们假设您编写了一个基本的闹铃应用，用户可以使用一个 NFC 标签设置闹铃。这分为两种情况</p>  <ol>   <li><u><b></b>在标签上设置闹铃</u>：NFC 标签可以重用，因此该应用应有一种方法供用户对闹铃编程。例如，用户可能希望编写不同的闹铃程序 – 一个用于工作日，另一个用于周末。这称为将数据发布到标签。</li>    <li><u><b></b>从标签设置闹铃</u>：在用户点击标签之后，该应用将启动以确认设置闹铃。这意味着可以使用上下文或参数启动该应用。</li> </ol>  <p>NFC API 支持使用多种方式达到同一目的，不过，我将介绍实现此场景的最简单的方法。</p>  <p>下面我们了解在 NFC 标签上设置闹铃的过程：</p>  <ol>   <li>Mario 启动闹铃应用，并设置时间，假设为早晨 7:00。这是一个普通闹铃，与 NFC 尚无任何关系。</li>    <li>Mario 选择一个选项“在 NFC 标签上设置闹铃”。这时，该应用将调用 NFC API 将信息发布到 NFC 无线通信装置，具体是一个应用标识字符串和 07:00。NFC 标签使用一个称为 NDEF（NFC 定义交换格式）的标准化消息格式。您的应用<b></b>无需担心是否可以将数据格式化为 NDEF 消息；Windows 会为您完成此任务！现在，用户可以点击 NFC 标签。</li>    <li>Mario 对着 PC 点击该标签，应用确认正在对该标签编程。在点击标签之后，务必让用户知道您的应用已成功对标签编程。我们刚才谈到，点击您的 PC 对大多数人来说是一个新概念，因此通过确认可让用户相信他们刚才的操作已经奏效。该应用通过注册消息传输处理程序，知道该消息已成功传输。</li> </ol>  <p>NFC API 位于 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.proximity.aspx" target="_blank">Windows.Networking.Proximity</a> 命名空间中。它们在第 2 步中使用 – 在用户选择选项“在 NFC 标签上设置闹铃”之后。首先，该应用初始化邻近感应对象。邻近感应对象用于检测是否有标签（或设备）处于感应范围以内/以外。接下来，我们将添加 DeviceArrival 事件处理程序。该处理程序识别何时点击了标签，这意味着我们可以开始向标签写入信息。让用户知道您正在向标签写入很有必要，以便他们此时不会将其移出感应范围。您可以使用相同事件来识别何时点击了任何邻近感应设备。</p>  <p>下面的代码片段显示了如何初始化和添加 DeviceArrival 事件处理程序。</p>  <p><strong>JavaScript</strong></p>  <blockquote>   <div>     <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">var</span> proximityDevice;<br /><br /><span style="color: rgb(0, 0, 255);">function</span> initializeProximityDevice() {<br />    proximityDevice = Windows.Networking.Proximity.ProximityDevice.getDefault();<br /><br />    <span style="color: rgb(0, 0, 255);">if</span> (proximityDevice) {<br />        proximityDevice.addEventListener(<span style="color: rgb(0, 96, 128);">&quot;devicearrived&quot;</span>, proximityDeviceArrived);<br />       <br />}<br />   <span style="color: rgb(0, 0, 255);">else</span> {<br />        <span style="color: rgb(0, 128, 0);">// No NFC radio on the PC, display an error message</span><br />    }<br /><br /><br /><span style="color: rgb(0, 0, 255);">function</span> proximityDeviceArrived(device) {<br />        <span style="color: rgb(0, 128, 0);">// Let the user know we’re ‘Writing to Tag’</span><br /><br />}<br />}<br /></pre>
  </div>
</blockquote>

<div>
  <br /><strong>C#</strong></div>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">void</span> InitializeProximityDevice()<br />{<br /><br />Windows.Networking.Proximity.ProximityDevice proximityDevice;<br />    proximityDevice = Windows.Networking.Proximity.ProximityDevice.GetDefault();<br /><br />    <span style="color: rgb(0, 0, 255);">if</span> (proximityDevice != <span style="color: rgb(0, 0, 255);">null</span>) {<br />        proximityDevice.DeviceArrived += ProximityDeviceArrived;<br />    <br />    }<br />    <span style="color: rgb(0, 0, 255);">else</span><br />    {<br />        <span style="color: rgb(0, 128, 0);">// No NFC radio on the PC, display an error message</span><br />    }<br />}<br /><br /><span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">void</span> ProximityDeviceArrived(Windows.Networking.Proximity.ProximityDevice device)<br />{<br />            <span style="color: rgb(0, 128, 0);">// Let the user know we’re ‘Writing to Tag’</span><br /><br />}<br /></pre>
  </div>
</blockquote>

<p>
  <br />接下来，我们向该标签发布信息。该应用发布两项内容：一个应用标识字符串，其中包含一个应用 ID 和应用平台，以及启动参数。对于 Windows 8，应用 ID 是 &lt;<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.applicationmodel.packageid.familyname.aspx" target="_blank">package family name</a>&gt;!&lt;<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br211415.aspx" target="_blank">app Id</a>&gt;，应用平台是“Windows”。您必须从应用软件包清单中应用程序元素的 ID 属性中复制应用 ID 值。启动参数是“07:00”– 该闹铃由用户设置。我们将其称为“消息”。<b></b></p>

<p>如果该应用跨平台运行，可以通过 Windows 发布备选应用 ID 和应用平台；这意味着您可以在支持 NFC 的不同设备（如 Windows Phone 8）上点击相同的标签！您可以在 MSDN 上找到有关<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.proximity.peerfinder.alternateidentities.aspx" target="_blank">备选 ID</a> 的更多信息。</p>

<p>该应用使用名为 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh701134.aspx" target="_blank">publishBinaryMessage</a> 的方法将数据发布到标签。此方法采用三个参数 – messageType、message 和一个 messageTransmittedHandler 函数。我们将 messageType 设置为“LaunchApp:WriteTag”，这可让 Windows 知道您的应用希望向 NFC 标签写入信息。message 就是我们刚才定义的消息（应用识别的字符串和启动参数）；我们需要在缓冲区中将此消息存储为二进制消息。messageTransmittedHandler 函数注册回调。这可让您的应用知道消息已成功写入标签。我们使用此方法告诉用户两个信息：我们已将消息成功写入标签，标签不再需要位于感应范围内。</p>

<p>消息将持续发布，直到我们调用 StopPublishingMessage 函数或者释放 ProximityDevice 对象之后才结束。在本例中，我们将使用 stop 函数。PublishBinaryMessage 返回一个发布 ID；我们将使用这一发布 ID 停止向 NFC 无线通信装置发布消息。</p>

<p>下面的代码片段显示了如何向 NFC 标签写入数据：</p>

<p><strong>JavaScript</strong></p>

<blockquote>
  <div>
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">var</span> proximityDevice;<br /><br /><span style="color: rgb(0, 0, 255);">function</span> getAlarmTime(){<br /><br />    <span style="color: rgb(0, 128, 0);">// Grab time set by the user, call this variable ‘Alarm’</span><br />    <span style="color: rgb(0, 0, 255);">return</span> Alarm;<br /><br />}<br /><br /><span style="color: rgb(0, 0, 255);">function</span> publishLaunchApp() {<br />    proximityDevice = Windows.Networking.Proximity.ProximityDevice.GetDefault();<br /><br /><span style="color: rgb(0, 0, 255);">if</span> (proximityDevice) {<br />        <span style="color: rgb(0, 128, 0);">// The format of the app launch string is: &quot;&lt;args&gt;\tWindows\t&lt;AppName&gt;&quot;.</span><br />        <span style="color: rgb(0, 128, 0);">// The string is tab or null delimited.</span><br /><br />        <span style="color: rgb(0, 128, 0);">// The &lt;args&gt; string can be an empty string (&quot;&quot;).</span><br />        <span style="color: rgb(0, 0, 255);">var</span> launchArgs = getAlarmTime();<br /><br />        <span style="color: rgb(0, 128, 0);">// The format of the AppName is: PackageFamilyName!PRAID.</span><br />        <span style="color: rgb(0, 0, 255);">var</span> praid = <span style="color: rgb(0, 96, 128);">&quot;AlarmApp&quot;</span>; <span style="color: rgb(0, 128, 0);">// The Application Id value from your package.appxmanifest.</span><br /><br />        <span style="color: rgb(0, 0, 255);">var</span> appName = Windows.ApplicationModel.Package.current.id.familyName + <span style="color: rgb(0, 96, 128);">&quot;!&quot;</span> + praid;<br /><br />        <span style="color: rgb(0, 0, 255);">var</span> launchAppMessage = launchArgs + <span style="color: rgb(0, 96, 128);">&quot;\tWindows\t&quot;</span> + appName;<br /><br />        <span style="color: rgb(0, 0, 255);">var</span> dataWriter = <span style="color: rgb(0, 0, 255);">new</span> Windows.Storage.Streams.DataWriter();<br />        dataWriter.unicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.utf16LE;<br />        dataWriter.writeString(launchAppMessage);<br />        <span style="color: rgb(0, 0, 255);">var</span> launchAppPubId =<br />             proximityDevice.publishBinaryMessage(<br />                 <span style="color: rgb(0, 96, 128);">&quot;LaunchApp:WriteTag&quot;</span>, <br />                 dataWriter.detachBuffer(), <br />                 proximityWriteTagLaunchAppMessageTransmitCallback);<br />        <br />    <span style="color: rgb(0, 0, 255);">if</span> (launchAppPubId != -1) {<br />            <span style="color: rgb(0, 128, 0);">// Stop publishing the message on NFC radio</span><br />        proximityDevice.stopPublishingMessage(launchAppPubId);<br />    }<br /><br />     }<br />}<br /><br /><span style="color: rgb(0, 0, 255);">function</span> proximityWriteTagLaunchAppMessageTransmitCallback() {<br />    <span style="color: rgb(0, 128, 0);">// Inform the user that: the message has been successfully written to a tag &amp; the tag no longer needs to be in range</span><br />}<br /></pre>
  </div>
</blockquote>

<div>
  <br /><strong>C#</strong></div>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">Windows.Networking.Proximity.ProximityDevice proximityDevice;<br /><br /><span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">string</span> GetAlarmTime(){<br /><br />    <span style="color: rgb(0, 128, 0);">// Grab time set by the user, call this variable ‘Alarm’</span><br />    <span style="color: rgb(0, 0, 255);">return</span> Alarm;<br />}<br /><br /><span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">void</span> PublishLaunchApp()<br />{<br />    proximityDevice = Windows.Networking.Proximity.ProximityDevice.GetDefault();<br /><br /><span style="color: rgb(0, 0, 255);">if</span> (proximityDevice != <span style="color: rgb(0, 0, 255);">null</span>)<br />    {<br />        <span style="color: rgb(0, 128, 0);">// The format of the app launch string is: &quot;&lt;args&gt;\tWindows\t&lt;AppName&gt;&quot;.</span><br />        <span style="color: rgb(0, 128, 0);">// The string is tab or null delimited.</span><br /><br />        <span style="color: rgb(0, 128, 0);">// The &lt;args&gt; string can be an empty string (&quot;&quot;).</span><br />        <span style="color: rgb(0, 0, 255);">string</span> launchArgs = getAlarmTime();<br /><br />        <span style="color: rgb(0, 128, 0);">// The format of the AppName is: PackageFamilyName!PRAID.</span><br />        <span style="color: rgb(0, 0, 255);">string</span> praid = <span style="color: rgb(0, 96, 128);">&quot;MyAppId&quot;</span>; <span style="color: rgb(0, 128, 0);">// The Application Id value from your package.appxmanifest.</span><br /><br />        <span style="color: rgb(0, 0, 255);">string</span> appName = Windows.ApplicationModel.Package.Current.Id.FamilyName + <span style="color: rgb(0, 96, 128);">&quot;!&quot;</span> + praid;<br /><br />        <span style="color: rgb(0, 0, 255);">string</span> launchAppMessage = launchArgs + <span style="color: rgb(0, 96, 128);">&quot;\tWindows\t&quot;</span> + appName;<br /><br />        var dataWriter = <span style="color: rgb(0, 0, 255);">new</span> Windows.Storage.Streams.DataWriter();<br />        dataWriter.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf16LE;<br />        dataWriter.WriteString(launchAppMessage);<br />        var launchAppPubId =<br />        proximityDevice.PublishBinaryMessage(<br />            <span style="color: rgb(0, 96, 128);">&quot;LaunchApp:WriteTag&quot;</span>, dataWriter.DetachBuffer(), <br />            proximityWriteTagLaunchAppMessageTransmitCallback);<br />    <br />    <span style="color: rgb(0, 0, 255);">if</span> (launchAppPubId!= -1)<br />    {<br />        proximityDevice.StopPublishingMessage(launchAppPubId);<br />    <span style="color: rgb(0, 128, 0);">// Stop publishing the message on NFC radio</span><br />    }<br /><br />    }<br />}<br /><br /><span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">void</span> proximityWriteTagLaunchAppMessageTransmitCallback(<br />    Windows.Networking.Proximity.ProximityDevice sender,<br />    <span style="color: rgb(0, 0, 255);">long</span> messageId)<br />{<br />        <span style="color: rgb(0, 128, 0);">// Inform the user that: the message has been successfully written to a tag &amp; the tag no longer needs to be in range</span><br />}<br /></pre>
  </div>
</blockquote>

<br />就是这样！现在您已经知道如何从 Windows 应用商店应用向 NFC 标签写入信息。非常简单；因此，我们转到下一个场景 - <b></b>从标签设置闹铃。我们来看一下从 NFC 标签读取闹铃的过程：

<ol>
  <li>Mario 正在阅读他的主屏幕内容/写电子邮件/玩游戏/使用 Windows，他意识到需要为周一早上设置一个闹铃。他拿起“工作日闹铃”标签，然后对着自己的 PC 点击该标签。他获得一个 toast，邀请他启动您的闹铃应用。Windows 将照顾一切，直到这一刻为止；您的应用无需执行任何操作。</li>

  <li>Mario 接受邀请，您的应用启动到“确认闹铃？”屏幕，时间为早晨 7:00。在 Mario 接受 toast 之后，Windows 在激活过程中将启动参数（与上述参数一样）传递到您的应用。这称为外部启动，这与将您的应用启动到特定页是一样的。</li>

  <li>Mario 设置闹铃。这是一个普通闹铃，与 NFC 无关。</li>
</ol>

<p>从 NFC 标签获取启动参数非常容易。应用需要处理来自 NFC 标签的外部启动。外部启动与将您的应用启动到特定页同义。我们的启动参数指定一个早晨 7:00 的闹铃，您的应用使用它来显示一个建议的闹铃。此外，如果 PC 上未安装您的应用，Windows 将邀请 Mario 从 Windows 应用商店安装该应用 – 安装过程完全自动！</p>

<p>以下代码片段显示了如何实现上下文启动。</p>

<p><strong>JavaScript</strong></p>

<blockquote>
  <div>
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">app.onactivated = <span style="color: rgb(0, 0, 255);">function</span> (args) {<br />    <span style="color: rgb(0, 0, 255);">if</span> (args.detail.kind === activation.ActivationKind.launch) {<br />        <span style="color: rgb(0, 0, 255);">if</span> (args.detail.arguments == <span style="color: rgb(0, 96, 128);">&quot;Windows.Networking.Proximity.PeerFinder:StreamSocket&quot;</span>) {<br />            <span style="color: rgb(0, 128, 0);">//do nothing here.</span><br />        }<br />        <span style="color: rgb(0, 0, 255);">else</span> {<br />    <span style="color: rgb(0, 128, 0);">// Use args.detail.arguments to parse out ’07.00’ string, and display to the user</span><br />        }<br /><br />        args.setPromise(WinJS.UI.processAll());<br />    }<br />}<br /></pre>
  </div>
</blockquote>

<div>
  <br /><strong>C#</strong></div>

<blockquote>
  <div id="codeSnippetWrapper">
    <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">async <span style="color: rgb(0, 0, 255);">protected</span> <span style="color: rgb(0, 0, 255);">override</span> <span style="color: rgb(0, 0, 255);">void</span> OnLaunched(LaunchActivatedEventArgs args)<br />        {<br />            <span style="color: rgb(0, 0, 255);">if</span> (args.Arguments == <span style="color: rgb(0, 96, 128);">&quot;Windows.Networking.Proximity.PeerFinder:StreamSocket&quot;</span>)<br />            {<br />                _isLaunchedByTap = <span style="color: rgb(0, 0, 255);">true</span>;<br />            }<br />            <span style="color: rgb(0, 0, 255);">else</span><br />            {<br /><span style="color: rgb(0, 128, 0);">// Use args.Arguments to parse out ’07.00’ string, and display to the user</span><br /><br />            }<br />            Window.Current.Activate();<br />        }<br /></pre>
  </div>
</blockquote>

<p>
  <br />这就是您的应用为支持读写 NFC 标签所需的全部操作；对于前沿场景而言相当简单。为了能够良好运行，在结束之前，让我们探讨一下错误处理问题。</p>

<h2>错误处理</h2>

<p>您的应用可能会遇到许多常见的错误。</p>

<ul>
  <li>点击的标签不是 NDEF 格式。Windows 8 不支持将标签自动重新格式化为 NDEF，因此您需要下载并安装 NDEF 格式化程序。</li>

  <li>点击的标签为只读模式。一些 NFC 标签可能被锁定为只读模式（类似于旧式 <a href="http://zh.wikipedia.org/wiki/VHS" target="_blank">VHS 录像带</a>）。</li>

  <li>点击的标签容量太小，无法容纳所有数据。</li>

  <li>用户的 PC 没有 NFC。我在开头已经提到，NFC 是一种新兴的技术；普通采用仍需时日。要检查 PC 是否支持邻近感应，请使用 ProximityDevice.getDefault() 方法；如果未安装 NFC 无线通信装置，该方法将返回 NULL。</li>
</ul>

<h2>就这么有趣、直观！</h2>

<p>NFC 终于到来了，它已经为主流消费者做好了准备 – Windows 有相应的生态体系和设计精良的端到端用户体验。该技术具有很大的潜力，可让应用和设备获得令人难以置信的交互式体验。就这么有趣、直观。</p>

<p>NFC 是一个广阔的领域，因此，请继续关注有关其他超酷 Windows 8 NFC 开发人员体验的更多博文。</p>

<p>有关 NFC 和邻近感应的更多信息，请参阅下面的资源。</p>

<h2>资源</h2>

<div align="center">
  <table class="b8table" cellspacing="0" cellpadding="3" width="298" align="center" border="0"><tbody>
      <tr>
        <td valign="top" width="197">
          <p><strong>链接</strong></p>
        </td>

        <td valign="top" width="99">
          <p><strong>类型</strong></p>
        </td>
      </tr>

      <tr>
        <td valign="top" width="197">
          <p align="left"><u><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh465215.aspx" target="_blank">邻近感应体验使用指南</a></u></p>
        </td>

        <td valign="top" width="99">
          <p>文档</p>
        </td>
      </tr>

      <tr>
        <td valign="top" width="197">
          <p align="left"><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br241250.aspx" target="_blank">邻近感应 API</a></p>
        </td>

        <td valign="top" width="99">
          <p>文档</p>
        </td>
      </tr>

      <tr>
        <td valign="top" width="197">
          <p align="left"><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh967765.aspx" target="_blank">测试邻近感应应用并排除故障</a></p>
        </td>

        <td valign="top" width="99">
          <p>文档</p>
        </td>
      </tr>

      <tr>
        <td valign="top" width="197">
          <p align="left"><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh465223.aspx" target="_blank">发布和订阅（详细说明）</a></p>
        </td>

        <td valign="top" width="99">
          <p>文档</p>
        </td>
      </tr>

      <tr>
        <td valign="top" width="197">
          <p align="left"><a href="http://code.msdn.microsoft.com/windowsapps/Proximity-Sample-88129731" target="_blank">邻近感应应用示例</a></p>
        </td>

        <td valign="top" width="99">
          <p>示例</p>
        </td>
      </tr>

      <tr>
        <td valign="top" width="197">
          <p align="left"><a href="http://social.msdn.microsoft.com/Forums/zh-cn/category/windowsapps" target="_blank">遇到有关邻近感应的问题？请在这里询问</a></p>
        </td>

        <td valign="top" width="99">
          <p>论坛</p>
        </td>
      </tr>
    </tbody></table>
</div>

<p>谢谢！</p>

<p>Priya Dandawate
  <br />设备和网络项目经理</p>

<p>投稿者：Max Morris、Marzena Makuta、Mike Loholt、Jake Sabulsky 和 Vishal Mhatre</p>

<!-- Title: “使用 NFC 开发前沿应用” Tags: “共享, NFC, 近场通信, 共享文件, 蓝牙配对, 共享数据” --></div>]]></content:encoded>
    </item>
    <item>
      <title>创建连接的 Windows 应用商店应用</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_cn/windows-2</link>
      <pubDate>Wed, 17 Apr 2013 13:53:00 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_cn/2013/04/17/windows-2/</guid>
      <description><![CDATA[当前，人们身边与网络连接的设备的数量不断增加。最新的冰箱和洗衣机甚至都能与 Internet...]]></description>
      <content:encoded><![CDATA[<div class="postcontent">
<p>当前，人们身边与网络连接的设备的数量不断增加。最新的冰箱和洗衣机甚至都能与 Internet 和家庭网络连接。因此，最终用户期待自己的应用也能实现网络连接也就不足为奇。这些&ldquo;连接的应用&rdquo;将使用来自 Web 的最新内容（社交媒体、数字媒体、博客以及其他类型的内容）。开发连接的应用已经实现了常态化，但是处理诸如网络连接断开、按流量计费的网络成本，或性能问题等常见问题仍让开发人员觉得力不从心。Windows 8 让编写连接的应用变得空前简单。</p>
<p>在本篇博文中，我们将介绍一些有用的提示，这些提示有助于用户在使用您的 Windows 应用商店应用时获得快速而流畅、轻松、连接的体验：</p>
<ul>
<li>为您的应用场景选择正确的 API</li>
<li>选择正确的网络功能</li>
<li>为按流量计费的网络调整应用行为</li>
<li>应对网络状态变化</li>
<li>缓存内容以提高流畅性</li>
</ul>
<p>我们来详细看看各条提示。</p>
<h2>选择正确的 API</h2>
<p>如果您打算建造一座房屋，那么您需要适当的工具。您需要锤子来钉钉子，需要拉锯来切割板子，还需要螺丝刀来拧紧螺丝钉。同样的，您在开发连接的 Windows 应用商店应用时也需要使用适当的网络连接 API。Windows 8 提供了大量网络连接 API，这些 API 可让您的应用通过 Internet 或专用网络与其他计算机和设备通信。因此，您的首个步骤是确定您的应用需要怎样的网络连接功能。</p>
<p>最常见的网络连接应用场景是访问网站来检索或存储信息。使用网站来存储用户信息和游戏分数的游戏便是一个简单的例子。而更为复杂的示例可能是连接到一个基于 REST 的 Web 服务，并使用 Web 服务提供的库来访问或存储信息的应用。Windows 8 拥有多个与 Web 服务和网站连接的 API。借助这些 API，您的应用可访问支持 REST 或向 Web 服务器发送基本 HTTP 协议命令（如 GET 和 POST）的 Web 服务。对于 Web 访问，您所需要的 API 将取决于您开发应用所使用的语言：</p>
<ul>
<li><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh453379.aspx" target="_blank">XMLHTTPRequest</a> 和 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br229787.aspx" target="_blank">WinJS.xhr</a> - 使用 JavaScript 和 HTML 编写的应用。</li>
<li><a href="http://msdn.microsoft.com/zh-cn/library/system.net.http.httpclient.aspx" target="_blank">HttpClient</a> - 使用 C# 或 Visual Basic .NET 和 XAML 编写的应用。</li>
<li><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh831163.aspx" target="_blank">XML HTTP Request 2</a> (IXHR2) - 使用 C++ 和 XAML 编写的应用。</li>
</ul>
<p>以下代码显示了如何执行一个基本请求，并响应基于 REST 的 Web 服务。在这一情形中，Web 服务将在 Web 服务器上执行一个 ASP.NET 脚本。</p>
<p><strong>JavaScript</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">function</span> makeXHRCall() {<br />    WinJS.xhr({ uri: <span style="color: #006080;">"http://www.microsoft.com/en-us/default.aspx&rdquo; }).done(<br /> function onComplete(result) {<br /> print(result.responseText);<br /> },<br /> function onError(err) {<br /> print("</span>Error: " + err.responseText);<br />        });<br />}</pre>
</div>
</blockquote>
<p><strong>C#</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">private</span> async <span style="color: #0000ff;">void</span> MakeHttpCall()<br />{<br />    HttpClient httpClient = <span style="color: #0000ff;">new</span> HttpClient();<br />    <span style="color: #0000ff;">try</span> {<br />        <span style="color: #0000ff;">string</span> response = await httpClient.GetStringAsync(<span style="color: #006080;">"http://www.microsoft.com/en-us/default.aspx"</span>);<br />    }<br />    <span style="color: #0000ff;">catch</span> (Exception) {<br />        <span style="color: #008000;">// Handle exception.</span><br />    }<br />}</pre>
</div>
</blockquote>
<p>欲了解有关 <strong>WinJS.xhr</strong> 的更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh761502.aspx" target="_blank">连接到 Web 服务（使用 JavaScript 和 HTML 编写的 Windows 应用商店应用）</a>。欲了解有关 <strong>HttpClient</strong> 的更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/xaml/hh781239.aspx" target="_blank">快速入门：使用 HttpClient 连接（使用 C#/VB/C++ 和 XAML 编写的 Windows 应用商店应用）</a>和 <a href="http://code.msdn.microsoft.com/windowsapps/HttpClient-sample-55700664" target="_blank">HttpClient 示例</a>。</p>
<p>另一常见的网络连接应用场景是下载和上载文件，此时传输过程将持续一段时间。需要从 Web 服务上载或下载照片或相册的相机或照片库应用就是一个例子。这些内容的传输将持续较长时间，因此让用户等待传输完成将毫无意义。<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.backgroundtransfer.aspx" target="_blank">Windows.Networking.BackgroundTransfer</a> API 具有文件下载和上载的功能，即使是在应用不再运行时，上载和下载仍可继续。应用将在其处于前端运行、且具有焦点内容时开始传输进程，即使其不再运行，Windows 8 随后也将在后台继续传输进程。</p>
<p>另一更为特殊的应用场景是访问联合内容。<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.web.syndication.aspx" target="_blank">Windows.Web.Syndication</a> API 可检索 RSS 中的源或 Atom 格式。此外，<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.web.atompub.aspx" target="_blank">Windows.Web.AtomPub</a> API 可让应用以多种 AtomPub 格式发布数据。</p>
<p>对于特定、高级别的网络连接协议无法通过 API 可用的应用场景，Windows 运行时还将支持 TCP 和 UDP 套接字（以及多播）。<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.sockets.aspx" target="_blank">Windows.Networking.Sockets</a> API 将提供一个 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.sockets.streamsocket.aspx" target="_blank">StreamSocket</a> (TCP) 和 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.sockets.datagramsocket.aspx" target="_blank">DatagramSocket</a> (UDP)，从而可让您实施其他较高层的协议。</p>
<p><strong>注意：</strong>Windows 8 中引入了一个名为 WebSocket<em></em> 的新型套接字。欲了解有关 WebSockets 及何时使用它们的更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh761442.aspx" target="_blank">与 WebSockets 连接</a>。</p>
<p>以下表格提供了更完整的受支持的网络连接功能列表，并提供了额外信息的链接。</p>
<div align="center">&nbsp;
<table class="b8table" border="0" cellspacing="0" cellpadding="3" align="center">
<tbody>
<tr>
<td valign="top" width="217">
<p align="center"><strong>API</strong><strong></strong></p>
</td>
<td valign="top" width="221">
<p align="center"><strong>功能</strong><strong></strong></p>
</td>
<td valign="top" width="145">
<p align="center"><strong>示例</strong><strong></strong></p>
</td>
</tr>
<tr>
<td valign="top" width="217">
<ul>
<ul>
<li>
<div align="left"><strong><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.web.syndication.aspx" target="_blank">Windows.Web.Syndication</a></strong><strong></strong></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><strong><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.web.atompub.aspx" target="_blank">Windows.Web.AtomPub</a></strong><strong></strong></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="221">
<p align="left">&nbsp;</p>
<p align="left">在 RSS 或跨多个版本的 Atom 格式中检索源。这些 API 可简化对诸如 OData 等较新格式实施支持的过程。Windows 运行时还将支持 Atom 发布协议，启用 Atom 集合的发布。 <br />欲了解更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh452973.aspx/" target="_blank">访问和管理联合内容</a>。</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="145">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/Syndication-sample-07ef6b0d" target="_blank">联合示例</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/AtomPub-sample-c1fcdc8e" target="_blank">AtomPub 示例</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">RSS 阅读器</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="217">
<p align="left">&nbsp;</p>
<p align="left"><strong><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.backgroundtransfer.aspx" target="_blank">Windows.Networking.BackgroundTransfer</a></strong></p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="221">
<p align="left">&nbsp;</p>
<p align="left">进行不间断、成本感知和可恢复的内容下载和上载，即使是在调用应用并未位于后台的时候，仍可继续。此 API 可支持使用 HTTP、HTTPS 和 FTP 协议传输内容。 <br />欲了解更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh452979.aspx" target="_blank">在后台传输数据</a>。</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="145">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/Background-Transfer-Sample-d7833f61" target="_blank">后台传输示例</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">SkyDrive</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">Xbox 视频</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="217">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><strong><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh453379.aspx" target="_blank">XMLHTTPRequest</a></strong><strong> (JavaScript</strong><strong>) </strong></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><strong><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br229787.aspx" target="_blank">WinJS.xhr</a></strong><strong> </strong><strong>(JavaScript)</strong></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><strong><a href="http://msdn.microsoft.com/zh-cn/library/system.net.http.httpclient.aspx" target="_blank">HttpClient</a></strong><strong> </strong><strong>(C#)</strong></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><strong><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh831163.aspx">XML HTTP Request 2</a></strong><strong> (IXHR2)</strong><strong> </strong><strong>(C++)</strong><strong></strong></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="221">
<p align="left">&nbsp;</p>
<p align="left">与基于 REST 的 Web 服务和其他基于 HTTP 的协议交互。 <br />欲了解更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh761502.aspx" target="_blank">连接到 Web 服务</a>。</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="145">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/XHR-handling-navigation-50d03a7a" target="_blank">XHR、处理导航错误和 URL 架构示例</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/HttpClient-sample-55700664" target="_blank">HttpClient 示例</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">Bing 地图</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="217">
<p align="left">&nbsp;</p>
<p align="left"><strong><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.proximity.aspx" target="_blank">Windows.Networking.Proximity</a></strong></p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="221">
<p align="left">&nbsp;</p>
<p align="left">检测邻近的两台设备，之后应用将使用套接字 API 触发二者之间的网络通信。 <br />欲了解更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh465229.aspx" target="_blank">支持邻近感应和点击</a>。</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="145">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/Proximity-Sample-88129731" target="_blank">邻近感应示例</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">游戏</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">协作式应用</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="217">
<p align="left">&nbsp;</p>
<p align="left"><strong><a href="http://msdn.microsoft.com/library/windows/apps/BR207928" target="_blank">Windows.Storage.Pickers</a></strong><strong></strong></p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="221">
<p align="left">&nbsp;</p>
<p align="left">与远程文件共享通信。 <br />欲了解更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh464959.aspx" target="_blank">访问数据和文件</a>。</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="145">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/File-picker-sample-9f294cba" target="_blank">文件选取器示例</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">Xbox 音乐</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">照片</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="217">
<p align="left">&nbsp;</p>
<p align="left"><strong><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.sockets.aspx" target="_blank">Windows.Networking.Sockets</a></strong><strong></strong></p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="221">
<p align="left">&nbsp;</p>
<p align="left">连接到使用上述 API 不支持的协议（如 SMTP、MAPI 或 telnet）的服务，或连接到其他同一本地网络的另一设备。还可用于需要类套接字语义（异步、双向）来通过 Web（包括 HTTP）连接到新服务的应用。 <br />欲了解更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh452977.aspx" target="_blank">与套接字连接</a>。</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="145">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/StreamSocket-Sample-8c573931" target="_blank">StreamSocket 示例</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/DatagramSocket-sample-76a7d82b" target="_blank">DatagramSocket 示例</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/Connecting-with-WebSockets-643b10ab" target="_blank">与 WebSockets 示例连接</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left"><a href="http://code.msdn.microsoft.com/windowsapps/site/search?query=controlchanneltrigger&amp;f%5B0%5D.Value=controlchanneltrigger&amp;f%5B0%5D.Type=SearchText&amp;ac=3" target="_blank">ControlChannelTrigger 示例</a></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">Lync、协作式白板或股票选择程序</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="217">
<p align="left">&nbsp;</p>
<p align="left"><strong><a href="http://msdn.microsoft.com/library/windows/apps/BR241587" target="_blank">Windows.Storage.ApplicationData</a></strong></p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="221">
<p align="left">&nbsp;</p>
<p align="left">Windows 8 实际上将在不同用户设备之间传输特定的应用数据。应用数据漫游对于应用大有益处，用户可不止使用一台设备，如工作使用的 PC 和在家里使用的平板电脑，并在多台设备上安装应用。 <br />欲了解更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh465094.aspx" target="_blank">漫游应用数据的指导原则</a>。</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="145">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<li>
<div align="left"><a href="http://go.microsoft.com/fwlink/p/?linkid=231478" target="_blank">应用数据示例</a></div>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
<h2>选择正确的网络功能</h2>
<p>网络隔离是 Windows 8 使用的应用安全模型中的一部分。Windows 将主动发现网络边界，并强制执行网络访问限制，以进行网络隔离。当您适当地部署了它们之后，这些功能将有助于保护您的用户和应用免遭恶意攻击。</p>
<p>应用必须声明网络隔离功能，以定义网络访问的范围。如果没有声明这些功能，那么您的应用将无法访问网络资源。请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/Hh770532.aspx" target="_blank">如何使用网络功能</a>来了解有关 Windows 如何为应用强制执行网络隔离的更多信息。</p>
<p>请注意，您无法将网络连接作为 Windows 应用商店应用和同一设备中桌面应用之间的进程间通信机制。因此，您无法使用 Windows 应用商店应用中的 IP 环回地址。出于开发目的，存在有限的例外可允许用户在 Visual Studio 调试程序中操作时使用 IP 环回地址。欲了解更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/Hh780593.aspx" target="_blank">如何启用环回和排除网络隔离的故障</a>。</p>
<p>网络访问请求包含两个类型：</p>
<ol>
<li><strong>客户端启动的出站请求</strong>：您的应用可作为客户端，并通过向远程计算机（通常是一台服务器）发送一个初始网络请求来启动网络访问。应用将向服务器发送一个或多个请求，而服务器将返回一个或多个响应。例如，所有从 Web 客户端应用向 Web 服务器的流量就将是这种类型。</li>
<li><strong>主动提供的入站请求</strong>：您的应用将作为一台网络服务器，并侦听来自远程计算机的入站网络请求。远程计算机将通过向您的应用发送一个初始请求（其中您的应用将作为一台服务器），进而启动网络访问。远程计算机将向您的应用发送一个或多个请求，您的应用将向远程计算机返回一个或多个响应。例如，作为一台媒体服务器的应用就将是该类型。</li>
</ol>
<p>请遵循最低特权的原则，并仅添加您的应用需要的功能。您的应用可能仅需客户端启动的出站请求，或者可能需要接收主动提供的入站请求。某些应用在通过网络进行身份验证时，可能还需要访问用户凭据和证书。</p>
<p>以下表格详细描述了网络隔离功能及连接的应用通常需要的其他相关功能。头三项功能是连接的应用将使用的主要网络隔离功能。事实上，您的应用必须启用这头三项功能中的至少一项。而表格中的其他条目是某些连接的应用经常需要的其他功能。</p>
<p>&nbsp;</p>
<div align="center">
<table class="b8table" style="width: 612px;" border="0" cellspacing="0" cellpadding="3" align="center">
<tbody>
<tr>
<td valign="top" width="146">
<p><strong>网络功能</strong></p>
</td>
<td valign="top" width="273">
<p><strong>描述</strong></p>
</td>
<td valign="top" width="191">
<p><strong>示例应用</strong></p>
</td>
</tr>
<tr>
<td valign="top" width="146">
<p align="left">Internet（客户端）</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="273">
<p align="left">&nbsp;</p>
<p align="left">提供对 Internet 以及机场和咖啡厅等公共场所的网络的出站访问。需要访问 Internet 的大多数应用都应声明这一功能。</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="191">
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">RSS 阅读器</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">社交网络</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">游戏</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="146">
<p align="left">&nbsp;</p>
<p align="left">Internet（客户端和服务器）</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="273">
<p align="left">&nbsp;</p>
<p align="left">提供对 Internet 以及机场和咖啡厅等公共场所的网络的入站和出站访问。对关键端口的入站访问将始终受阻。该功能是 Internet（客户端）功能的一个超集；因此没有必要声明二者。</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="191">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">P2P 应用</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">使用多播来发现玩家的多玩家游戏</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="146">
<p align="left">&nbsp;</p>
<p align="left">专用网络（客户端和服务器）</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="273">
<p align="left">&nbsp;</p>
<p align="left">在用户信任的私人位置提供入站和出站网络访问。这些通常是家庭或工作网络。对关键端口的入站访问将始终受阻。</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="191">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">访问网络连接存储 (NAS) 内容的应用</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">业务线应用</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">通过在专用（家庭或工作）网络中使用多播来发现玩家的多玩家游戏</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="146">
<p align="left">&nbsp;</p>
<p align="left">临近感应</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="273">
<p align="left">&nbsp;</p>
<p align="left">需要与设备进行近场邻近通信。允许应用访问网络来连接到附近设备，需要用户同意发送或接受邀请。</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="191">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">邻近用户的多玩家游戏<em></em></div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="146">
<p align="left">&nbsp;</p>
<p align="left">企业身份验证</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="273">
<p align="left">&nbsp;</p>
<p align="left">提供连接到需要域凭据的企业 Intranet 资源的功能。</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="191">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">业务线应用</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="146">
<p align="left">&nbsp;</p>
<p align="left">共享用户证书</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="273">
<p align="left">&nbsp;</p>
<p align="left">提供访问软件和硬件证书（如智能卡证书）来验证用户身份的功能。当相关的 API 在运行时被调用时，用户必须采取措施（如插入卡片或选择证书）。</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="191">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">虚拟专用网络应用</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<li>
<div align="left">业务线应用</div>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
<p>使用此检查列表来确保您的应用中已配置网络隔离：</p>
<ul>
<li>确定您的应用所需的网络访问方向；客户端启动的出站请求，主动提供的入站请求，抑或二者。</li>
<li>确定您的应用将通信的网络资源的类型：家庭网络或工作网络中的受信任资源，Internet 中的资源，抑或二者。</li>
<li>在应用部件清单 (manifest) 中配置最低所需网络隔离功能。您可使用 Microsoft Visual Studio 2012 中的<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br230259.aspx" target="_blank">应用程序清单设计器</a>来进行这一操作，或<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br211477.aspx" target="_blank">手动添加这些功能</a>。</li>
<li>使用用于故障排除的网络隔离工具来部署和运行您的应用，进而测试您的应用。欲了解更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/Hh780593.aspx" target="_blank">如何启用环回和排除网络隔离的故障</a>。</li>
</ul>
<p>以下屏幕显示了如何使用 Microsoft Visual Studio 2012 中的<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/br230259.aspx" target="_blank">应用程序清单设计器</a>来启用网络功能。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/7268.app-manifest-design_47107BBF.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/7268.app_2D00_manifest_2D00_design_5F00_47107BBF.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="在 Visual Studio 2012 中选择您应用的 package.appxmanifest 内的网络功能" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/7752.app-manifest-design_thumb_5EC7B325.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/7752.app_2D00_manifest_2D00_design_5F00_thumb_5F00_5EC7B325.png" alt="在 Visual Studio 2012 中选择您应用的 package.appxmanifest 内的网络功能" width="600" height="318" border="0" /></a></p>
<p align="center">在 Visual Studio 2012 中选择您应用的 package.appxmanifest 内的网络功能<em></em></p>
<h2>为按流量计费的网络调整应用行为</h2>
<p>想想您上次接近每月数据限额或出国旅行的情形。在这些情形中，我通常将非常小心的使用设备和应用，以避免不必要的网络费用。</p>
<p>Windows 8 通过让应用监控可用网络资源，并在按流量计费的网络中进行相应的操作，进而解决了用户的这一问题。为了增强用户对您的应用的信心，您可让应用在连接可能产生成本的时候告知用户，并调整应用行为以避免或减少费用。</p>
<p><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.aspx" target="_blank">Windows.Networking.Connectivity</a> API 提供了网络连接的类型和成本的信息。这可让您的应用确定何时正常使用网络资源，何时保守使用网络资源，抑或何时先询问用户再使用网络资源。</p>
<p><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectionprofile.aspx" target="_blank">ConnectionProfile</a> 代表了一个网络连接。您的应用可使用 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectionprofile.aspx" target="_blank">ConnectionProfile</a> 的 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectioncost.aspx" target="_blank">ConnectionCost</a> 来确定是否调整其行为。<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectioncost.networkcosttype.aspx" target="_blank">NetworkCostType</a> 属性可显示网络连接的类型。总共包含四种可能的值：</p>
<ul>
<li><strong>Unrestricted</strong> - 网络连接使用不受限制。此类网络连接无使用费和容量限制。</li>
<li><strong>Fixed</strong> - 在特定限值以下此类网络连接的使用将不受限制。</li>
<li><strong>Variable</strong> - 此类网络连接的使用将以每字节为基础计费。</li>
<li><strong>Unknown</strong> - 此类网络连接的成本信息不可知。</li>
</ul>
<p><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectioncost.aspx" target="_blank">ConnectionCost</a> 的数个其他布尔属性仍可提供更多信息。</p>
<ul>
<li><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectioncost.roaming.aspx" target="_blank">Roaming</a> - 该连接面向家庭提供程序以外的网络。</li>
<li><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectioncost.approachingdatalimit.aspx" target="_blank">ApproachingDataLimit</a> - 该连接接近数据计划指定的使用允许时间。</li>
<li><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectioncost.overdatalimit.aspx" target="_blank">OverDataLimit</a> - 该连接已经超过了数据计划指定的使用允许时间。</li>
</ul>
<p>让您的应用响应这些属性指示的条件。当连接为 <strong>Roaming</strong> 时，与网络使用相关的数据成本将非常高。当 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectioncost.networkcosttype.aspx" target="_blank">NetworkCostType</a> 为 <strong>Variable</strong> 时，网络将是一个按流量计费的网络，用户将根据其在网络中发送或接收的数据量付费。当 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectioncost.networkcosttype.aspx" target="_blank">NetworkCostType</a> 为 <strong>Fixed</strong> 时，如果用户已经超过或接近数据极限时，则将产生问题。</p>
<p>借助这些信息，应用可遵循这些指导原则来确定如何最佳使用网络资源。</p>
<div align="center">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<table class="b8table" border="0" cellspacing="0" cellpadding="3" align="center">
<tbody>
<tr>
<td valign="top" width="97">
<p><strong>行为</strong></p>
</td>
<td valign="top" width="150">
<p><strong>连接断开</strong></p>
</td>
<td valign="top" width="168">
<p><strong>应用指导原则</strong></p>
</td>
<td valign="top" width="175">
<p><strong>示例</strong></p>
</td>
</tr>
<tr>
<td valign="top" width="97">
<p align="left">正常</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="150">
<p align="left">&nbsp;</p>
<p align="left"><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectioncost.networkcosttype.aspx" target="_blank">NetworkCostType</a> 为 <strong>Unrestricted</strong> 或 <strong>Unknown</strong>，而 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectioncost.aspx" target="_blank">ConnectionCost</a> 不是 <strong>Roaming</strong></p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="168">
<p align="left">&nbsp;</p>
<p align="left">应用将不会实施限制。应用认为连接成本&ldquo;不受限制&rdquo;，而且不受使用费和容量的限制。<strong></strong></p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="175">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">媒体播放机应用可播放整个高清电影。</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">应用可在不受任何限制或发出提示的情况下下载大型文件。</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="97">
<p align="left">&nbsp;</p>
<p align="left">保守</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="150">
<p align="left">&nbsp;</p>
<p align="left"><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectioncost.networkcosttype.aspx" target="_blank">NetworkCostType</a> 为 <strong>Fixed</strong> 或 <strong>Variable</strong>，而 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectioncost.aspx" target="_blank">ConnectionCost</a> 不是 <strong>Roaming</strong> 或 <strong>OverDataLimit</strong></p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="168">
<p align="left">&nbsp;</p>
<p align="left">应用将实施限制来优化网络使用，进而处理按流量计费网络中的操作。</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="175">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">媒体播放机应用能以较低的分辨率播放电影。</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">应用可延迟非关键下载。</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="97">
<p align="left">&nbsp;</p>
<p align="left">选择加入</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="150">
<p align="left">&nbsp;</p>
<p align="left"><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectioncost.aspx" target="_blank">ConnectionCost</a> 是 <strong>Roaming</strong> 或 <strong>OverDataLimit</strong></p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="168">
<p align="left">&nbsp;</p>
<p align="left">应用将处理网络访问成本远高于计划成本的特殊情况。</p>
<p align="left">&nbsp;</p>
</td>
<td valign="top" width="175">
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<ul>
<ul>
<li>
<div align="left">应用提示用户访问网络。</div>
</li>
</ul>
</ul>
<p align="left">&nbsp;</p>
<ul>
<li>
<div align="left">应用挂起所有后台数据网络活动。<em></em></div>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
<p>该代码示例检查了连接成本，并返回恰当应用行为的建议。</p>
<p><strong>JavaScript</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">var</span> CostGuidance = { Normal: 0, Conservative: 1, OptIn: 2 };<br /><span style="color: #008000;">// GetCostGuidance returns an object with a Cost (with value of CostGuidance), </span><br /><span style="color: #008000;">// CostName (a string) and Reason, which says why the cost is what it is.</span><br /><span style="color: #0000ff;">function</span> GetCostGuidance() <br />{<br />    <span style="color: #0000ff;">var</span> connectionCost = Windows.Networking.Connectivity.NetworkInformation.getInternetConnectionProfile().getConnectionCost();<br />    <span style="color: #0000ff;">var</span> networkCostConstants = Windows.Networking.Connectivity.NetworkCostType;<br />    <span style="color: #0000ff;">var</span> Retval = <span style="color: #0000ff;">new</span> Object();<br />    <span style="color: #0000ff;">if</span> (connectionCost.roaming || connectionCost.overDataLimit)<br />    {<br />        Retval.Cost = CostGuidance.OptIn;<br />        Retval.CostName = <span style="color: #006080;">"OptIn"</span>;<br />        Retval.Reason = connectionCost.roaming<br />            ? <span style="color: #006080;">"Connection is roaming; using the connection may result in additional charge."</span><br />            : <span style="color: #006080;">"Connection has exceeded the usage cap limit."</span>;<br />    }<br />    <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> (connectionCost.networkCostType == networkCostConstants.<span style="color: #0000ff;">fixed</span><br />        || connectionCost.networkCostType == networkCostConstants.variable)<br />    {<br />        Retval.Cost = CostGuidance.conservative;<br />        Retval.CostName = <span style="color: #006080;">"Conservative"</span>;<br />        Retval.Reason = connectionCost.networkCostType == NetworkCostType.<span style="color: #0000ff;">fixed</span><br />            ? <span style="color: #006080;">"Connection has limited allowed usage."</span><br />            : <span style="color: #006080;">"Connection is charged based on usage. "</span>;<br />    }<br />    <span style="color: #0000ff;">else</span><br />    {<br />        Retval.Cost = CostGuidance.Normal;<br />        Retval.CostName = <span style="color: #006080;">"Normal"</span>;<br />        Retval.Reason = connectionCost.networkCostType == networkCostConstants.unknown<br />            ? <span style="color: #006080;">"Connection is unknown."</span><br />            : <span style="color: #006080;">"Connection cost is unrestricted."</span>;<br />    }<br /><br />    <span style="color: #0000ff;">return</span> Retval;<br />}</pre>
</div>
</blockquote>
<p><strong>C#</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">enum</span> NetworkCost { Normal, Conservative, OptIn };<br /><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span> CostGuidance<br />{<br />    <span style="color: #0000ff;">public</span> CostGuidance()<br />    {<br />        var connectionCost = NetworkInformation.GetInternetConnectionProfile().GetConnectionCost();<br />        Init(connectionCost);<br />    }<br />    <span style="color: #0000ff;">public</span> NetworkCost Cost { get; <span style="color: #0000ff;">private</span> set; }<br />    <span style="color: #0000ff;">public</span> String Reason { get; <span style="color: #0000ff;">private</span> set; }<br /><br /><br />    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> Init(ConnectionCost connectionCost)<br />    {<br />        <span style="color: #0000ff;">if</span> (connectionCost == <span style="color: #0000ff;">null</span>) <span style="color: #0000ff;">return</span>;<br />        <span style="color: #0000ff;">if</span> (connectionCost.Roaming || connectionCost.OverDataLimit)<br />        {<br />            Cost = NetworkCost.OptIn;<br />            Reason = connectionCost.Roaming<br />                ? <span style="color: #006080;">"Connection is roaming; using the connection may result in additional charge."</span><br />                : <span style="color: #006080;">"Connection has exceeded the usage cap limit."</span>;<br />        }<br />        <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> (connectionCost.NetworkCostType == NetworkCostType.Fixed<br />            || connectionCost.NetworkCostType == NetworkCostType.Variable)<br />        {<br />            Cost = NetworkCost.Conservative;<br />            Reason = connectionCost.NetworkCostType == NetworkCostType.Fixed<br />                ? <span style="color: #006080;">"Connection has limited allowed usage."</span><br />                : <span style="color: #006080;">"Connection is charged based on usage. "</span>;<br />        }<br />        <span style="color: #0000ff;">else</span><br />        {<br />            Cost = NetworkCost.Normal;<br />            Reason = connectionCost.NetworkCostType == NetworkCostType.Unknown<br />                ? <span style="color: #006080;">"Connection is unknown."</span><br />                : <span style="color: #006080;">"Connection cost is unrestricted."</span>;<br />        }<br />    }<br />}</pre>
</div>
</blockquote>
<p>请使用<a href="http://code.msdn.microsoft.com/windowsapps/Network-Information-Sample-63aaa201" target="_blank">网络信息示例</a>来了解有关如何为按流量计费的网络调整应用行为的更多信息。</p>
<p>用户也可运行&ldquo;任务管理器&rdquo;来查看每个应用消耗的网络数据量。此屏幕截图显示了一个示例。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/5618.task-manager_6C99F920.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/5618.task_2D00_manager_5F00_6C99F920.png"><img style="border: 0px currentcolor; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" title="&ldquo;任务管理器&rdquo;中的&ldquo;应用历史记录&rdquo;选项卡可让用户按应用查看 CPU 和网络消耗情况" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/51/31/metablogapi/3554.task-manager_thumb_4C7EEC63.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-51-31-metablogapi/3554.task_2D00_manager_5F00_thumb_5F00_4C7EEC63.png" alt="&ldquo;任务管理器&rdquo;中的&ldquo;应用历史记录&rdquo;选项卡可让用户按应用查看 CPU 和网络消耗情况" width="600" height="545" border="0" /></a></p>
<p align="center">&ldquo;任务管理器&rdquo;中的&ldquo;应用历史记录&rdquo;选项卡可让用户按应用查看 CPU 和网络消耗情况<em></em></p>
<h2>应对网络状态变化</h2>
<p>在任何移动设备应用场景中，用户都可随意连接网络。移动 3G 或 4G 宽带网络可覆盖用户的家中或车库，只要 Wi-Fi 仍然可用。类似地，Wi-Fi 也可在用户离开家中时进行覆盖。当然有时候网络也不可用。由于 Wi-Fi 和移动宽带网络覆盖面不断扩张，这样的网络变化可能经常出现。</p>
<p><a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.networkinformation.networkstatuschanged" target="_blank">NetworkStatusChanged</a> 表明&ldquo;可用成本&rdquo;或&ldquo;连接选项&rdquo;可能发生变化。<em></em><em></em>为了应对网络状态的变化，并在状态变化时为用户提供一个无缝的体验，请让您的连接应用遵循这些应用场景指导原则。</p>
<h3>错误导致的连接断开</h3>
<p>在大多数情况下，您只需重试网络操作即可重新建立连接。如果失败，则等待 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.networkinformation.networkstatuschanged" target="_blank">NetworkStatusChanged</a> 事件。我们建议为应用设置不断增加的重试间隔回退，从 50 毫秒的间隔开始，如果连接仍然失败，则成倍提高此回退间隔。</p>
<h3>网络断开</h3>
<p>请告知用户连接已断开，然后注册并等待 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.networkinformation.networkstatuschanged" target="_blank">NetworkStatusChanged</a> 事件。</p>
<h3>新网络可用性</h3>
<p>有些情况中设备可能与多个网络连接。例如，用户可能连接了移动宽带，在到家之前使用 Windows 8 消息应用与好友聊天，也可能连接到了不受限制的网络。Windows 8 中的默认策略是首选不受限制的网络，而非流量计费的网络，首选较快的网络，而非较慢的网络。然而，应用确立的现有连接并不会自动切换到新的网络。由于只有应用才能做出是否切换到新网络的最佳决策，因此必须应用必须参与网络选择确定过程。</p>
<p>例如，如果一个视频流接近结束，那么切换到新网络可能毫无意义。然而，如果当前网络正在删除数据包，或者网络速度过慢，抑或该流需要额外的时间才能完成，那么切换到新网络可能是最佳方案。</p>
<p>如果您确定可为您的应用使用情形切换网络，那么请在检测新网络时遵循这些指导原则：</p>
<ol>
<li>检查现有网络和新网络的网络成本。如果切换到新网络有意义，那么请根据我刚刚提到的指导原则尝试建立新的网络连接，并重试网络操作。Windows 将在网络可用的情况下，自动选择不受限制的网络，而非按流量计费的网络，以及选择较快的网络，而非较慢的网络。</li>
<li>如果新网络连接成功建立，那么请在您的应用中使用新的网络连接，并在连接已经存在的情况下，在原来网络中取消原始网络操作。</li>
</ol>
<h3>网络成本的变化</h3>
<p>当 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectioncost.networkcosttype.aspx" target="_blank">NetworkCostType</a> 为 <strong>Fixed</strong>，且使用量接近数据极限或超过数据极限时，网络成本将发生变化。如果 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectioncost.networkcosttype.aspx" target="_blank">NetworkCostType</a> 成为 <strong>Variable</strong>，或如果 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.connectioncost.roaming.aspx" target="_blank">Roaming</a> 变为&ldquo;true&rdquo;时，网络成本也可能发生变化。在这些情况中，请根据此前提示中的指导原则调整应用行为。</p>
<p>这些代码示例将使用 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.networkinformation.networkstatuschanged" target="_blank">NetworkStatusChanged</a> 事件来在网络状态发生多次变化时为用户提供一个无缝的应用体验。两个示例均使用了一个名为 registeredNetworkStatusNotification<em></em> 的全局布尔变量，且其值最初均设为&ldquo;false&rdquo;。<strong></strong></p>
<p><strong>JavaScript</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #008000;">// Register for NetworkStatusChanged notifications, and display new </span><br /><span style="color: #008000;">// Internet ConnectionProfile info upon network status change.</span><br /><span style="color: #0000ff;">function</span> registerForNetworkStatusChange() {<br />    <span style="color: #0000ff;">try</span> {<br /><br />        <span style="color: #008000;">// Register for network status change notifications.</span><br />        <span style="color: #0000ff;">if</span> (!registeredNetworkStatusNotification) {<br />            <span style="color: #0000ff;">var</span> networkInfo.addEventListener(<span style="color: #006080;">"networkstatuschanged"</span>, onNetworkStatusChange);<br />            registeredNetworkStatusNotification = <span style="color: #0000ff;">true</span>;<br />        }<br />    }<br />    <span style="color: #0000ff;">catch</span> (e) {<br />        print(<span style="color: #006080;">"An unexpected exception occurred: "</span> + e.name + <span style="color: #006080;">": "</span> + e.message);<br />    }<br />}<br /><br /><span style="color: #008000;">// Event handler for NetworkStatusChanged event</span><br /><span style="color: #0000ff;">function</span> onNetworkStatusChange(sender) {<br />    <span style="color: #0000ff;">try</span> {<br />        <span style="color: #008000;">// Get the ConnectionProfile that is currently used to connect </span><br />        <span style="color: #008000;">// to the Internet.</span><br />        <span style="color: #0000ff;">var</span> internetProfile = networkInfo.getInternetConnectionProfile();<br />        <span style="color: #0000ff;">if</span> (internetProfile === <span style="color: #0000ff;">null</span>) {<br />            print(<span style="color: #006080;">"Not connected to Internet\n\r"</span>);<br />        }<br />        <span style="color: #0000ff;">else</span> {<br />            internetProfileInfo += getConnectionProfileInfo(internetProfile) + <span style="color: #006080;">"\n\r"</span>;<br />            print(internetProfileInfo);<br />        }<br />        internetProfileInfo = <span style="color: #006080;">""</span>;<br />    }<br />    <span style="color: #0000ff;">catch</span> (e) {<br />        print(<span style="color: #006080;">"An unexpected exception occurred: "</span> + e.name + <span style="color: #006080;">": "</span> + e.message);<br />    }<br />}</pre>
</div>
</blockquote>
<p><strong>C#</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #008000;">// Register for NetworkStatusChanged notifications, and display new </span><br /><span style="color: #008000;">// Internet ConnectionProfile info upon network status change.</span><br /><span style="color: #0000ff;">void</span> NetworkStatusChange()<br />{<br />    <span style="color: #008000;">// Register for network status change notifications.</span><br />    <span style="color: #0000ff;">try</span><br />    {<br />        var networkStatusCallback = <span style="color: #0000ff;">new</span> NetworkStatusChangedEventHandler(OnNetworkStatusChange);<br />        <span style="color: #0000ff;">if</span> (!registeredNetworkStatusNotification)<br />        {<br />            NetworkInformation.NetworkStatusChanged += networkStatusCallback;<br />            registeredNetworkStatusNotification = <span style="color: #0000ff;">true</span>;<br />        }<br />    }<br />    <span style="color: #0000ff;">catch</span> (Exception ex)<br />    {<br />        rootPage.NotifyUser(<span style="color: #006080;">"Unexpected exception occurred: "</span> + ex.ToString(), NotifyType.ErrorMessage);<br />    }<br />}<br /><br /><span style="color: #008000;">// Event handler for NetworkStatusChanged event</span><br />async <span style="color: #0000ff;">void</span> OnNetworkStatusChange(<span style="color: #0000ff;">object</span> sender)<br />{<br />    <span style="color: #0000ff;">try</span><br />    {<br />        <span style="color: #008000;">// Get the ConnectionProfile that is currently used to connect </span><br />        <span style="color: #008000;">// to the Internet </span><br />        ConnectionProfile InternetConnectionProfile = NetworkInformation.GetInternetConnectionProfile();<br /><br />        <span style="color: #0000ff;">if</span> (InternetConnectionProfile == <span style="color: #0000ff;">null</span>)<br />        {<br />            await _cd.RunAsync(CoreDispatcherPriority.Normal, () =&gt;<br />            {<br />                rootPage.NotifyUser(<span style="color: #006080;">"Not connected to Internet\n"</span>, NotifyType.StatusMessage);<br />            });<br />        }<br />        <span style="color: #0000ff;">else</span><br />        {<br />            connectionProfileInfo = GetConnectionProfile(InternetConnectionProfile);<br />            await _cd.RunAsync(CoreDispatcherPriority.Normal, () =&gt;<br />            {<br />                rootPage.NotifyUser(connectionProfileInfo, NotifyType.StatusMessage);<br />            });<br />        }<br />        internetProfileInfo = <span style="color: #006080;">""</span>;<br />    }<br />    <span style="color: #0000ff;">catch</span> (Exception ex)<br />    {<br />        rootPage.NotifyUser(<span style="color: #006080;">"Unexpected exception occurred: "</span> + ex.ToString(), NotifyType.ErrorMessage);<br />    }<br />}</pre>
</div>
</blockquote>
<p>欲了解有关 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.networking.connectivity.networkinformation.networkstatuschanged" target="_blank">NetworkStatusChanged</a> 事件的更多信息，请参阅<a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/hh700376.aspx" target="_blank">快速入门：管理连接事件和可用性变更</a>以及<a href="http://code.msdn.microsoft.com/windowsapps/Network-Information-Sample-63aaa201" target="_blank">网络信息示例</a>。</p>
<h2>缓存内容以提高流畅性</h2>
<p>将内容缓存到磁盘可让您的应用快速、流畅地运行。例如，一个 RSS 源阅读器应用可立即显示从此前会话缓存到磁盘中的源。当最新源可用时，应用将更新其内容。缓存可确保用户在启动应用时立即读取内容，同时应用也可获得新内容。</p>
<p>Windows 8 在 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.storage.aspx" target="_blank">Windows.Storage</a><strong></strong> 命名空间中提供了一个 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.storage.applicationdata.aspx" target="_blank">ApplicationData</a><strong></strong> 类。该类提供了对应用数据存储的访问权限。此数据存储包含设备本地的文件和设置、可在多台设备间漫游的文件和设置，抑或是临时文件和设置。</p>
<p>文件是存储大型数据集、数据库或常见文件格式数据的理想介质。文件可存放于漫游、本地或临时文件夹中。以下内容解释了其含义：</p>
<ul>
<li>漫游文件将在用户使用连接的帐户登录的多台计算机和设备间同步。系统并不会立即漫游文件，而是将权衡数个因素后再确定何时发送数据。请确保数据漫游的使用量低于配额（您可通过 <a href="http://msdn.microsoft.com/zh-cn/library/windows/apps/windows.storage.applicationdata.roamingstoragequota.aspx" target="_blank">RoamingStorageQuota</a><strong></strong> 属性查看配额）；如果使用量超过配额，则数据漫游将被挂起。系统可在应用向文件写入数据时漫游文件，因此当系统不再需要应用的文件对象时，请务必关闭它们。</li>
<li>本地文件将不会在多台计算机间同步。它们将保留于被原始写入的计算机上。</li>
<li>临时文件有可能在系统不使用时被删除。系统将在确定何时及是否删除临时文件时考虑诸如可用磁盘容量和文件年限等因素。</li>
</ul>
<p>这些代码示例可将内容缓存到磁盘中。缓存服务器响应可让您的应用在终止或重新启动后立即向用户显示内容。为简单起见，这些示例并没有向您显示如何向应用数据存储写入设置，以及如何响应漫游数据。<a href="http://code.msdn.microsoft.com/windowsapps/ApplicationData-sample-fb043eb2" target="_blank">应用数据示例</a>将介绍这些详细内容。</p>
<p><strong>JavaScript</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;"><span style="color: #0000ff;">var</span> roamingFolder = Windows.Storage.ApplicationData.current.roamingFolder;<br /><span style="color: #0000ff;">var</span> filename = <span style="color: #006080;">"serverResponse.txt"</span>;<br /><br /><span style="color: #0000ff;">function</span> cacheResponse(strResponse) {<br />    roamingFolder.createFileAsync(filename, Windows.Storage.CreationCollisionOption.replaceExisting)<br />        .done(<span style="color: #0000ff;">function</span> (file) {<br />            <span style="color: #0000ff;">return</span> Windows.Storage.FileIO.writeTextAsync(file, strResponse);<br />        });<br />}<br /><br /><span style="color: #0000ff;">function</span> getCachedResponse() {<br />    roamingFolder.getFileAsync(filename)<br />        .then(<span style="color: #0000ff;">function</span> (file) {<br />            <span style="color: #0000ff;">return</span> Windows.Storage.FileIO.readTextAsync(file);<br />        }).done(<span style="color: #0000ff;">function</span> (response) {<br />            print(response);<br />        }, <span style="color: #0000ff;">function</span> () {<br />            <span style="color: #008000;">// getFileAsync or readTextAsync failed. </span><br />            <span style="color: #008000;">// No cached response.</span><br />        });<br />}</pre>
</div>
</blockquote>
<p><strong>C#</strong></p>
<blockquote>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: 'Courier New', courier, monospace; font-size: 8pt; direction: ltr; background-color: #f4f4f4;">MainPage rootPage = MainPage.Current;<br />StorageFolder roamingFolder = <span style="color: #0000ff;">null</span>;<br /><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">string</span> filename = <span style="color: #006080;">"serverResponse.txt"</span>;<br /><br />async <span style="color: #0000ff;">void</span> cacheResponse(<span style="color: #0000ff;">string</span> strResponse)<br />{<br />    StorageFile file = await roamingFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);<br />    await FileIO.WriteTextAsync(file, strResponse);<br />}<br /><br />async <span style="color: #0000ff;">void</span> getCachedResponse()<br />{<br />    <span style="color: #0000ff;">try</span><br />    {<br />        StorageFile file = await roamingFolder.GetFileAsync(filename);<br />        <span style="color: #0000ff;">string</span> response = await FileIO.ReadTextAsync(file);<br />    }<br />    <span style="color: #0000ff;">catch</span> (Exception)<br />    {<br />        <span style="color: #008000;">// getFileAsync or readTextAsync failed.</span><br />        <span style="color: #008000;">// No cached response.</span><br />    }<br />}</pre>
</div>
</blockquote>
<p>欲了解有关应用数据存储的更多信息，请参阅<a href="http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/07/25/roaming.aspx" target="_blank">漫游您的应用数据</a>博文，并试用<a href="http://code.msdn.microsoft.com/windowsapps/ApplicationData-sample-fb043eb2" target="_blank">应用数据示例</a>。</p>
<h2>总结</h2>
<p>为了给您的用户提供一个卓越、轻松、连接的应用使用体验，请在规划 Windows 应用商店应用时遵循本文中介绍的指导原则。这些提示能够简化您的开发过程，同时确保用户体验的流畅性，并提高用户对您的应用的满意度。</p>
<p>- Windows 二级项目经理 Suhail Khalid</p>
<p>投稿者：Steven Baker 和 Peter Smith</p>
<!-- Title: “创建连接的 Windows 应用商店应用” Tags: “Windows 应用商店应用, HttpClient, 连接的应用, XMLHTTPRequest, IXHR2, XML HTTP Request 2, WinJS.xhr” --></div>]]></content:encoded>
    </item>
    <item>
      <title>立即注册参加 //build/ 2013 大会！</title>
      <link>https://docs.microsoft.com/archive/blogs/windowsappdev_cn/build-2013</link>
      <pubDate>Mon, 08 Apr 2013 14:27:01 GMT</pubDate>
      <dc:creator><![CDATA[Windows 8 Team]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/windowsappdev_cn/2013/04/08/build-2013/</guid>
      <description><![CDATA[上周，Steve Guggenheimer 宣布了 //build/ 2013 大会召开的消息。该大会将于 2013 年 6 月 26 日至 28 日在旧金山莫斯康展览中心举行。//build/...]]></description>
      <content:encoded><![CDATA[<div class="PostContent"><p>上周，Steve Guggenheimer <a href="http://blogs.technet.com/b/microsoft_blog/archive/2013/03/26/announcing-build-2013.aspx" target="_blank">宣布</a>了 //build/ 2013 大会召开的消息。该大会将于 2013 年 6 月 26 日至 28 日在旧金山莫斯康展览中心举行。//build/ 大会是大家深入了解 Windows 应用商店应用开发的绝好机会。该大会将为您提供优秀的演示文稿、演示和信息研讨会等内容。我们工程团队的许多成员将亲临旧金山，为您主持这些演示并回答您的问题。这是您了解有关 Windows 应用商店应用开发的最新、最有价值信息的绝好机会。</p>  <p>即日起（4 月 2 日，星期二）开放注册。<sup></sup>请访问 <a href="http://www.buildwindows.com" target="_blank">www.buildwindows.com</a> 了解有关大会和注册的详细信息。</p>  <p>热切期待您的光临！</p> <!-- Title: “立即注册参加 //build/ 2013 大会！” Tags: “build” --></div>]]></content:encoded>
    </item>
  </channel>
</rss>