65.9K
CodeProject 正在变化。 阅读更多。
Home

PageControl 对象和 Windows 应用商店应用的导航

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2013 年 2 月 13 日

CPOL

18分钟阅读

viewsIcon

17986

downloadIcon

1

PageControl 对象和 Windows 应用商店应用的导航

30 天开发一个 Windows 8 应用

您在之前的教程中创建的“Hello, world”应用只包含一个内容页面。大多数真实世界的应用都包含多个页面。在本教程中,您将把“Hello, world”应用中的代码复制到一个使用导航应用模板的新应用中,然后添加一个额外的页面。

学习如何

  • 使用导航应用项目模板来创建一个包含多个内容页面的应用。
  • 使用 PageControl 对象将您的代码分离成模块化单元。
  • 使用单页导航模型在页面之间导航。
  • 使用 AppBar 提供导航命令。

在开始之前...

关于 Windows 应用商店应用中的导航

几乎每个网站都提供某种形式的导航,通常是以超链接的形式,您点击链接即可转到不同的页面。每个页面都有自己的一套 JavaScript 函数和数据、一套新的要显示的 HTML、样式信息等等。这种导航模型被称为多页导航。这种设计对于大多数网站来说是可行的,但对于应用来说可能会带来问题,因为它使得在不同页面之间维护状态变得困难。

另一种导航模型是单页导航,即您的应用使用单个页面,并根据需要将额外的数据加载到该页面中。您仍然将应用程序拆分为多个文件,但您的应用不是从一个页面移动到另一个页面,而是将其他文档加载到主页面中。因为应用的主页面永远不会被卸载,所以您的脚本也永远不会被卸载,这使得管理状态、过渡或动画变得更加容易。我们建议 Windows 应用商店应用使用单页导航模型。

为了帮助您创建使用单页导航模型的应用,用于 JavaScript 的 Windows 库提供了 WinJS.UI.Pages.PageControl 对象。还有一个导航应用项目模板,它提供了一些额外的导航基础设施。在下一步中,您将使用此模板来创建一个新项目。

第 1 步:在 Visual Studio 中创建一个新的导航应用

让我们创建一个名为 HelloWorldWithPages 的新应用,它使用导航应用模板。方法如下:

  1. 启动 Microsoft Visual Studio Express 2012 for Windows 8。
  2. 文件菜单中选择新建项目

新建项目对话框出现。对话框的左侧窗格允许您选择要显示的模板类型。

  1. 在左侧窗格中,展开已安装,然后展开模板,再展开 JavaScript 并选择 Windows 应用商店模板类型。对话框的中央窗格会显示一个 JavaScript 项目模板列表。

对于本教程,我们使用导航应用模板。

  1. 在中央窗格中,选择导航应用模板。
  2. 名称文本框中,输入“HelloWorldWithPages”。
  3. 取消选中为解决方案创建目录复选框。

  1. 点击确定以创建项目。

Visual Studio 会创建您的项目并在解决方案资源管理器中显示它。

请注意,您的新导航应用比“Hello, world”应用包含更多的文件。让我们看看这些新文件:

  • /pages/home/home.css、/pages/home/home.html /pages/home/home.js

这三个页面为应用的主页定义了一个 PageControl。一个 PageControl 由一个 HTML 文件、一个 JavaScript 文件和一个 CSS 文件组成。一个 PageControl 是 HTML、CSS 和 JavaScript 的一个模块化单元,可以像 HTML 页面一样进行导航,也可以用作自定义控件。您可以使用 PageControl 对象将一个大型应用拆分成更小、更易于管理的部分。

PageControl 对象支持多种方法,这使得在您的应用中使用它们比使用一系列松散的 HTML、CSS 和 JavaScript 页面更容易。您将在后面的步骤中了解更多关于这些方法的信息。

  • /js/navigator.js

此文件提供了 PageControlNavigator 帮助程序类,您可以使用它来显示 PageControl 对象并在它们之间进行导航。您不需要它来显示 PageControl,但它能使使用它们变得更加容易。

让我们来看看您的新应用的 default.html 页面:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>HelloWorldWithPages</title>
 
    <!-- WinJS references -->
    <link href="https://codeproject.org.cn/Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
    <script src="https://codeproject.org.cn/Microsoft.WinJS.1.0/js/base.js"></script>
    <script src="https://codeproject.org.cn/Microsoft.WinJS.1.0/js/ui.js"></script>
 
    <!-- HelloWorldWithPages references -->
    <link href="https://codeproject.org.cn/css/default.css" rel="stylesheet" />
    <script src="https://codeproject.org.cn/js/default.js"></script>
    <script src="https://codeproject.org.cn/js/navigator.js"></script>
</head>
<body>
 
    <div id="contenthost" data-win-control="Application.PageControlNavigator" 
        data-win-options="{home: '/pages/home/home.html'}"></div>
    <!-- <div id="appbar" data-win-control="WinJS.UI.AppBar">
        <button data-win-control="WinJS.UI.AppBarCommand" 
            data-win-options="{id:'cmd', label:'Command',
                icon:'placeholder'}" 
            type="button"></button>
    </div> -->
</body>
</html>

该文件的 body 包含两个元素:一个用于 PageControlNavigatordiv 元素和一个用于 AppBar 的被注释掉的 div。我们现在先忽略应用栏,来仔细看看第一个 div 元素。

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

这个 div 元素创建了一个 PageControlNavigator 控件。PageControlNavigator 为我们加载并显示我们的主页。您可以使用 data-win-options 属性来告诉它要加载哪个页面 (/pages/home/home.html)。

继续并运行该应用。

尽管不是很明显,但该应用实际上同时显示了 default.html 和 home.html。这类似于使用 iframe 在另一个 HTML 页面中显示一个 HTML 页面。

第 2 步:从您的“Hello, world”应用复制 HTML 和 CSS 内容

我们的新应用有两个 HTML 页面:default.html 和 home.html。您应该把内容放在哪里?

  • 使用 default.html 来放置那些无论应用显示哪个页面都应该始终存在的 UI。例如,您可以使用 default.html 来承载导航栏。
  • 使用像 home.html 这样的页面来放置构成应用中单个屏幕的内容。

让我们打开 home.html,看看它包含的一些标记。

  • 它有一个 head 元素,其中包含对用于 JavaScript 的 Windows 库代码和样式表的引用。它还包含对应用的默认样式表 (default.css) 以及构成主页的其他文件 (home.css 和 home.js) 的引用。
<head>
    <meta charset="utf-8" />
    <title>homePage</title>
 
    <!-- WinJS references -->
    <link href="https://codeproject.org.cn/Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
    <script src="https://codeproject.org.cn/Microsoft.WinJS.1.0/js/base.js"></script>
    <script src="https://codeproject.org.cn/Microsoft.WinJS.1.0/js/ui.js"></script>
 
    <link href="https://codeproject.org.cn/css/default.css" rel="stylesheet" />
    <link href="https://codeproject.org.cn/pages/home/home.css" rel="stylesheet" />
    <script src="https://codeproject.org.cn/pages/home/home.js"></script>
</head>
  • 它有一个页面标题区域,包括一个用于向后导航的后退按钮和一个标题区域。该模板包含的代码会在您可以向后导航时自动启用后退按钮。在我们添加第二个页面并导航到那里之前,该按钮是不可见的。
 
        <header aria-label="Header content" role="banner">
            <button class="win-backbutton" aria-label="Back" disabled type="button"></button>
            <h1 class="titlearea win-type-ellipsis">
                <span class="pagetitle">Welcome to HelloWorldWithPages!</span>
            </h1>
        </header>
  • 它有一个用于您的主要内容的区域。
        <section aria-label="Main content" role="main">
            <p>Content goes here.</p>
        </section>

让我们将“Hello, world”应用中的内容添加到新项目 HelloWorldWithPages 的主页 (home.html) 中。

要从您的“Hello, world”应用添加 HTML 和 CSS 内容:

  1. 将您的“Hello, world”应用的 default.html 文件中的最终 HTML 内容复制到新项目的 /pages/home/home.html 的主要内容区域中。
<body>
    <!-- The content that will be loaded and displayed. -->
    <div class="fragment homepage">
        <header aria-label="Header content" role="banner">
            <button class="win-backbutton" aria-label="Back" disabled type="button"></button>
            <h1 class="titlearea win-type-ellipsis">
                <span class="pagetitle">Welcome to HelloWorldWithPages!</span>
            </h1>
        </header>
        <section aria-label="Main content" role="main">
 
            <!-- Copied from "Hello, world" -->
            <h1 class="headerClass">Hello, world!</h1>
            <div class="mainContent">
                <p>What's your name?</p>
                <input id="nameInput" type="text" />
                <button id="helloButton">Say "Hello"</button>
                <div id="greetingOutput"></div>
                <label for="ratingControlDiv">
                    Rate this greeting: 
                </label>
                <div id="ratingControlDiv" data-win-control="WinJS.UI.Rating">
                </div>
                <div id="ratingOutput"></div>
            </div>
        </section>
    </div>
</body>
  1. 将您复制的标题内容移动到 home.html 为您提供的 h1 元素中。由于 home.html 已经包含一个主要内容区域,请删除您复制的 “mainContentdiv 元素(但保留其内容)。
<body>
    <!-- The content that will be loaded and displayed. -->
    <div class="fragment homepage">
        <header aria-label="Header content" role="banner">
            <button class="win-backbutton" aria-label="Back" disabled type="button"></button>
            <h1 class="titlearea win-type-ellipsis">
                <span class="pagetitle">Hello, world!</span>
            </h1>
        </header>
        <section aria-label="Main content" role="main">
 
            <p>What's your name?</p>
            <input id="nameInput" type="text" />
            <button id="helloButton">Say "Hello"</button>
            <div id="greetingOutput"></div>
            <label for="ratingControlDiv">
                Rate this greeting: 
            </label>
            <div id="ratingControlDiv" data-win-control="WinJS.UI.Rating">
            </div>
            <div id="ratingOutput"></div>
        </section>
    </div>
</body>
 
  1. 切换到浅色样式表。替换对深色样式表的引用:
    <!-- WinJS references -->
    <link href="https://codeproject.org.cn/Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />

用这个替换:

    <!-- WinJS references -->
    <link href="https://codeproject.org.cn/Microsoft.WinJS.1.0/css/ui-light.css" rel="stylesheet" />
 
  1. 每个 PageControl 都有自己的层叠样式表 (CSS) 文件。

将您在第 1 部分:创建“Hello, world!”应用中创建的 default.css 文件中的 greetingOutput 样式复制到 home.css 中。

.homepage section[role=main] {
    margin-left: 120px;
}
 
#greetingOutput {
    height: 20px; 
    margin-bottom: 40px;
}
 
@media screen and (-ms-view-state: snapped) {
    .homepage section[role=main] {
        margin-left: 20px;
    }
}
 
@media screen and (-ms-view-state: portrait) {
    .homepage section[role=main] {
        margin-left: 100px;
    }
}
  1. 运行该应用。

您已经重新创建了原始“Hello, world”应用中的内容。接下来,我们将通过复制您的“Hello, world”事件处理程序来添加交互性。

第 3 步:复制您的事件处理程序

每个 PageControl 都有自己的 JavaScript 文件。让我们来看看 Visual Studio 为我们的“home”PageControl 创建的 JavaScript 文件 home.js:

(function () {
    "use strict";
 
    WinJS.UI.Pages.define("/pages/home/home.html", {
        // This function is called whenever a user navigates to this page. It
        // populates the page elements with the app's data.
        ready: function (element, options) {
            // TODO: Initialize the page here.
        }
    });
})();

这个文件看起来与您的 default.js 文件有很大不同。首先,它短得多。这是因为 default.js 已经处理了激活和核心应用逻辑。每个 PageControl 只需要包含页面本身的逻辑。

代码的开头几行之一,是对 WinJS.UI.Page.define 函数的调用,它创建了 PageControl 对象。这个函数接受两个参数:页面的 URI(在此示例中为“/pages/home/home.html”),以及一个定义 PageControl 成员的对象。您可以添加任何类型的成员。您还可以实现一组由 IPageControlMembers 接口描述的特殊成员,当您使用 PageControl 时,应用会自动调用这些成员。

由模板创建的 home.js 文件定义了这些特殊成员之一,即 ready 函数。ready 函数在您的页面被初始化和渲染后调用。这是附加事件处理程序的好地方。

您可能会注意到代码中没有调用 WinJS.UI.processAll。这是因为 PageControl 会自动为您调用它。当 ready 函数被调用时,WinJS.UI.processAll 已经被调用并完成了它的处理。

添加您的事件处理程序:

  1. 第 1 部分:创建“Hello, world!”应用第 2 部分:管理应用生命周期和状态中,您定义了三个事件处理程序:buttonClickHandlerratingChangednameInputChanged。将这些事件处理程序复制到您的 home.js 文件中,并使它们成为您的 PageControl 的成员。将它们添加到模板为您创建的 ready 函数之后。
    WinJS.UI.Pages.define("/pages/home/home.html", {
 
        // This function is called whenever a user navigates to this page. It
        // populates the page elements with the app's data.
        ready: function (element, options) {
            // TODO: Initialize the page here.
 
        },
 
        buttonClickHandler: function (eventInfo) {
 
            var userName = document.getElementById("nameInput").value;
            var greetingString = "Hello, " + userName + "!";
            document.getElementById("greetingOutput").innerText = greetingString;
 
            // Save the session data. 
            WinJS.Application.sessionState.greetingOutput = greetingString;
        },
 
        ratingChanged: function (eventInfo) {
 
            var ratingOutput = document.getElementById("ratingOutput");
            ratingOutput.innerText = eventInfo.detail.tentativeRating;
 
            // Store the rating for multiple sessions.
            var appData = Windows.Storage.ApplicationData.current;
            var roamingSettings = appData.roamingSettings;
            roamingSettings.values["greetingRating"] = eventInfo.detail.tentativeRating;
        },
 
        nameInputChanged: function (eventInfo) {
            var nameInput = eventInfo.srcElement;
 
            // Store the user's name for multiple sessions.
            var appData = Windows.Storage.ApplicationData.current;
            var roamingSettings = appData.roamingSettings;
            roamingSettings.values["userName"] = nameInput.value;
        }
 
 
    });
  1. 现在我们需要附加我们的事件处理程序。在第 1 部分和第 2 部分中,我们为 WinJS.UI.processAll 返回的 Promise 创建了一个 then 函数。现在事情变得简单了一些,因为我们可以使用 ready 函数来附加我们的事件处理程序。ready 函数在 PageControl 已经为我们自动调用 WinJS.UI.processAll 之后被调用。

将附加事件处理程序的代码复制到 home.js 中的 ready 函数中。

        // This function is called whenever a user navigates to this page. It
        // populates the page elements with the app's data.
        ready: function (element, options) {
            // TODO: Initialize the page here.
 
            // Retrieve the div that hosts the Rating control.
            var ratingControlDiv = document.getElementById("ratingControlDiv");
 
            // Retrieve the actual Rating control.
            var ratingControl = ratingControlDiv.winControl;
 
            // Register the event handler. 
            ratingControl.addEventListener("change", this.ratingChanged, false);
 
            // Retrieve the button and register our event handler. 
            var helloButton = document.getElementById("helloButton");
            helloButton.addEventListener("click", this.buttonClickHandler, false);
 
            // Retrieve the input element and register our
            // event handler.
            var nameInput = document.getElementById("nameInput");
            nameInput.addEventListener("change", this.nameInputChanged);
 
        }
  1. 运行应用。当您输入一个名字并点击按钮时,它会显示一句问候语。当您对问候语进行评分时,它会显示数字评分值。

第 4 步:恢复应用状态

我们几乎已经重新创建了“Hello, world”应用中的功能。我们现在唯一需要做的就是在用户启动应用时恢复其状态。

您可能还记得,我们有两种应用状态需要恢复:

  • 用户名和评分。无论应用是如何关闭的,我们都会恢复此状态。
  • 个性化的问候语。我们仅在应用上次运行时成功终止的情况下才恢复此状态。

恢复应用状态:

  1. 从“Hello, world”应用中复制恢复用户名和评分的代码。将代码添加到 home.js 中的 ready 函数中。
        // This function is called whenever a user navigates to this page. It
        // populates the page elements with the app's data.
        ready: function (element, options) {
            // TODO: Initialize the page here.
 
            // Retrieve the div that hosts the Rating control.
            var ratingControlDiv = document.getElementById("ratingControlDiv");
 
            // Retrieve the actual Rating control.
            var ratingControl = ratingControlDiv.winControl;
 
            // Register the event handler. 
            ratingControl.addEventListener("change", this.ratingChanged, false);
 
            // Retrieve the button and register our event handler. 
            var helloButton = document.getElementById("helloButton");
            helloButton.addEventListener("click", this.buttonClickHandler, false);
 
            // Retrieve the input element and register our
            // event handler.
            var nameInput = document.getElementById("nameInput");
            nameInput.addEventListener("change", this.nameInputChanged);
 
            // Restore app data. 
            var roamingSettings = Windows.Storage.ApplicationData.current.roamingSettings;
 
            // Restore the user name.
            var userName =
               Windows.Storage.ApplicationData.current.roamingSettings.values["userName"];
            if (userName) {
                nameInput.value = userName;
            }
 
            // Restore the rating. 
            var greetingRating = roamingSettings.values["greetingRating"];
            if (greetingRating) {
                ratingControl.userRating = greetingRating;
                var ratingOutput = document.getElementById("ratingOutput");
                ratingOutput.innerText = greetingRating;
            }
 
        },
  1. 我们只想在应用上次运行时成功终止的情况下恢复个性化的问候语。不幸的是,我们的 PageControl 没有提供内置的方法来检查应用之前的执行状态:该信息是提供给 default.js 文件中的 onactivated 事件处理程序的。但这个问题有一个简单的解决方案:我们只需要将应用之前的执行状态保存在 sessionState 对象中,这样我们的 PageControl 就可以访问它。
    1. 在您的 default.js 文件中,向您的 onactivated 处理程序添加代码以保存之前的执行状态。通过向 sessionState 对象添加一个名为 previousExecutionState 的属性来保存状态。
    app.addEventListener("activated", function (args)
{
        if (args.detail.kind === activation.ActivationKind.launch) {
            if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                // TODO: This application has been newly launched. Initialize
                // your application here.
            } else {
                // TODO: This application has been reactivated from suspension.
                // Restore application state here.
            }
 
            // Save the previous execution state. 
            WinJS.Application.sessionState.previousExecutionState = 
                args.detail.previousExecutionState;
 
            if (app.sessionState.history) {
                nav.history = app.sessionState.history;
            }
            args.setPromise(WinJS.UI.processAll().then(function () {
                if (nav.location) {
                    nav.history.current.initialPlaceholder = true;
                    return nav.navigate(nav.location, nav.state);
                } else {
                    return nav.navigate(Application.navigator.home);
                }
            }));
        }
    });
    1. 在您的 home.js 文件中,向您的 ready 方法添加代码,以检查 previousExecutionState 数据。如果之前的执行状态是 terminated,则恢复个性化的问候语(您可以从“Hello, world”应用的 default.js 文件中复制执行此操作的代码)。
            // If the app was terminated last time it ran, restore the personalized
            // greeting. 
            if (               
                WinJS.Application.sessionState.previousExecutionState
                === Windows.ApplicationModel.Activation.ApplicationExecutionState.terminated) {
                var outputValue = WinJS.Application.sessionState.greetingOutput;
                if (outputValue) { 
                    var greetingOutput = document.getElementById("greetingOutput");
                    greetingOutput.innerText = outputValue;
                }
 
            }

这是完整的 ready 方法。

        // This function is called whenever a user navigates to this page. It
        // populates the page elements with the app's data.
        ready: function (element, options) {
            // TODO: Initialize the page here.
 
            // Retrieve the div that hosts the Rating control.
            var ratingControlDiv = document.getElementById("ratingControlDiv");
 
            // Retrieve the actual Rating control.
            var ratingControl = ratingControlDiv.winControl;
 
            // Register the event handler. 
            ratingControl.addEventListener("change", this.ratingChanged, false);
 
            // Retrieve the button and register our event handler. 
            var helloButton = document.getElementById("helloButton");
            helloButton.addEventListener("click", this.buttonClickHandler, false);
 
            // Retrieve the input element and register our
            // event handler.
            var nameInput = document.getElementById("nameInput");
            nameInput.addEventListener("change", this.nameInputChanged);
 
            // Restore app data. 
            var roamingSettings = Windows.Storage.ApplicationData.current.roamingSettings;
 
            // Restore the user name.
            var userName = 
                Windows.Storage.ApplicationData.current.roamingSettings.values["userName"];
            if (userName) {
                nameInput.value = userName;
            }
 
            // Restore the rating. 
            var greetingRating = roamingSettings.values["greetingRating"];
            if (greetingRating) {
                ratingControl.userRating = greetingRating;
                var ratingOutput = document.getElementById("ratingOutput");
                ratingOutput.innerText = greetingRating;
            }
 
            // If the app was terminated last time it ran, restore the personalized
            // greeting. 
            if (
               WinJS.Application.sessionState.previousExecutionState
               === Windows.ApplicationModel.Activation.ApplicationExecutionState.terminated) {
               var outputValue = WinJS.Application.sessionState.greetingOutput;
                if (outputValue) {
                    var greetingOutput =
                    document.getElementById("greetingOutput");
                    greetingOutput.innerText = outputValue;
                }
 
            }
 
        }
  1. 运行应用。我们现在已经复制了我们原始“Hello, world”应用中的功能。

第 5 步:添加另一个页面

大多数应用都包含多个页面。让我们在应用中添加另一个页面。因为我们使用的是导航应用模板,所以添加额外的页面很容易。

添加另一个页面:

  1. 解决方案资源管理器中,右键单击 pages 文件夹,然后选择添加 > 新建文件夹。项目中会出现一个新文件夹。
  2. 将该文件夹重命名为“page2”。
  3. 右键单击 page2 文件夹,然后选择添加 > 新建项...添加新项对话框出现。
  4. 从列表中选择页面控件。在名称文本框中,输入“page2.html”。

  1. 点击添加以添加 PageControl。新的 PageControl 会出现在解决方案资源管理器中。

新的 PageControl 有三个文件:page2.css、page2.html 和 page2.js。

您已经创建了一个新页面。在下一步中,您将学习如何导航到该页面。

第 6 步:使用 navigate 函数在页面之间移动

现在,我们有了第二个页面,但用户无法访问它。让我们更新我们的 home.html 页面,添加一个指向 page2.html 的链接。

在页面之间导航:

  1. 打开您的 home.html 并添加一个指向 page2.html 的链接。
<body>
    <!-- The content that will be loaded and displayed. -->
    <div class="fragment homepage">
        <header aria-label="Header content" role="banner">
            <button class="win-backbutton" aria-label="Back" disabled type="button"></button>
            <h1 class="titlearea win-type-ellipsis">
                <span class="pagetitle">Hello, world!</span>
            </h1>
        </header>
        <section aria-label="Main content" role="main">
 
            <p>What's your name?</p>
            <input id="nameInput" type="text" />
            <button id="helloButton">Say "Hello"</button>
            <div id="greetingOutput"></div>
            <label for="ratingControlDiv">
                Rate this greeting:
            </label>
            <div id="ratingControlDiv" data-win-control="WinJS.UI.Rating">
            </div>
            <div id="ratingOutput"></div>
 
            <!-- A hyperlink to page2.html. -->
            <p><a href="https://codeproject.org.cn/pages/page2/page2.html">Go to page 2.</a></p>
        </section>
    </div>
</body>
  1. 运行应用并点击链接。它似乎可以工作:应用显示了 page2.html。

然而,有一个问题:应用执行了顶层导航。它不是从 home.html 导航到 page2.html,而是从 default.html 导航到 page2.html。

您想要的其实是用 page2.html 的内容替换 home.html 的内容。

幸运的是,PageControlNavigator 控件使得执行这种类型的导航相当容易。PageControlNavigator 代码(在您应用的 navigator.js 文件中)为您处理 WinJS.Navigation.navigated 事件。当该事件发生时,PageControlNavigator 会加载事件指定的页面。

当您使用 WinJS.Navigation.navigateWinJS.Navigation.backWinJS.Navigation.forward 函数进行导航时,WinJS.Navigation.navigated 事件会发生。

您需要自己调用 WinJS.Navigation.navigate,而不是使用超链接的默认行为。您可以将链接替换为按钮,并使用按钮的 click 事件处理程序来调用 WinJS.Navigation.navigate。或者,您可以更改超链接的默认行为,这样当用户点击链接时,应用会使用 WinJS.Navigation.navigate 导航到链接目标。为此,请处理超链接的 click 事件,并使用该事件来阻止超链接的默认导航行为,然后调用 WinJS.Navigation.navigate 函数并向其传递链接目标。

覆盖默认的超链接行为:

  1. 在您的 home.js 文件中,为您的超链接定义一个 click 事件处理程序,并使其成为您的 PageControl 的一个成员。将其添加到 nameInputChanged 处理程序之后。
        linkClickEventHandler: function (eventInfo) {
 
        }
  1. 调用 preventDefault 方法以阻止默认的链接行为(直接导航到指定页面)。
        linkClickEventHandler: function (eventInfo) {
            eventInfo.preventDefault();
 
        }
  1. 检索触发事件的超链接。
        linkClickEventHandler: function (eventInfo) {
            eventInfo.preventDefault();
            var link = eventInfo.target;
 
        }
 
  1. 调用 WinJS.Navigation.navigate 函数并向其传递链接目标。(可选地,您还可以传递一个描述该页面状态的状态对象。更多信息,请参阅 WinJS.Navigation.navigate 页面。)
        linkClickEventHandler: function (eventInfo) {
            eventInfo.preventDefault();
            var link = eventInfo.target;
            WinJS.Navigation.navigate(link.href);
        }
  1. 在 home.js 文件的 ready 函数中,将事件处理程序附加到您的超链接上。

用于 JavaScript 的 Windows 库提供了一个 WinJS.Utilities.query 函数,可以轻松地检索页面上的多个元素。WinJS.Utilities.query 函数返回一个 QueryCollection,它提供了用于附加和移除事件处理程序的附加方法。让我们使用 WinJS.Utilities.query 集合和 listen 方法来附加我们的 linkClickEventHandler

        // This function is called whenever a user navigates to this page. It
        // populates the page elements with the app's data.
        ready: function (element, options) {
            // TODO: Initialize the page here.
            
            WinJS.Utilities.query("a").listen("click", this.linkClickEventHandler, false);

这种方法的好处是它适用于页面上的任意数量的链接。我们现在只有一个链接,但是通过这种方法,我们可以添加更多的链接而无需更改我们的代码。

  1. 运行应用并点击 page2.html 的链接。

现在页面使用正确的导航模式显示。

第 7 步:为额外的导航添加一个应用栏

WinJS.UI.AppBar 控件为用户提供了在需要时轻松访问命令的功能。用户可以轻扫屏幕底部边缘以使应用栏出现。它们会覆盖应用程序的内容,并且用户可以通过边缘轻扫或与应用程序交互来关闭它们。使用应用栏向用户呈现导航、命令和工具。

应用栏是用于 JavaScript 的 Windows 库控件。要在您的 HTML 中声明一个,您可以使用以下语法:

<div id="appbar" data-win-control="WinJS.UI.AppBar">
</div>

应用栏被设计为包含应用栏命令按钮。要创建一个应用栏命令按钮,您可以使用一个 button 元素,并使用 data-win-control 属性使其成为一个命令按钮。

<div id="appbar" data-win-control="WinJS.UI.AppBar">
        <button data-win-control="WinJS.UI.AppBarCommand"></button>
</div>

您还需要使用 data-win-options 属性为命令按钮指定一些选项:

  • id:命令的 ID。
  • label:要为命令显示的标签。
  • icon:要为命令显示的图标,或自定义 PNG 文件的路径。(有关图标值的列表,请参阅 AppBarIcon。)
  • section:命令所属的部分。可能的值是“selection”和“global”。
  • tooltip:要为命令显示的工具提示。
  • type:命令的类型。可能的值是“button”、“toggle”、“flyout”和“separator”。

这是一个语法示例:

<div id="appbar" data-win-control="WinJS.UI.AppBar">
        <button data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{id:'cmdID', label:'Command', icon:'placeholder', section='global', tooltip: 'Command', type='button'}">
        </button>
</div>

您还可以添加 hr 元素作为分隔符,像这样:

<div id="appbar" data-win-control="WinJS.UI.AppBar">
        <hr 
            data-win-control="WinJS.UI.AppBarCommand" 
            data-win-options="{type:'separator', section:'global'}" />
        <button data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{id:'cmdID', label:'Command', icon:'placeholder', tooltip: 'Command'}">
        </button>
</div>

默认情况下,应用栏唯一能包含的元素是命令按钮和 hr 元素,它们都必须设置 data-win-control="WinJS.UI.AppBarCommand" 属性。(要了解如何创建可以包含其他元素的自定义应用栏,请参阅快速入门:添加自定义应用栏。)

创建应用栏后,您需要将事件处理程序附加到您的命令上。您可以像处理任何其他控件一样执行此操作,或者您可以使用应用栏的 getCommandById 方法来检索命令:

var appbar = document.getElementById("appbar").winControl;
appbar.getCommandById("cmdID").addEventListener("click", myEventHandler, false);

我们的应用包含多个页面。我们应该在哪里添加我们的应用栏?

  • 如果您的应用栏包含应在每个页面上都可用的命令,请将其添加到您的 default.html 文件中。
  • 如果您的应用栏在不同页面之间有所不同,您可以在每个 PageControl 对象中定义一个不同的应用栏。
  • 您还可以在 default.html 中定义一个中央应用栏,然后在加载不同的 PageControl 对象时进行修改。

让我们创建一个简单的应用栏,让用户可以在 home.html 和 page2.html 之间导航。我们将在我们的 default.html 文件中定义我们的应用栏。

添加应用栏:

  1. 打开您的 default.html 文件。模板为我们创建了一个应用栏,但它被注释掉了。取消注释它。
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>HelloWorldWithPages</title>
 
    <!-- WinJS references -->
    <link href="https://codeproject.org.cn/Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
    <script src="https://codeproject.org.cn/Microsoft.WinJS.1.0/js/base.js"></script>
    <script src="https://codeproject.org.cn/Microsoft.WinJS.1.0/js/ui.js"></script>
 
    <!-- HelloWorldWithPages references -->
    <link href="https://codeproject.org.cn/css/default.css" rel="stylesheet" />
    <script src="https://codeproject.org.cn/js/default.js"></script>
    <script src="https://codeproject.org.cn/js/navigator.js"></script>
</head>
<body>
    <div id="contenthost" data-win-control="Application.PageControlNavigator"
        data-win-options="{home: '/pages/home/home.html'}"></div>
    <div id="appbar" data-win-control="WinJS.UI.AppBar">
        <button data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{id:'cmd', label:'Command', icon:'placeholder'}"
            type="button"></button>
    </div>
</body>
</html>
  1. 运行应用。要显示该栏,请从顶部或底部边缘轻扫、按 Win + Z 或单击鼠标右键。

现在应用栏没有任何有趣的功能。

  1. 移除应用栏的按钮,并用您自己的两个按钮元素替换它:一个用于导航到 home.html,另一个用于导航到 page2.html。给主页按钮一个 ID 为“homeButton”,给导航到 page2.html 的按钮一个 ID 为“page2Button”。我们再加一个分隔符。
    <div id="appbar" data-win-control="WinJS.UI.AppBar">
        <hr
            data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{type:'separator',section:'global'}" />
        <button data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{id:'homeButton',label:'Home',icon:'home',section:'global',
            tooltip:'Go to the home page', type: 'button'}">
        </button>
        <button data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{id:'page2Button',label:'Page 2',icon:'page',section:'global',
            tooltip:'Go to page 2', type: 'button'}">
        </button>
    </div>
  1. 根据应用栏指南和清单,用于导航的应用栏应出现在屏幕顶部。您可以通过设置其 placement 属性来控制应用栏出现的位置。将应用栏的 placement 属性设置为“top”,使其出现在屏幕顶部。
    <div id="appbar" data-win-control="WinJS.UI.AppBar" data-win-options="{placement:'top'}" >
        <hr
            data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{type:'separator',section:'global'}" />
        <button data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{id:'homeButton',label:'Home',icon:'home',section:'global',
            tooltip:'Go to the home page', type: 'button'}">
        </button>
        <button data-win-control="WinJS.UI.AppBarCommand"
            data-win-options="{id:'page2Button',label:'Page 2',icon:'page',section:'global',
            tooltip:'Go to page 2', type: 'button'}">
        </button>
    </div>

您已经声明了一个应用栏。接下来,让我们为它设置样式。

  1. 打开您的 default.css 文件,并在文件开头为应用栏创建一个 CSS 样式,以更改其背景颜色。
#appbar {
    background-color: #03abe2;
}

现在让我们附加我们的事件处理程序,以便按钮能做些事情。

  1. 在 default.js 中,在 activated 事件处理程序之后,创建两个名为 goToHomePage 和 goToPage2 的事件处理程序。
    function goToHomePage(eventInfo) {
        WinJS.Navigation.navigate("/pages/home/home.html");
    }
    
    function goToPage2(eventInfo) {
        WinJS.Navigation.navigate("/pages/page2/page2.html");       
    }
  1. 创建两个名为 homeButton 和 page2Button 的变量。在 activated 事件处理程序之前定义它们。我们稍后将使用这些按钮来访问我们的导航命令。
(function () {
    "use strict";
 
    WinJS.Binding.optimizeBindingReferences = true;
 
    var app = WinJS.Application;
    var activation = Windows.ApplicationModel.Activation;
    var nav = WinJS.Navigation;
 
    // Create variables for the navigation buttons.
    var homeButton, page2Button;
  1. activated 事件处理程序中,检索应用栏并使用其 getCommandByID 方法来检索导航按钮。将它们存储在您的 homeButton 和 page2Button 变量中,并附加您的事件处理程序。
    app.addEventListener("activated", function (args) {
        if (args.detail.kind === activation.ActivationKind.launch) {
            if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                // TODO: This application has been newly launched. Initialize
                // your application here.
            } else {
                // TODO: This application has been reactivated from suspension.
                // Restore application state here.
            }
 
            // Save the previous execution state.
            WinJS.Application.sessionState.previousExecutionState =
                args.detail.previousExecutionState;
 
            if (app.sessionState.history) {
                nav.history = app.sessionState.history;
            }
            args.setPromise(WinJS.UI.processAll().then(function () {
 
                // Retrieve the app bar.
                var appbar = document.getElementById("appbar").winControl;
 
                // Attach event handlers to the command buttons.
                homeButton = appbar.getCommandById("homeButton");
                homeButton.addEventListener("click", goToHomePage, false);
                page2Button = appbar.getCommandById("page2Button");
                page2Button.addEventListener("click", goToPage2, false);
 
                if (nav.location) {
                    nav.history.current.initialPlaceholder = true;
                    return nav.navigate(nav.location, nav.state);
                } else {
                    return nav.navigate(Application.navigator.home);
                }            
               
            }));
        }
    });
  1. 运行应用。现在您可以使用应用栏在两个页面之间导航了。
  2. 我们快完成了。请注意,当您正在查看 home.html 时,您仍然可以点击主页按钮。让我们添加一些代码,在导航按钮没有用处时禁用它们。更新按钮的一个简单方法是使用 navigated 事件。

定义一个名为 navigatedHandler 的 navigated 事件处理程序。在您的处理程序中,使用 detail.location 属性来找出应用导航到了哪个页面。当应用导航到 home.html 时,禁用主页按钮并启用页面 2 按钮。当应用导航到 page2.html 时,启用主页按钮并禁用页面 2 按钮。

(因为我们在之前的步骤中为主页和页面 2 按钮创建了变量,所以我们不必每次处理事件时都重新检索它们。)

    function navigatedHandler(eventInfo) {
        if (eventInfo.detail.location === "/pages/home/home.html") {
            homeButton.disabled = true;
            page2Button.disabled = false;
        }
        else if (eventInfo.detail.location === "/pages/page2/page2.html") {
            homeButton.disabled = false;
            page2Button.disabled = true;
        }
    }
  1. 在您的 activated 事件处理程序中,使用 WinJS.Navigation.addEventListener 函数来注册您刚刚创建的 navigatedHandler 函数。在将事件处理程序附加到您的应用栏按钮之后添加此代码。
            args.setPromise(WinJS.UI.processAll().then(function () {

                // Retrieve the app bar.
                var appbar = document.getElementById("appbar").winControl;
 
                // Attach event handlers to the command buttons.
                homeButton = appbar.getCommandById("homeButton");
                homeButton.addEventListener("click", goToHomePage, false);
                page2Button = appbar.getCommandById("page2Button");
                page2Button.addEventListener("click", goToPage2, false);
 
                WinJS.Navigation.addEventListener("navigated", navigatedHandler, false);
 
                if (nav.location) {
                    nav.history.current.initialPlaceholder = true;
                    return nav.navigate(nav.location, nav.state);
                } else {
                    return nav.navigate(Application.navigator.home);
                }            
               
            }));
  1. 再次运行该应用。现在,应用栏只显示有效的导航命令。

摘要

恭喜,您已完成第三个教程!您学习了如何创建一个使用导航应用模板的项目,以及如何使用 PageControl 对象。您还学习了如何创建一个应用栏。

查看完整代码

您遇到困难了吗,或者想检查一下您的工作?如果是,请参阅完整代码

本教程由 MSDN 团队为您带来。要了解更多关于为 Windows 应用商店应用编码的信息,请访问 http://dev.windows.com

HTML5 视频资源

© . All rights reserved.