新型应用程序

精通用 JavaScript 构建的 Windows 应用商店应用中的控件和设置

Rachel Appel

良好的用户体验可将数据自然而然地以直观的方式展现给用户,无论什么外观尺寸都是如此。展现数据和内容时,需要使用经过更新的 API、控件和工具创造新式体验。在 Windows 应用商店应用中,所需的代码量以及控件的复杂程度取决于创建何种应用程序、它是生产力应用程序、游戏、社交应用程序还是财务应用程序。任何用 JavaScript 构建 Windows 应用商店应用的开发人员均可轻松掌握 Windows JavaScript 库 (WinJS) 控件,接下来我将谈论这些控件。

Windows 8 引入了新的 UI 范例和新的 UI 控件

Windows 应用商店应用的外观和行为与在 Windows 早期版本中运行的程序大相径庭。Windows 中已进行了重大革新,启动后显示新的“开始”页面,上面全都是动态磁贴,作为您与应用程序的第一层交互。其他的明显变化是 Windows 应用商店应用以全屏模式或对齐视图运行,使内容在正面居中,同时命令和菜单停留在视线之外,直到用户请求它们时才出现。

曾经无处不在的最小化、最大化和关闭按钮等 Windows UI 元素在 Windows 应用商店应用中不复存在,因为触摸轻扫和鼠标移动已使这些控件失去用处。若要关闭应用程序,只需从屏幕顶部到底部轻扫或下移鼠标。甚至菜单都不再固定出现在每个屏幕的顶部。在 Windows 应用商店应用中,菜单保持隐藏,直至触摸轻扫或鼠标手势从 AppBar 底部展现这些菜单,如图 1 所示,其中使用一个小型倒计时器应用程序作为示例。


图 1:应用程序底部的 AppBar

如您可在图 1 中所见,菜单在外侧,命令元素先是图形,后是一些文字,而非传统的先是文字菜单,偶尔配有图形。这些元素也完全符合手指大小。如果除了只是底部还需要更多空间容纳选项,则可放置导航栏,即页面顶部的 AppBar。

在传统 Windows 菜单中导航有时非常麻烦。我们都讨厌级联菜单深达 13 级的程序,以至于忘记最初所寻找的内容。在 Windows 应用商店应用中,导航本身与内容交织在一起,因为对 ListView 项的触摸和鼠标手势将调用其他页面。手指开合手势与 Ctrl 键+鼠标滚轮可激活语义式缩放 (bit.ly/16IDtdi),即 Windows 应用商店应用中的控制和导航范例。Semantic­Zoom 是整个 WinJS 控件列表 (bit.ly/w1jLM5) 的一部分。

使用 HTML 和 WinJS 控件

Windows 应用商店应用中主要有两种使用 JavaScript 的控件: 标准 HTML 元素和 WinJS 控件。WinJS 控件是 HTML 与扩展 HTML 元素外观或行为的预生成 JavaScript 相结合。由于这些控件是 HTML,因此可用 CSS 设置 WinJS 控件的样式。图 2 是一个基本 WinJS 控件的示例 WinJS DatePicker,它是由表示日、月和年的多个 DropDown 控件组成的一个控件,显示此代码的默认输出:

 

<span id="eventDate" data-win-control="WinJS.UI.DatePicker" />

The WinJS DatePicker Control
图 2:WinJS DatePicker 控件

当然,图 2 中的 DatePicker 控件在默认 WinJS 样式之外没有其他样式,但可通过重写 .win-datepicker-date、.win-datepicker-month 和 .win-datepicker-year WinJS CSS 选择器,改变这种情况。 使用 .win-datepicker 设置整个控件的样式。

DatePicker(或任何 WinJS 控件)之所以正常工作是因为 HTML5 data-* 特性,称为 data-win-control。 data-win-control 特性表示 WinJS 将在适当位置呈现的控件类型,因此当您将 data-­win-control 特性的值设置为 WinJS.UI.DatePicker 时,该控件呈现图 2 中的下拉列表。 通过 data-win-options 特性,可设置控件的其他属性。 例如,可对 DatePicker 设置默认显示日期以及最小和最大日期范围的 data-win-options。 虽然它名叫 DatePicker,但可更改该控件,让其改为捕获时间,例如小时、分钟和秒。

由于 WinJS 生成并呈现最终控件输出,因此设计时 HTML 与运行时 HTML 看起来颇有区别。 图 3 演示 WinJS 在运行时注入到 host 元素的 HTML。 可从 DOM 资源管理器(“调试”|“Windows”|“DOM 资源管理器”)中查看它。

图 3:DatePicker 呈现三个 DropDown,其中填写了日期/月/年的选项

<span class="win-datepicker" id="eventDate" role="group"
   lang="en-US" dir="ltr" data-win-control="WinJS.UI.DatePicker">
<select tabindex="0" class="win-datepicker-month win-order0"
   aria-label="Select Month">
<option value="January">January</option>
<option value="February">February</option>
<option value="March">March</option>
<option value="April">April</option>
<!-- more <options> that show the other months -->
</select>
<select tabindex="0" class="win-datepicker-date win-order1"
   aria-label="Select Day">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<!-- more <options> that  show day numbers -->
</select>
<select tabindex="0" class="win-datepicker-year win-order2"
   aria-label="Select Year">
<option value="1913">1913</option>
<option value="1914">1914</option>
<option value="1915">1915</option>
<option value="1916">1916</option>
<!—more <options> that show years -->
<option value="2112">2112</option>
<option value="2113">2113</option>
</select>

支持 DatePicker 等 WinJS 控件的代码位于 <ProjectRoot>\References\Windows Library for JavaScript 1.0\js\ui.js 文件内,同某些核心 WinJS 相关组件在一起。 注意,这就是在 Windows 应用商店应用页的 <head> 元素中需要的同一个 <script> 引用,即:

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

修改这些文件需自担风险,因为这些文件由核心 WinJS 代码组成。

在运行时可通过一个名为 winControl 的属性访问包括 DatePicker 在内的任何 WinJS 控件。 WinJS 在运行时向 winControl 属性添加 WinJS 控件类型特有的子属性。 举例来说,ListView 包含其项列表,也可向 WinJS.UI.Ratings 控件查询用户选择的评级。 可按如下方式访问元素的 winControl:

var control = document.getElementById("WinJSElementId").winControl

Button、CheckBox、RadioButton、DropDown、TextBox 等控件的作用均与其在传统的普通 HTML 页中相同;但是,WinJS.UI 命名空间充满了用于许多复杂方案的 UI 控件,包括重要的列表控件。

列表和网格控件

许多应用程序需要在网格或列表中展示数据,因此当然有一个名为 ListView 的控件用于这些方案,该控件可将自身呈现为网格或列表,里面进行了全面的分组,并且项大小可变。 ListView 不仅非常灵活,还可自动缩放以适合屏幕以及根据分辨率和设备显示屏大小将列表项放置在大小可变的行列中等,因此非常适合新式 Windows 体验。

虽然大多数其他 WinJS 控件均独立,但 ListView 与作为模板的对应 HTML 协同工作。 这意味着需要同时设置模板 HTML 和控件容器本身,如图 4 所示。 注意,模板的 data-win-control 和 ListView 的 data-win-options 特性包含将 ListView 与其模板链接在一起的设置。

图 4:创建 WinJS ListView 所需的 HTML

<div id="maincontent">     
  <div id="listViewTemplate"
         data-win-control="WinJS.Binding.Template" >
    <div data-win-bind="style.background: color" class="win-item">
      <h1 data-win-bind=" innerText: daysToGo"></h1>
      <h2 class="subtitle" 
             data-win-bind="innerText: eventTitle"></h2><br />
      <h2 class="subtitle-bottom" 
             data-win-bind=" innerText: eventDate"></h2>
    </div>
  </div>
  <div id="listView" data-win-control="WinJS.UI.ListView"
    class="win-listview"
    data-win-options=
    "{ itemDataSource: Data.items.dataSource,
    itemTemplate: select('#listViewTemplate'),
    selectionMode: 'single'}">
  </div>
</div>

图 4 中含有两个 <div> 元素,一个表示模板,ID 为 listViewTemplate,另一个表示 ListView 自身,名为 listView。 listViewTemplate 元素中含有子元素,后者表示列表或网格中每项的不同字段,如 eventTitle 或 eventDate。 查看图 4 中的 ListView 发现 itemDataSource 属性被设置为 Data.items.dataSource,表示 Data 是一个命名空间,而 items 是一个填充了数据的 WinJS.Binding.List 对象。 由于 JavaScript 使用松散类型的数据,因为只需向 List 构造函数填入一个对象数组,然后它即可绑定到 ListView 控件,类似于下面这段代码:

WinJS.Namespace.define("Data", {
    items: []
});

// NOTE: The 'month' parameter for the Date constructor expects
// the month as a 0-based index (Jan = 0; Dec = 11).
var list = [
  { eventTitle: "Rachel's Birthday", 
   eventDate: new Date(2014, 0, 13) },
  { eventTitle: "Rachel's BFF's Birthday", 
    eventDate: new Date(2013, 4, 29) }
];

list.map(function (item) {
    var daysToGo = item.eventDate.getTime() - Date.now();
    item["daysToGo"] = (daysToGo / (1000 * 60 * 60 * 24)).toFixed();
});

Data.items = new WinJS.Binding.List(list);

此外,可使用 push 方法将项推送到 List 对象上,而非将一个数组传递给 List 的构造函数。 管理 ListView 中数据的最佳方法是通过 AppBar 控件公开相关选项(添加、删除等)。

AppBar 和命令

内容在版式上方是一个重要的 Microsoft 设计原则。 AppBar 是此设计原则的一个组成部分,因为它们保持在视线之外,等待在需要它们时展示其选项。 在代码中,AppBar 就是一个 <div>,其中含有一个或多个 <button> 元素(称为“应用栏命令”),其 data-win-control 特性设置为 WinJS.UI.AppBarCommand。 个别 AppBar 命令之间有区别的功能在于 data-win-options,正如您可能猜到的那样。

检查每个 AppBar 命令在图 5 中的 data-win-options 可发现每个命令的 id、label、icon 和 section。 可将 AppBar 按钮分配给 AppBar 的 global 部分(显示在应用程序屏幕的右下角),或将 section 选项设置为“selection”(以显示在左下角)。 将 AppBar 命令的 section 选项设置为“selection”使这些命令与上下文相关,在用户通过轻扫或单击而选择 ListView 中的某项时,可使用这些命令。

图 5:生成 AppBar

<!-- HTML -->
<div id="appbar" class="win-appbar" 
       data-win-control="WinJS.UI.AppBar">
<button data-win-control="WinJS.UI.AppBarCommand"
   data-win-options="{id:'deleteButton', 
    label:'Delete',
    icon:'delete', section:'selection'}" 
    type="button"></button>
<button data-win-control="WinJS.UI.AppBarCommand"
   data-win-options="{id:'addButton', 
    label:'Add', icon:'add',
    section:'global'}" 
    type="button"></button>
<button 
   data-win-control="WinJS.UI.AppBarCommand"
   data-win-options="{id:'refreshButton',
   label:'Refresh',
   icon:'refresh', 
   section:'global'}" 
   type="button"></button>
</div>
// JavaScript
document.getElementById("addButton").addEventListener(
  "click", this.addButtonClick);
document.getElementById("deleteButton").addEventListener(
  "click", this.deleteButtonClick);
document.getElementById("refreshButton").addEventListener(
  "click", this.refreshButtonClick);

在 HTML 页相关的 JavaScript 文件中,向 AppBar 按钮附加事件侦听器,如同对任何其他 HTML 元素所做的一样。 不需要出现 AppBar 自身的侦听器,因为它根据用户命令自动显示和隐藏自身,但也可以编程方式调用它。 图 5 中的示例展示一个完整的 AppBar,其中含有添加、删除和刷新数据的按钮。

在方案需要时,可编写代码以显示、隐藏、启用和禁用 AppBar 按钮。 

浮出控件

由于触摸屏是头等大事,因此可能已注意到当 UI 元素和对话框显示其自身时,只需点击或单击屏幕上除对话框自身之外的任何部分,即可轻松地使其消失。 这种隐含关闭对话框的理念称为“轻量解除”,并且它是 Windows 8 中 MessageDialog 和 PopupMenu 的默认行为,因为对于用户而言,轻量解除远比处理关闭按钮容易得多。

正如以前的控件一样,浮出控件使用 data-­win-control 特性指定它实际上是 WinJS.UI.Flyout 控件。 浮出控件 <div> 元素的子元素在浮出控件内呈现。 例如,可在浮出控件中放置 HTML 表单,以使用户可填写即将召开的活动的标题和日期,如图 6 中的代码所示,产生图 7 中的内容。

图 6:用 WinJS 控件收集信息的轻量解除浮出控件

<!-- HTML  -->
<div id="eventFlyoutPanel" data-win-control="WinJS.UI.Flyout">
  <table width="100%" height="100%">    
    <tr><td>Event Title:</td><td><input type="text"
 id="eventTitle"/></td></tr>
    <tr><td>Event Date:</td><td id="eventDate"
       data-win-control="WinJS.UI.DatePicker"></td></tr>        
    <tr><td>&nbsp;</td><td align="right">
    <input type="button" id="confirmButton" value="Submit" /></td></tr>
    </table>
</div>
// JavaScript
addButtonClick: function () {
  document.getElementById("eventFlyoutPanel").winControl.show(
  "addButton", "top");
}

A Flyout to Collect Information
图 7:收集信息的浮出控件

注意,图 7 中的浮出控件就是一个 HTML 表单。 当用户点击或单击“Add”(添加)AppBar 命令时,将显示该浮出控件,如图 6 中的 addButtonClick 函数指示的那样。 浮出控件在屏幕上显示的位置与其他控件有关,因此当调用 winControl.show 方法时,即传递控件定位元素的名称以及在何处放置控件,即定位控件的顶边还是底边。

用户从浮出控件中点击或单击任何地方即可离开以使其消失,因为它是轻量接触控件。 您将注意到 Windows 应用商店应用中明显缺少模态对话框,而这正是 Microsoft 设计哲学的一部分。 设计圈的人对模态对话框感到不悦确有理由 — 任何打扰用户或阻止其自由移动的情况均被视为不好的设计。

另一种浮出控件为 SettingsFlyout,它与 Windows 早期版本中的应用程序管理用户首选设置的方式大相径庭。

应用程序设置

Windows 用户对典型的“工具”|“选项”或“帮助”|“关于”菜单选项都不陌生,这些选项将启动充满错综复杂的设置的对话框。 幸运的是,在 Windows 应用商店应用中取代这些对话框的功能对于用户更加直观。 设置和关于页面均显示为较高的垂直浮出控件,当用户从 Windows 超级按钮中选择“设置”图标时调用这些控件 (bit.ly/146cniM)。

“设置”对于 Windows 应用商店应用均有效。 当用户调用“设置”超级按钮时,无论运行的是什么应用程序,均在右侧显示同样的 SettingsFlyout。 隐私策略、用户首选设置、帮助等内容的链接归入 SettingsFlyout。 创建隐私或选项页面的链接只需在通常位于 /js/default.js 中的 app.onactivated 事件中编写几行代码即可:

/ In default.js, app.onactivated

WinJS.Application.onsettings = function (e) {

  e.detail.applicationcommands =
   { "privacypolicy": { title: "Privacy Policy",
 href: "privacy.html" } };

    WinJS.UI.SettingsFlyout.populateSettings(e);

};

当用户点击或单击某个“设置”链接后,即显示其对应的浮出控件。 图 8 包含 SettingsFlyout 的 HTML,其中含有隐私策略信息(在 Windows 应用商店进行发布时隐私策略必须简洁明了)。

图 8:SettingsFlyout 的 HTML(包含隐私策略信息)

<div id="settingsFlyout" 
  data-win-control="WinJS.UI.SettingsFlyout"
  data-win-options="{settingsCommandId:'privacypolicy', width:'narrow'}">
  <div class="win-header" style="background-color:#312e2e">
    <button type="button" onclick="WinJS.UI.SettingsFlyout.show()"
       class="win-backbutton"></button>
    <div class="win-label">Privacy Policy</div>
  </div>
  <div class="win-content">
    <div class="win-settings-section">
      <p>This application does not collect any personal information.</p>
      <p>Internet access is only used to retrieve data from the web,<div id="settingsFlyout" 
  data-win-control="WinJS.UI.SettingsFlyout"
  data-win-options="{settingsCommandId:'privacypolicy', width:'narrow'}">
  <div class="win-header" style="background-color:#312e2e">
    <button type="button" onclick="WinJS.UI.SettingsFlyout.show()"
       class="win-backbutton"></button>
    <div class="win-label">Privacy Policy</div>
  </div>
  <div class="win-content">
    <div class="win-settings-section">
      <p>This application does not collect any personal information.</p>
      <p>Internet access is only used to retrieve data from the web,
         or to allow you to contact the developer:</p>
          <p>
            <a href="mailto:rachel@rachelappel.com">Email Rachel Appel </a>
 <br />
            <a href="http://rachelappel.com/privacy-policy"
             target="_blank">View privacy statement online</a>
          </p>
      </div>
  </div>
</div>

         or to allow you to contact the developer:</p>
          <p>
            <a href="mailto:rachel@rachelappel.com">Email Rachel Appel </a>
 <br />
            <a href="http://rachelappel.com/privacy-policy"
             target="_blank">View privacy statement online</a>
          </p>
      </div>
  </div>
</div>

不要忘记使隐私策略设置文件的名称与用于注册浮出控件的 href 参数相同(见图 8)。

在“设置”中不仅可放置隐私策略。 SettingsFlyout 可包含任何有效的 HTML,并且通常是 ToggleSwitche、CheckBox 和 DropDown 以及函数的宿主,正如“工具”|“选项”对话框现在这样。 但是,正如前述内容,SettingsFlyout 是轻量解除控件,因此只需在其他地方点击一下即可使其消失,刚好与模态对话框相反。 Windows 应用商店应用开发中另一个简单的新范例是 SemanticZoom 控件,这是一个方便的导航帮助器。

语义式缩放

某些应用程序需要处理大量数据。 在这些应用程序中导航可能比较困难,尤其是在其处理大量数据时。 而这正是语义式缩放大展身手之处。 通过语义式缩放,可表达两种模式的数据可视化: 放大和缩小。 默认是放大模式,显示所有数据,而用户必须平移或滚动数据。 缩小模式通常形成数据的聚合表现形式,使用户可轻松导航到某个数据区域,然后放大到某个具体数据项。

SemanticZoom 是三个控件一组: 宿主控件和两个缩放控件,如图 9 所示。 子控件必须实现 IZoomable 才能参与语义式缩放,因此对于 WinJS 应用程序,只有 ListView 可发挥作用。

图 9:SemanticZoom 控件的代码

<div id="semanticZoomDiv" data-win-control="WinJS.UI.SemanticZoom">            
  <!-- The zoomed-in view. -->   
  <div id="zoomedInListView"
    data-win-control="WinJS.UI.ListView"
    data-win-options="{ itemDataSource:
      myData.groupedItemsList.dataSource,
     itemTemplate: select('#mediumListIconTextTemplate'),
     groupHeaderTemplate: select('#headerTemplate'),
     groupDataSource: myData.groupedItemsList.groups.dataSource,
     selectionMode: 'none',
     tapBehavior: 'none',
     swipeBehavior: 'none' }">  </div>
  <!-- The zoomed-out view. -->
  <div id="zoomedOutListView"
    data-win-control="WinJS.UI.ListView"
    data-win-options="{ itemDataSource:
     myData.groupedItemsList.groups.dataSource, itemTemplate:
     select('#semanticZoomTemplate'), selectionMode: 'none',
     tapBehavior: 'invoke', swipeBehavior: 'none' }">
  </div>
</div>

正如您可见,语义式缩放只是在两个 ListView 之间切换,这样使其成为用户转移的良好替代模式 — 开发人员也易于实现。

更多控件

还有更多控件,如进度条、FlipView、弹出菜单、MessageDialog 和 Ratings,所有这些都是 Windows 新体验的一部分,但这里没有篇幅再谈论所有这些控件。 HTML5 和 ECMAScript 5 (ES5) 等开放标准是 WinJS 所有功能的基础,因此从 Web 装订到定位和输入再到 HTML5 音频和视频,所有这些都作为 Windows 应用商店应用开发平台的一部分运行良好。

Rachel Appel是一名顾问、作家、导师和前 Microsoft 员工,在 IT 行业有 20 多年的经验。 她常在 Visual Studio Live!、DevConnections、MIX 等顶级行业大会上发言。 她的专业是开发侧重于 Microsoft 系列开发技术和开放式 Web 并且符合业务和技术需要的解决方案。 有关 Appel 的详细信息,请访问她的网站 rachelappel.com

 衷心感谢以下技术专家对本文的审阅: Keith Boyd (Microsoft)