C--面向对象编程入门指南-四-

97 阅读1小时+

C# 面向对象编程入门指南(四)

原文:Beginning C# object-oriented programming

协议:CC BY-NC-SA 4.0

十二、开发网络应用

在第十一章中,你学习了如何使用 C# 和 WPF 构建一个简单的基于 Windows 的图形用户界面(GUI)。尽管 WPF 让程序员能够轻松地构建极其丰富的用户界面,但假设用户会通过传统的基于 Windows 的 PC 访问你的程序并不总是现实的。随着内部网、web 应用和移动设备的激增,应用现在需要允许用户能够通过各种浏览器和设备访问界面。本章向你展示了如何使用 ASP.NET 构建一个基于网络的用户界面。ASP.NET 是一个 web 框架,支持两种构建 web 应用的方法:Web 表单和 ASP.NET MVC 应用(基于模型-视图-控制器模式)。出于刚开始学习 web 编程的目的,我建议使用 Web 表单。Web 表单提供了你在第十一章中使用过的熟悉的控件和基于事件的编程模型。(关于 Web 表单开发和 MVC 之间区别的讨论,请访问 ASP.NET 官方网站http://www.asp.net/)

在本章中,您将执行以下任务:

  • 创建网页
  • 使用控件
  • 响应页面和控件事件
  • 保持状态
  • 实现数据绑定控件

网页和网络表单

ASP.NET 网页 为你的网络应用提供用户界面。虽然你可以用传统的 HTML 网页建立一个网站,ASP.NET 网页提供了许多优势。它们支持客户端和服务器端应用逻辑的实现。编程模型基于一个熟悉的面向对象的事件驱动范例。ASP.NET 建在山顶上 .NET Framework 并获得它提供的所有好处,如类型安全、托管代码和健壮的类库。ASP.NET 的网页被设计成可以在许多流行的桌面浏览器上运行,比如 Internet Explorer 和 Chrome。当呈现 ASP.NET 网页时,检测请求该页面的浏览器的类型。然后使用正确的 HTML 动态呈现页面,以利用特定于浏览器的功能,如布局、格式和样式。

一个网页由两部分组成:可视化界面和编程逻辑。可视化界面由一个文本文件组成,该文本文件包含 HTML 标记标签、对包含文件的引用、处理指令和客户端脚本块。该文本文件的默认扩展名为。aspx 并被称为页面。页面中包含一个 Web 表单。Web 窗体充当将在浏览器中显示的文本和控件的容器。图 12-1 显示了。包含 Web 窗体的 ASP.NET 页的 aspx 文件,该 Web 窗体包含一个 TextBox 控件、一个 Button 控件和一个 Label 控件。当网页被编译时,代码被合并到一个新的类文件中。然后这个文件被编译成一个继承自System.Web.UI.Page类的新类。时执行的就是这个类代码。已请求 aspx 页面。图 12-2 显示了在浏览器(Internet Explorer)中呈现的页面。

9781430249351_Fig12-01.jpg

图 12-1 。中包含的代码。aspx 文件

9781430249351_Fig12-02.jpg

图 12-2 。在 IE 中呈现的网页

查看页面顶部的 page 指令可以发现编程模型的第二部分,它被称为代码隐藏文件,由. aspx.cs 扩展名表示。代码隐藏文件包含一个分部类文件。代码放在分部类文件中,以处理由页面或页面中包含的控件公开的任何必要事件。下面的类代码包含在链接到。图 12-1 : 中的 aspx 文件

public partial class Page1 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
}

当浏览器请求网页时,页面文件和代码隐藏文件结合在一起,并为。aspx 文件,并编译成一个可执行的页面类文件。正是这个页面类拦截和处理传入的请求。处理完传入的请求后,Page 类动态地创建一个 HTML 响应流并将其发送回浏览器。因为 Page 类是编译代码,所以执行速度比依赖解释脚本的技术快得多。Internet 信息服务(IIS) Web 服务器还能够在内存中缓存执行代码,以进一步提高性能。

Web 服务器控件基础

那个 .NET Framework 提供了一组专门用于在 Web 窗体中承载的 Web 服务器控件。可用的 Web 服务器控件类型包括常见的窗体控件,如 TextBox、Label 和 Button,以及更复杂的控件,如 GridView 和 Calendar。Web 服务器控件从开发人员那里抽象出 HTML 编码。当 Page 类向浏览器发送响应流时,Web 服务器控件使用适当的 HTML 呈现在页面上。发送到浏览器的 HTML 取决于浏览器类型和已进行的控制设置等因素。

下面的代码用于在 Web 窗体中放置一个 TextBox Web 服务器控件:

<asp:TextBox ID="txtName" runat="server" BorderStyle="Dashed"
    ForeColor="#0000C0"></asp:TextBox>

然后,该控件在 Internet Explorer (IE) 10.0 中呈现为以下 HTML 代码:

<input name="txtName" type="text" id="txtName"
style="color:#0000C0;border-style:Dashed;" />

如果 TextBox 控件的TextMode属性被设置为MultiLine,网页中的代码将被修改以反映该属性设置:

<asp:TextBox ID="txtName" runat="server" BorderStyle="Dashed" ForeColor="#0000C0"
 TextMode="MultiLine"></asp:TextBox>

尽管 Web 服务器控件的代码变化很小,但呈现给浏览器的 HTML 代码却变成了一个完全不同的 HTML 控件:

<textarea name="txtName" rows="2" cols="20" id="txtName"
style="color:#0000C0;border-style:Dashed;"></textarea>

Web 服务器控件提供 .NET 程序员的许多优势,包括熟悉的事件驱动对象模型、带有动态呈现的自动浏览器检测、数据绑定功能和自动控件状态维护,等等。

了解网页和 Web 服务器控件继承的层次结构

包含在网页界面代码的顶部。aspx 文件是下面的页面指令:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Page2.aspx.cs" Inherits="Chapter12Demo1.Page2" %>

这段代码揭示了网页接口代码继承自 Page2.aspx.cs 文件中的Page2代码隐藏类。代码隐藏类又继承了系统中的Page类。Web.UI 命名空间:

public partial class Page2 : System.Web.UI.Page

Page类公开了编写 Web 应用和与 Web 服务器交互所需的重要功能。例如,它支持对应用、会话、响应、请求和服务器对象的访问。Application 对象允许 Web 应用的所有用户共享值。Request 对象允许在 Web 请求期间读取网页发送的值。Page类还公开了一些功能,如处理由嵌入在网页中的脚本发起的回发事件、发起页面级验证以及注册服务器控件所需的隐藏字段。

如果您进一步追踪 Web 页面的继承链,您会发现Page类继承了TemplateControl类的功能。此类添加了对加载用户控件的支持,用户控件是通常为在多个网页中划分和重用用户界面(UI)功能而创建的自定义控件。用户控件是在中创建的。ascx 文件并由网页托管。除了管理插入到网页中的用户控件,TemplateControl类还向Page类添加了事务支持和错误处理功能。

TemplateControl类继承自Control类。Control类公开了所有服务器控件所需的大部分功能。这个类包括重要的属性,比如IDPage属性。属性获取并设置控件的编程标识符,属性获取包含控件的页面对象的引用。Control类公开了呈现控件和任何子控件的 HTML 代码的方法。有处理和引发事件的方法,如LoadUnloadPreRender事件。Control类还公开了向 Web 服务器控件添加数据绑定支持所需的功能。图 12-3 显示了Page类在类视图窗口中的层次链。

9781430249351_Fig12-03.jpg

图 12-3 。页面类的层次链

追踪 Web 服务器控件的层次链——例如,TextBox 控件——发现它们也从Control类获得了很多功能。当您将 TextBox 服务器控件放在 Web 表单上时,它会在运行时实例化一个属于System.Web.UI.WebControls命名空间的TextBox类的对象实例。该类公开文本框所需的属性、方法和事件。例如,它将这些属性定义为TextReadOnlyWrap属性。TextBox类还增加了引发和处理TextChanged事件的功能。

所有 Web 服务器控件类都继承了WebControl类的通用功能。WebControl类提供了控制控件在浏览器中呈现时的外观的属性。例如,ForeColorBackcolorHeightWidth属性是在WebControl类中定义的。它还定义了控制控件行为的属性,如EnabledTabIndex属性。WebControl类继承了前面讨论过的Control类。图 12-4 在类图中显示了一个文本框 Web 服务器控件的层次链。

9781430249351_Fig12-04.jpg

图 12-4 。文本框控件的层次结构链

使用 Visual Studio 网页设计器

Visual Studio 集成开发环境(IDE)包括一个优秀的网页设计器。使用设计器,您可以将控件从工具箱拖放到网页上,并使用“属性”窗口设置控件属性。图 12-5 显示了 Visual Studio 设计器工具箱中的一些可用控件。

9781430249351_Fig12-05.jpg

图 12-5 。Visual Studio 网页设计器工具箱

网页设计器在窗口底部包含三个选项卡——设计、拆分和源代码。这些选项卡将窗口中的视图从网页的可视表示(设计)切换到 ASP.NET 和 HTML 标记(源)。拆分视图将设计器和源代码显示为一个拆分窗口。“源”视图显示用于呈现页面的标记代码,并允许您将客户端脚本插入到页面中。图 12-6 显示了在设计器的分割视图中显示的网页。

9781430249351_Fig12-06.jpg

图 12-6 。拆分视图中的 Visual Studio 网页设计器

网页生命周期

当一个网页被请求时,它会经历一系列的阶段,每个阶段都有明确的目的。例如,在初始化阶段,任何主题都被应用到页面上,并且页面上的任何控件都设置了它们的UniqueID属性。在加载阶段,如果页面正在回发(已经呈现过一次,并且正在回发信息),则控件属性将从存储在隐藏控件(称为视图状态和控件状态)中的信息中加载(视图状态将在本章的后面进行更详细的描述)。回发事件处理阶段是调用控件事件处理程序的地方。为了正确调试网页,了解页面生命周期的各个阶段以及它们发生的时间是很重要的。表 12-1 总结了请求页面时发生的各个阶段。

表 12-1。页面请求的阶段

阶段描述
页面请求ASP.NET 确定页面是否需要被解析和编译,或者是否可以发送页面的缓存版本。
开始设置请求和响应等页面属性。页面设置了IsPostBack属性。
初始化页面上的控件可用,并且设置了每个控件的UniqueID属性。如果适用,母版页和主题也将应用于页面。
负荷如果请求是回发,则控件属性将加载从视图状态和控件状态恢复的信息。
回发事件处理如果请求是回发,则调用控件事件处理程序。之后,调用所有验证器控件的 Validate 方法,该方法设置单个验证器控件和页面的IsValid属性。
翻译在呈现之前,保存页面和所有控件的视图状态。在呈现阶段,页面为每个控件调用Render方法。
倾销响应和请求等页面对象被卸载,并执行清理。

当页面在其生命周期中运行时,它会引发各种事件,您可以通过创建运行代码的事件处理程序来响应这些事件。web 页面支持自动事件连接,因此当事件发生时,它会寻找具有标准命名的方法来运行。这些方法是以Page_event的形式出现的,所以要为页面加载事件连接一个事件处理程序,您应该将方法命名为Page_Load。在页面中的控件被初始化后,在页面请求开始时发生Page_Load事件。此事件通常用于从服务器控件读取数据和将数据加载到服务器控件中。查询IsPostBack页面属性以确定页面是第一次被查看还是对回发事件的响应。如果是回发,下面的代码使用 page load 事件在页面上写一条消息。

protected void Page_Load(object sender, EventArgs e)
{
    if (IsPostBack)
    {
        Response.Write("This is a post back.");
    }
}

您可以为其他一些常见的页面事件创建事件处理程序,如Page_InitPage_PreRenderPage_Unload事件。

控制事件

就像 Windows 的图形用户界面(GUI)一样,Web 控件基于事件驱动的模型与用户交互。当客户端发生事件(例如,单击按钮)时,事件信息会在客户端捕获,并通过超文本传输协议(HTTP) post 传输到服务器。在服务器上,事件信息被事件委托对象截获,事件委托对象又通知订阅了调用列表的任何事件处理程序方法。虽然这听起来很复杂 .NET Framework 类抽象并封装了您的大部分流程。

因为通过回发处理事件的开销需要从浏览器到服务器的往返,所以 Web 窗体和服务器控件只公开有限的一组事件。服务器端事件处理程序不支持经常发生的事件,如鼠标移动和按键事件。这些事件通过用脚本编写的客户端事件处理程序来支持。使用服务器端事件处理程序可以响应的一个常见事件是按钮单击事件。

protected void Button1_Click(object sender, EventArgs e)
{
    Response.Write ("Hello " + this.TextBox1.Text);
}

为了将事件绑定到按钮控件,onClick属性被添加到按钮的标记代码中。连接起来响应事件的控件也必须有一个对表单容器集唯一的 ID。

<asp:Button ID="Button1" runat="server" Text="OK" OnClick="Button1_Click"/>

按照约定,控件的事件处理程序方法的名称是发出事件的对象的名称,后跟一个下划线字符和事件的名称(类似于页级事件处理)。然而,事件处理程序的实际名称并不重要。

所有事件处理程序都必须提供两个参数,这两个参数将在事件触发时传递给方法。第一个参数是发送方,它表示启动事件的对象。类型为System.EventArgs的第二个参数e是一个对象,用于传递特定事件的任何特定信息。例如,当单击 DataList 控件中某项的编辑按钮时,就会引发EditCommand事件。事件参数 e 用于传递有关 DataList 控件中正在编辑的项的信息。以下代码检查在DataList中选择的项目是否被启用:

protected void DataList1_EditCommand(object source, DataListCommandEventArgs e)
{
    if (e.Item.Enabled)
    {
        //Add code here.
    }
}

了解应用和会话事件

伴随着页面和控件事件 .NET Framework 提供了拦截和响应在 Web 会话开始或结束时引发的事件的能力。当用户从应用请求页面时,Web 会话开始,当会话被放弃或超时时,Web 会话结束。除了会话事件之外 .NET Framework 公开了几个应用级事件。第一次有人请求 Web 应用中的页面时,就会发生Application_Start事件。Application_BeginRequest在请求任何页面或服务时发生。当结束请求、验证用户、引发错误和停止应用时,会触发相应的事件。会话级和应用级事件处理程序位于 Global.asax.cs 代码隐藏文件中。图 12-7 显示了代码编辑器中的 Global.asax.cs 文件。

9781430249351_Fig12-07.jpg

图 12-7 。在代码编辑器中打开 Global.asax.cs 文件

当应用开始时,Global.asax 编译成一个动态生成的类,该类从HttpApplication基类派生而来。此类公开并定义 ASP.NET 应用中所有应用对象通用的方法、属性和事件。对HttpApplication类的详细解释超出了本书的范围。有关更多信息,请参见帮助文件中的HttpApplication对象。

image 注意global . asax 文件是可选的,必须添加到 web 应用中。

活动 12-1。使用网页和服务器控件

在本活动中,您将熟悉以下内容:

  • 创建基于 Web 表单的 GUI 应用
  • 使用页面和服务器控件事件

创建网络应用

要创建 Web 应用,请按照下列步骤操作:

  1. 启动 Visual Studio。在“文件”菜单上,选择“新建网站”。

  2. Choose an ASP.NET Empty Web Site. Change the name of the web site at the end of location path Activity12_1. By default, web sites are stored in a folder under the path Documents\Visual Studio 2012\WebSites. Figure 12-8 shows the New Web Site dialog box for creating a Web Site or Service.

    9781430249351_Fig12-08.jpg

    图 12-8 。新建网站对话框

  3. 在“新建网站”对话框中单击“确定”按钮。

  4. 创建网站后,您需要添加一个 Web 表单。在“网站”菜单下,选择“添加新项目”并添加名为 Page1.aspx 的 Web 表单

  5. 您将看到源代码视图中的网页设计器。通过单击窗口左下角的选项卡切换到设计视图。

  6. Select the <div> tag at the bottom of the Design window (see Figure 12-9).

    9781430249351_Fig12-09.jpg

    图 12-9 。选择 div 标签

  7. In the Properties window, select the style property and click on the ellipses to launch the Modify Style dialog window. Under the Position category, set the height to 200 pixels (see Figure 12-10) and click the OK button

    9781430249351_Fig12-10.jpg

    图 12-10 。设置 div 高度

  8. 将页面设计窗口切换到拆分模式。从工具箱中拖动一个 Label 控件,并将其放在源代码窗格中的 div 标记之间。将文本更改为“Hello Stranger…”并将 ID 更改为 lblMessage。

  9. You may get a message asking you to synchronize the design view and the source view. After synchronizing the views, your designer should look similar to Figure 12-11.

    9781430249351_Fig12-11.jpg

    图 12-11 。添加标签控件

  10. In a similar process to step 9, add the controls in Table 12-2 between the div tags. Don’t worry about the positioning on the form yet.

表 12-2。Web 控件属性
| **控制** | **属性** | **值** |
| --- | --- | --- |
| 标签 | `ID` | `lblName` |
|  | `Text` | `Enter your name:` |
| 文本框 | `ID` | `txtName` |
|  | `Text` | `[Blank]` |
| 纽扣 | `ID` | `btnSubmit` |
|  | `Text` | `Submit` |

11. To position a control, select the control in the designer and in the format menu, select Set Position and check Absolute (see Figure 12-12).

![9781430249351_Fig12-12.jpg](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/d57c514ab804417aa5acfa3a8116d501~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1771068300&x-signature=BXSEfV47zOoV%2F8TDW9EX4u3F0W0%3D)

图 12-12 。打开绝对定位

12. Turn on absolute positioning for the rest of the controls. You should now be able to drag them around in the visual designer to position them. Arrange the controls on the form so they look similar to Figure 12-13.

![9781430249351_Fig12-13.jpg](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/d678193ad2ff43bfb7d6e5bfaa19ff00~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1771068300&x-signature=N%2FImMrkkOny100PIXhzrcAwmAXY%3D)

图 12-13 。定位控件

创建服务器端控件事件处理程序

要创建服务器端控件事件处理程序,请遵循以下步骤:

  1. 在设计窗格中,双击提交按钮。此操作将打开 Page1.aspx.cs 代码隐藏页,并为按钮的 click 事件创建一个事件处理程序方法。

  2. In the button’s click event handler, check to see if the page request is the result of a postback. If it is, change the lblMessage text to say hello using the text contained in the txtName control:

    protected void btnSubmit_Click(object sender, EventArgs e)
    {
        if (IsPostBack)
        {
            lblMessage.Text = "Hello " + txtName.Text;
        }
    }
    
  3. Using the Debug menu, launch the page in debug mode. The first time you launch the debugger for a web project, you will see a dialog box asking if you want to enable debugging in the Web.config file. Click the OK button to enable debugging. When the page displays in the browser, enter your name and click the Submit button. Verify that the page redisplays with your name included in the Hello… message. After testing, close the browser.

    创建非回发服务器控件事件

    要创建非回发服务器控件事件,请执行以下步骤:

  4. 使用页面设计器,将 RadioButtonList 控件添加到 Page1。将其位置更改为 absolute,并将其放在 textbox 下方。

  5. Change the ID property to rblFontColor. Click the Items collection in the Properties window to display the ListItem Collection Editor dialog box. Add a Black item and a Red item to the list (see Figure 12-14).

    9781430249351_Fig12-14.jpg

    图 12-14 。添加列表项

  6. In the Properties window for the rblFontColor, click the events button to display the event list (see Figure 12-15).

    9781430249351_Fig12-15.jpg

    图 12-15 。选择控制事件

  7. 双击SelectedIndexChanged事件。这将带您进入代码隐藏页,并设置处理事件的方法。添加以下代码以更改事件处理程序中 lblMessage 控件的字体颜色:

    protected void rblFontColor_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (rblFontColor.SelectedItem.Value == "Red")
        {
            lblMessage.ForeColor = System.Drawing.Color.Red;
        }
        else
        {
            lblMessage.ForeColor = System.Drawing.Color.Black;
        }
    }
    
  8. 使用“调试”菜单,在调试模式下启动页面。当页面显示在浏览器中时,单击红色单选按钮。请注意,事件不会导致回发发生。此事件将一直排队,直到 AutoPostBack 事件触发回发。

  9. 在 txtName 控件中输入您的姓名,然后单击 Submit 按钮。处理回发时,会处理rblFontColor的 SelectedIndexChanged 事件和提交按钮的 Click 事件。

  10. 测试后,关闭浏览器并退出 Visual Studio。

image 注意本节只探讨了服务器端事件处理。如果浏览器支持客户端脚本和动态 HTML,您可以将 JavaScript 代码添加到。aspx 页来利用客户端处理功能。

在 Web 应用中存储和共享状态

Web 应用使用无状态协议(HTTP)在浏览器和服务器之间进行通信。然而,在许多 Web 应用中,在用户会话期间需要存在某种状态维护。传统上,有效地维护状态对 Web 程序员来说是一个挑战。为了帮助减轻状态管理的挑战 .NET Framework 提供了几个选项来管理客户端和服务器端的状态。

维护视图状态

Web 应用中需要进行的一种状态管理是在表单回发到客户端时保留控件属性值。因为网页在每次被请求时都会被重新创建和销毁,所以当页面被回发时,客户端对控件所做的任何更改都会丢失。例如,在文本框控件中输入的任何信息在回发时都会丢失。为了克服这种状态丢失,服务器控件有一个ViewState属性,它提供了一个字典对象,用于在回发之间保留控件的值。处理页面时,页面和控件的当前状态被散列到一个字符串中,并保存为页面上的隐藏字段。在回发期间,当初始化控件以将页面呈现回浏览器时,将检索并还原这些值。下面的代码显示了呈现给浏览器的视图状态隐藏控件:

<input type="hidden " name="__VIEWSTATE"
value="dDw1Njg2NjE2ODY7O2w8XN0MTowOz4+upC3lZ6nNLX/ShtpGHJAmi8mpH4=" />

使用查询字符串

虽然视图状态用于存储向同一页面回发之间的信息,但您通常需要将信息从一个页面传递到另一个页面。实现这一点的一种流行方法是使用查询字符串。查询字符串被附加到页面请求 URL 例如,下面的 URL 将一个 ID 字段传递给页面,该页面可用于在数据库中查找值。

http://LocalHost/LookUpAuthorInfo.aspx?AuthorId=30

通过用&符号分隔键值对,可以传递多个查询字符串值。

http://LocalHost/LookUpAuthorInfo.aspx?AuthorId=30&BookTitleId=355

要从查询字符串中检索值,可以使用Request对象的QueryString属性。

int ID = int.Parse(Request.QueryString["AuthorId"]);

image 注意使用查询字符串的一个缺点是,它们在浏览器中很容易看到,用户可以很容易地更改值。不要使用查询字符串传递敏感数据。

使用 Cookies

您可以使用 cookies 在位于客户端设备上的文本文件中存储少量数据。HttpResponse类的Cookie属性提供了对Cookies集合的访问。Cookies集合包含客户端在Cookies头中传输给服务器的 cookies。该集合包含最初在服务器上生成的 Cookie,并在 Set-Cookie 标头中传输到客户端。因为浏览器只能将 cookie 数据发送到最初创建 cookie 的服务器,并且 cookie 信息在发送到浏览器之前可以被加密,所以这是一种相当安全的维护用户数据的方式。cookies 的一个常见用途是向客户端发送用户身份令牌,下次用户访问网站时可以检索到该令牌。然后使用这个令牌从数据库中检索特定于客户端的信息。使用 cookies 是在访问站点之间维护客户端状态的好方法。在下面的代码中,创建了一个Cookie对象来保存用户访问的日期和时间。这个Cookie对象随后被添加到Cookies集合中,并发送给浏览器:

HttpCookie visitDate = new HttpCookie("visitDate");
DateTime now = DateTime.Now;
visitDate.Value = now.ToString();
visitDate.Expires = now.AddHours(1);
Response.Cookies.Add(visitDate);

要读取 cookie,可以使用Request对象。下面的代码读取 cookies 值并将其加载到 Textbox 控件中。

if (Request.Cookies["visitDate"] != null)
{
    txtLastVisit.Text = Request.Cookies["visitDate"].Value;
}

维护会话和应用状态

Web 应用中经常需要的另一种状态管理是会话状态。会话状态是在用户请求 Web 应用中的各种页面时维护与用户相关的信息的能力。会话状态在服务器上维护,并由HttpSessionState类的对象实例提供。此类提供对 Web 应用的会话状态值和会话级设置的访问。会话状态值存储在只能由当前浏览器会话访问的键值字典结构中。下面的代码检查Session对象,查看当前用户的电话号码是否存储在会话键值对中。Session对象公开了HttpSessionState类的一个对象实例:

if (Session["phone"]==null)
{
    txtphone.Visible = true;
}

默认情况下,会话状态存储在 web 服务器的内存中。有时,您希望会话状态更持久,并在服务器重新启动时保持值不变。在这些情况下,可以将会话值存储在 SQL Server 数据库或 ASP.NET 会话状态服务器中。

尽管会话状态的范围是基于每个会话的,但有时 Web 应用需要在应用中的所有会话之间共享一个状态。您可以使用HttpApplicationState类的对象实例来实现这种全局范围的状态。应用状态存储在类似于会话状态的键-值字典结构中,只是它可用于应用中的所有会话和所有表单。客户机第一次从 Web 应用请求任何 URL 资源时,会创建一个HttpApplicationState类的实例。这个实例通过固有的Application对象公开。以下代码使用Application对象来存储SqlConnection对象的连接字符串属性:

string pubsConnectionString;
pubsConnectionString = "Integrated Security=SSPI;Initial Catalog=pubs;" +
    "Data Source=localhost";
Application["PubsConnectionString"] = pubsConnectionString;

image 注意应用状态也可以使用System.Web.Caching名称空间中的 cache 类存储在缓存中。

活动 12-2。维护 WEB 应用中的状态

在本活动中,您将熟悉以下内容:

  • 如何在 web 窗体中维护视图状态
  • 如何使用 cookies 存储和检索数据
  • 会话和应用状态之间的区别

维护视图状态

要调查视图状态,请按照下列步骤操作:

  1. 启动 Visual Studio。在“文件”菜单上,选择“新建网站”。

  2. 选择一个 ASP.NET 空网站。在位置路径练习 12_2 的末尾,更改网站的名称。

  3. After the Web Site is created, add a Web Form named Page1.aspx. On the Web form add a Textbox control and a Button control using the following mark up.

    <form id="form1" runat="server">

    <div>

    <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>

    <asp:Button ID="Button1" runat="server" Text="Button" />

    </div>

    </form>

  4. Launch the page using the debugger. After the page loads, right-click on it and chose View source. You should see the mark up for a hidden control used to store the view state.

    <div class="aspNetHidden">

    <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="s8jATaLWaO0KhQ+roBl8XjQi6d2d0KSSbBx8tZAvxtIM8ih2HwPQotse+N13fzpd3g+w7fbor7Hi2uGUuJ5+H+IomzQJHTf50lldSWwLD3s=" />

    </div>

  5. Stop the debugger and add a click event handler to the button.

    <asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click"/>

  6. In the code behind class (Page1.aspx.cs) add the method to handle the button click event. In this event you will save and retrieve a value from the view state that records how many times the button is clicked.

    protected void Button1_Click(object sender, EventArgs e)

    {

    int count;

    if (IsPostBack)

    {

    if (ViewState["count"] != null)

    {

    count = (int)ViewState["count"];

    count += 1;

    ViewState["count"] = count;

    }

    else

    {

    count = 1;

    ViewState.Add("count", count);

    }

    }

    Response.Write(ViewState["count"]);

    }

  7. 使用调试器启动页面。点击按钮;您应该会在页面顶部看到一个计数器,每次点击都会增加。测试完成后,停止调试器。

读写 cookie

要使用 cookies 进行调查,请遵循以下步骤:

  1. 将第二个 Web 表单添加到名为 Page2 的项目中。

  2. On Page1 add a second button and add the following code to the button’s click event handler. This code saves the textbox text to a cookie. It then transfers execution to Page2.

    protected void Button2_Click(object sender, EventArgs e)

    {

    HttpCookie visitor = new HttpCookie("visitor");

    visitor.Expires = DateTime.Now.AddDays(1);

    visitor.Value =TextBox1.Text;

    Response.Cookies.Add(visitor);

    Server.Transfer("Page2.aspx");

    }

  3. In Page2’s Page_Load method, retrieve the value of the cookie using the Request object and write it out to the page using the Response object.

    protected void Page_Load(object sender, EventArgs e)

    {

    if (Request.Cookies["visitor"] != null)

    {

    Response.Write("Hello " + Request.Cookies["visitor"].Value);

    }

    }

  4. 使用调试器启动第 1 页。在文本框中键入您的姓名,然后单击第二个按钮;您应该看到第 2 页显示了 hello 消息。测试完成后,停止调试器。

会话和应用状态

要调查会话和应用状态,请完成以下步骤:

  1. 使用“添加新项”对话框,添加名为 Global.asax 的全局应用类。

  2. In the Global.asax code page add the following code to the Session_Start event handler.

    void Session_Start(object sender, EventArgs e)

    {

    Session["SessionStartTime"] = DateTime.Now;

    }

  3. 将第三个 Web 表单添加到名为 Page3 的项目中。

  4. In the Form_Load event handler for Page3 add the following code to display the session start time.

    protected void Page_Load(object sender, EventArgs e)

    {

    Response.Write(Session["SessionStartTime"]);

    }

  5. 在“解决方案资源管理器”窗口中,右击 Page3.aspx 并选择“在浏览器中查看”。您应该会看到显示的会话开始时间。

  6. 大约 30 秒后刷新页面。时间不应该改变,因为您在同一个会话中。

  7. 启动浏览器的一个新实例,并将第 3 页的网址从第一个浏览器窗口复制到第二个浏览器。您应该看到第 3 页显示了新的时间,因为这是一个新的会话。测试完成后,关闭浏览器。

  8. In the Global.asax code page add the following code to the Application_Start event handler.

    void Application_Start(object sender, EventArgs e)

    {

    Application["ApplicationStartTime"] = DateTime.Now;

    }

  9. In the Form_Load event handler for Page3 add the following code to display the application start time.

    protected void Page_Load(object sender, EventArgs e)

    {

    Response.Write(Session["SessionStartTime"]);

    Response.Write("<br/>");

    Response.Write(Application["ApplicationStartTime"]);

    }

  10. 在“解决方案资源管理器”窗口中,右击 Page3.aspx 并选择“在浏览器中查看”。您应该会看到显示的会话和应用启动时间。

  11. 启动浏览器的一个新实例,并将第 3 页的网址从第一个浏览器窗口复制到第二个浏览器。您应该看到第 3 页显示了不同的会话开始时间,但应用开始时间相同,因为这是一个新会话,但应用开始时间相同。测试完成后,关闭浏览器并退出 Visual Studio。

数据绑定 Web 控件

正如传统的基于 Windows 的 GUI 一样,使用基于 Web 的 GUI 开发的程序经常需要与数据进行交互。业务应用的用户需要查看、查询和更新后端数据存储中的数据。幸运的是 .NET Framework 通过公开在 Web 窗体中实现数据绑定控件所需的功能,使这一点变得很容易。

数据绑定控件使在 web 表单中显示数据的过程自动化。ASP.NET 提供了各种控件来显示数据,具体取决于数据的类型、显示方式以及是否可以更新。其中一些控件用于显示只读数据,例如,Repeater 控件显示只读数据的列表。如果需要更新列表中的数据,可以使用 ListView 控件。如果需要以表格格式显示许多列和行的数据,可以使用 GridView 控件。如果需要一次显示一条记录,可以使用 DetailsView 或 FormView 控件。

数据绑定自动化了从数据存储区和数据绑定控件传输数据的过程。为了绑定控件,需要向网页添加一个 DataSource 控件。DataSource 控件不呈现 UI 标记。相反,它们负责提供数据源和数据绑定控件之间的链接。根据数据源的不同,有几种类型的数据源控件可用。表 12-3 列出了提供的数据源类型 .NET 框架。

在下面的标记中,SqlDataSource 控件用于向 Repeater 控件提供数据,Repeater 控件为每个名称重复一个 label 控件。注意数据绑定表达式EvalBind的使用。Eval用于单向绑定(只读)Bind用于双向绑定(读取和更新)

表 12-3 。提供的数据源 .NET 框架

数据源使用
设计器使您能够绑定到基于实体数据模型的数据。
控件使您能够使用 Microsoft SQL Server、OleDb、ODBC 或 Oracle 数据库
对象数据源使您能够使用业务对象或其他类
数据源控件使您能够使用语言集成查询
数据源控件使您能够使用 XML 文件。
站点地图用于 ASP.NET 网站导航。
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
            ConnectionString="<%$ ConnectionStrings:pubsConnectionString %>"
            SelectCommand="SELECT [au_id], [au_lname] FROM [authors]">
</asp:SqlDataSource>

<asp:Repeater ID="Repeater1" runat="server" DataSourceID="SqlDataSource1">
    <ItemTemplate>
        <asp:Label ID="lblName" runat="server" Text='<%# Eval("au_lname") %>'></asp:Label>
        <br />
    </ItemTemplate>
</asp:Repeater>

数据源控件便于快速应用开发。它们允许您在编写最少代码的同时创建支持 CRUD(创建读取更新删除)操作的网页。

模型绑定

虽然 DataSource 控件对于简单的 CRUD 操作和简单的应用来说工作得很好,但是随着应用逻辑和数据访问逻辑变得越来越复杂,使用起来就变得不灵活和麻烦了。它们将数据检索逻辑与表示逻辑混合在一起,这违反了将数据访问逻辑、业务逻辑和表示逻辑分成不同层的 n 层开发原则。为了克服这些限制,ASP.NET 包含了 Web 表单模型绑定。

模型绑定是一种更注重代码的数据访问逻辑,用于数据绑定控件。使用模型绑定,您可以使用ItemType属性声明绑定控件的数据类型。当设置控件的ItemType属性时,ItemBindItem表达式用于将控件绑定到数据源项。Item表达式实现单向绑定(只读),而BindItem表达式用于双向绑定(读取和更新)。下面的代码显示了如何使用一个 repeater 控件来承载一个 label 控件,并将其Text属性绑定到Employee.Name属性。注意,Repeater 控件的ItemType属性被设置为Employee类。

<asp:Repeater ID="Repeater1" runat="server"
            ItemType="Chapter12Demo1.Employee" SelectMethod="GetEmployee">
    <ItemTemplate>
        <asp:Label ID="Label1" runat="server" Text='<%# Item.Name %>'></asp:Label>
        <br />
    </ItemTemplate>
</asp:Repeater>

要将数据控件配置为使用模型绑定来选择数据,可以将控件的SelectMethod属性设置为页面代码中方法的名称。在上面的例子中,SelectMethod被设置为返回一列Employee对象的GetEmployee方法。数据控件在页面生命周期的适当时间调用该方法,并自动绑定返回的数据。

要实现双向绑定,需要包含一个方法来更新数据,并将其设置为等于控件的UpdateMethod属性。模型绑定会将更新后的值发送到 update 方法,在该方法中,值被更新回数据源。下面的代码定义了一个使用模型绑定来更新雇员的 GridView。注意使用了BindItemUpdateMethod来实现双向绑定。

<asp:GridView ID="employeesGrid" runat="server" DataKeyNames="ID"
      ItemType="Chapter12Demo1.Employee" AutoGenerateColumns="false" AutoGenerateEditButton="true"
      SelectMethod="GetEmployees" UpdateMethod="UpdateEmployee">
    <Columns>
        <asp:TemplateField>
            <ItemTemplate>
                <asp:Label ID="lblID" runat="server" Text='<%# Item.ID %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField>
            <ItemTemplate>
                <asp:Label ID="lblName" runat="server" Text='<%# BindItem.Name %>'></asp:Label>
            </ItemTemplate>
            <EditItemTemplate>
                <asp:TextBox ID="txtName" runat="server" Text='<%# BindItem.Name %>'></asp:TextBox>
            </EditItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

image 注意针对 ASP.NET 4.5 的新开发,微软推荐使用模型绑定。对于面向以前版本的应用,建议使用数据源控件。

活动 12-3。绑定 GRIDVIEW 控件

在本活动中,您将熟悉以下内容:

  • 如何使用 DataSource 控件实现到 Repeater 控件的单向绑定
  • 如何使用模型绑定实现 GridView 控件的双向绑定

在 ASP.NET 中继器控件中显示数据

要在 ASP.NET 中继器控件中显示数据,遵循以下步骤:

  1. 启动 Visual Studio。在文件菜单上,选择新建image网站。

  2. 选择一个 ASP.NET 空网站。在位置路径练习 12_3 的末尾,更改网站的名称。

  3. 向站点添加一个新的 Web 表单,并将其命名为 AuthorListPage。

  4. Open the AuthorListPage in Split view. Drag a SqlDataSource control located under the Data node to the form. You should see a SqlDataSource Tasks pane as in figure 12-16.

    9781430249351_Fig12-16.jpg

    图 12-16 。“SqlDataSource”任务窗格

  5. 单击配置数据源链接。这将启动一个向导来帮助配置 DataSource 控件。

  6. Create a new connection to your SQL Server using the .NET Framework Data Provider for SQL Server (see Figure 12-17).

    9781430249351_Fig12-17.jpg

    图 12-17 。选择 SQL Server 数据源

  7. 在添加连接对话框中,输入您的服务器名并选择 Pubs 数据库。测试连接,然后单击 OK 按钮。

  8. 在下一个屏幕中,选中 yes 将连接字符串保存在配置文件中。

  9. In the Configure the Select Statement screen select the au_id and au_lname columns from the authors table as in Figure 12-18.

    9781430249351_Fig12-18.jpg

    图 12-18 。配置 Select 语句

  10. 在下一个屏幕中,您可以测试查询。测试完查询后,单击 Finish 按钮。

  11. In the page source you should see the mark up added to the page to set up the SqlDataSource control.

< ASP:sqldata source ID = " sqldata source 1 " runat = " server "
ConnectionString=" "

SelectCommand="SELECT [au_id],[au_lname] FROM [authors]" >

12. 在 AuthorListPage 的设计视图中,将 Repeater 控件从工具箱中的数据节点拖到窗体上。将其放在 SqlDataSource 控件之后。 13. 在 Repeater Tasks 弹出窗口中,选择 SQLDataSource1 控件。 14. Switch to the Source window and add the ItemTemplate mark up and the asp:Label mark up inside the Repeater control mark up. Notice the Text property of the label is bound to the au_lname column using the Eval method.

`<asp:Repeater ID="Repeater1" runat="server" DataSourceID="SqlDataSource1">`

`<ItemTemplate>`

`<asp:Label ID="lblName" runat="server" Text='<%# Eval("au_lname") %>'>        </asp:Label>`

`<br />`

`</ItemTemplate>`

`</asp:Repeater>`

15. 在解决方案资源管理器窗口中右击 AutherListPage.aspx 节点,并选择“设为起始页”。启动调试器。您应该会看到显示有作者姓氏列表的页面。 16. 完成测试后,停止调试器。

使用 ASP.NET GridView 控件显示数据

要使用 ASP.NET GridView 控件显示和更新数据,请遵循以下步骤:

  1. 在 Solution Explore 窗口中,右键单击项目节点并选择 Add image Add New Item。添加一个 ADO.NET 实体数据模型,并将其命名为 Pubs.edmx。您将收到一条关于将其添加到 App_Code 文件夹的消息。选择是。

  2. 在实体数据模型向导中,选择从数据库生成。

  3. 在 Chose Your Data Connection 中,您可以重用之前创建的连接,也可以创建一个到 Pubs 数据库的新连接。确保在 Web 中保存实体连接设置。Config,并将其命名为 pubsEntities。

  4. In the Choose Your Database Objects and Settings window, select the employee table. Leave the default check boxes checked and name the model pubsModel (see Figure 12-19).

    9781430249351_Fig12-19.jpg

    图 12-19 。选择数据对象

  5. 完成向导后,您应该在实体设计器中看到 employee 实体。关闭实体设计器,并向设计器添加一个新的 Web 窗体。将页面命名为 EmployeeGridViewPage.aspx。

  6. Add a GridView control to the form and add the mark up below. Notice you are setting the ItemType to the employee entity and the SelectMethod to GetEmployees.

    <asp:GridView ID="grdEmployee" runat="server"

    ItemType="employee" SelectMethod="GetEmployees"

    AutoGenerateColumns="false">

    <Columns>

    <asp:BoundField DataField="emp_id" HeaderText="ID" ReadOnly="true" />

    <asp:BoundField DataField="fname" HeaderText="First Name" />

    <asp:BoundField DataField="lname" HeaderText="Last Name" />

    </Columns>

    </asp:GridView>

  7. In the code behind file for the page (EmployeeGridViewPage.aspx.cs) add the following code to retrieve the employee data using the employee entity.

    public partial class EmployeeGridViewPage : System.Web.UI.Page

    {

    pubsEntities dbContext = new pubsEntities();

    protected void Page_Load(object sender, EventArgs e)

    {

    }

    public IQueryable<employee> GetEmployees()

    {

    var Employees = dbContext.employees;

    return Employees;

    }

    }

  8. 在解决方案资源管理器窗口中右击 EmployeesGridViewPage.aspx 节点,并选择“设为起始页”。启动调试器。您应该会看到显示有员工信息表的页面。

  9. 完成测试后,停止调试器。

使用 ASP.NET GridView 控件更新数据

  1. Update the GridView control’s mark up to allow updating and set the UpdateMethod attribute to the UpdateEmployee method. The DataKeyNames attribute is set to the unique identifier of the employee entity.

    <asp:GridView ID="grdEmployee" runat="server"

    ModelType="employee" SelectMethod="GetEmployees" DataKeyNames="emp_id"

    AutoGenerateEditButton="true" UpdateMethod="UpdateEmployee"

    AutoGenerateColumns="false">

    <Columns>

    <asp:BoundField DataField="emp_id" HeaderText="ID" ReadOnly="true" />

    <asp:BoundField DataField="fname" HeaderText="First Name" />

    <asp:BoundField DataField="lname" HeaderText="Last Name" />

    </Columns>

    </asp:GridView>

  2. In the code behind file for the page (EmployeeGridViewPage.aspx.cs) add the following code to update the employee data using the employee entity. This code looks up the old values held in the entity and tries to update the model. If it is successful it saves the changes back to the database.

    public void UpdateEmployee(employee Employee)

    {

    var Employee2 = dbContext.employees.Find(Employee.emp_id);

    TryUpdateModel(Employee2);

    if (ModelState.IsValid)

    {

    dbContext.SaveChanges();

    }

    }

  3. By default Web Forms have UnobtrusiveValidationMode turned on. To implement this you need to add a ScriptManager and some jScript include files. For now you can just turn it off by adding the following to the Page_Load event handler method.

    this.UnobtrusiveValidationMode = System.Web.UI.UnobtrusiveValidationMode.None;

  4. 启动调试器。您应该看到显示了一个雇员信息表和一个编辑按钮的页面。单击某行的编辑按钮。这会将网格设置为编辑模式。

  5. 更新名字,然后单击“更新”按钮。网格将刷新并显示新值。

摘要

在这一章中,你学习了如何使用基于 Web 表单的前端来实现应用的界面层。在这个过程中,您仔细研究了 .NET 框架,用于实现基于 Web 窗体的用户界面。您还接触了数据绑定 Web 服务器控件,尤其是 GridView 控件。我在这一章的目的是向你展示一些用于开发以数据为中心的 web 应用的技术。要成为一名熟练的 web 开发人员,您需要深入研究这些主题和其他主题,如使用母版页、客户端脚本、使用样式表和 web 安全性。

在第十三章中,你将看到 Windows 8 引入的一种新型应用 Windows Store 应用。Windows 应用商店应用是传统客户端应用和网络应用的混合体。界面使用 HTML 或 XAML 开发,应用逻辑可以用 C#、Visual Basic 或 JavaScript 开发。它们支持越来越流行的触摸屏设备,代表着用户与应用交互方式的转变。

十三、开发 Windows 应用商店应用

在前面的章节中,您学习了如何基于传统的 Windows 客户端(ASP.NET)用户界面和基于 web 的用户界面构建一个以数据为中心的应用。在本章中,您将了解如何使用新的 Windows 应用商店应用构建用户界面。Windows 应用商店应用旨在 Windows 8 设备上运行。Windows 应用商店应用拥有全新的外观,旨在动态支持不同的显示尺寸和设备。随着平板电脑和手机日益成为最受欢迎的信息交互设备,企业用户要求能够使用这些设备与他们的信息存储进行交互。幸运的是,微软提供了各种应用编程接口(API),可以用来创建在这些设备上运行的应用。他们暴露了一个被管理的 .NET 框架,它允许你使用 C# 来编写代码,使用 XAML 框架来开发用户界面。开发这些应用是开发 WPF 应用和 web 应用的结合。

阅读完本章后,您将能够轻松执行以下任务:

  • 使用 XAML 标记设计用户界面
  • 使用布局控件
  • 使用显示控件
  • 响应控制事件
  • 使用数据绑定控件

构建用户界面

构建 Windows 应用商店应用的用户界面与开发 WPF 应用非常相似。如果你还记得《??》第十一章,应用的视觉界面包含了对象。与面向对象语言中的大多数对象一样,这些对象公开属性、方法和事件。在 Windows 应用商店应用中,主要对象是页面。一个页面有属性(例如Background属性)、方法(例如focus方法)和事件(例如DoubleTapped事件)。

控件是具有可视化界面的组件,为用户提供了与程序交互的方式。页面是一个容器控件,它承载其他控件。您可以在页面上放置许多不同类型的控件。页面上使用的一些常见控件有文本框、文本块、按钮、列表视图和 GridViews。

就像 WPF 一样,Windows 应用商店用户界面是使用声明性标记语言 XAML 构建的。例如,下面的标记定义了一个Grid中的按钮控件。

<Grid Background="#E5951D1D">
     <Button Content="Button" HorizontalAlignment="Left" Margin="56,124,0,0"
             VerticalAlignment="Top" Background="#FF2457A0"/>

</Grid>

注意Grid需要一个正式的结束标记,因为它包含了Button控件。由于Button控件不包含任何其他控件,您可以在结束括号前使用正斜杠(/)来关闭它。请注意,控件的属性是使用属性语法设置的。

使用网格行和列来绝对或相对定位控件。注意上面的代码使用了按钮在网格中的绝对位置,这是通过使用Margin属性实现的。下面的代码显示了使用网格行和列相对定位的按钮。

<Grid Background="#E5951D1D">
        <Grid.RowDefinitions>
            <RowDefinition Height="140"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width ="140"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <Button Content="Button" HorizontalAlignment="Left"
                Grid.Column="1" Grid.Row="1"
                VerticalAlignment="Top" Background="#FF2457A0"
                Height="78" Width="162"/>
    </Grid>

图 13-1 显示了带有由之前的 XAML 代码创建的按钮的页面。

9781430249351_Fig13-01.jpg

图 13-1 。用 XAML 创造的窗户

如前所述,Grid 控件包含列和行来控制其子控件的位置。列和行的高度和宽度可以设置为固定值、auto 或*。自动设置占用所包含控件所需的空间。*设置占用尽可能多的可用空间。下面的代码展示了一个用于收集用户信息的简单数据输入表单。生成的表格如图 13-2 中的所示。

<Grid Background="#E5B1BDB9">

            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
         <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="200" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Row="0" Grid.Column="0" Text="Name:"
                   Foreground="Black" VerticalAlignment="Center"
                   Margin="10" FontSize="16"/>
            <TextBlock Grid.Row="1" Grid.Column="0" Text="City:"
                   Foreground="Black" VerticalAlignment="Center"
                   Margin="10" FontSize="16"/>
            <TextBlock Grid.Row="2" Grid.Column="0" Text="State:"
                   Foreground="Black" VerticalAlignment="Center"
                   Margin="10" FontSize="16" />

            <TextBox Grid.Column="1" Grid.Row="0" Margin="3" />
            <TextBox Grid.Column="1" Grid.Row="1" Margin="3" />
            <TextBox Grid.Column="1" Grid.Row="2" Margin="3" />
            <Button Grid.Column="1" Grid.Row="4" HorizontalAlignment="Right"
                    MinWidth="80" MinHeight="50" Margin="0,0,0,8" Content="Submit"
                    Foreground="Black"/>
    </Grid>

9781430249351_Fig13-02.jpg

图 13-2 。输入表单页面

其他可用的布局控件有StackPanelWrapGridCanvas。下面的代码显示了StackPanel控件中的两个按钮:

<StackPanel Grid.Column="1" Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="right">
        <Button MinWidth="80" MinHeight="50" Margin="0,0,0,8" Content="Submit"
                Foreground="Black"/>
        <Button MinWidth="80" MinHeight="50" Margin="0,0,0,8" Content="Cancel"
                Foreground="Black"/>
</StackPanel>

使用样式表

开发 Windows 应用商店应用的一个重要方面是在页面和应用之间保持一致的外观。界面需要在大显示器和小显示器之间缩放,并平滑地处理方向的变化。这些应用的创建和设计可能是一个耗时、乏味的过程。幸运的是,您可以在应用的所有页面和控件中使用预先构建的样式。这些样式包含在 XAML 文件中,并由使用静态资源标记扩展的控件引用。下面的代码使用静态资源来设置两个按钮控件的样式。

<Button Content="Submit" Style="{StaticResource TextPrimaryButtonStyle}"  />
<Button  Content="Cancel" Style="{StaticResource TextSecondaryButtonStyle}"/>

下列定义样式的标记位于 Common 文件夹中的 StandardStyles.xaml 文件中。请注意,您可以在样式定义中引用样式。

<Style x:Key="TextPrimaryButtonStyle" TargetType="ButtonBase"
        BasedOn="{StaticResource TextButtonStyle}">
        <Setter Property="Foreground"
                   Value="{StaticResource ApplicationHeaderForegroundThemeBrush}"/>
</Style>

处理控制事件

就像 ASP.NET 和 WPF 应用一样,Windows 应用商店应用通过事件与用户互动。不同之处在于事件是如何启动的。虽然 ASP.NET 和 WPF 的应用主要由鼠标和键盘事件驱动,但 Windows Store 应用主要由触摸交互驱动(仍然支持键盘和鼠标事件)。一些常见的交互动作包括点击、按住、滑动和滑动。

在 Visual Studio 中,可以通过编写 XAML 代码或在控件的“属性”窗口中选择事件来将事件添加到控件中。图 13-3 显示了在 XAML 编辑器窗口中连接一个事件处理器;图 13-4 显示了使用属性窗口的事件选项卡连接事件处理程序。请记住,在代码中使用控件时,您需要使用 name 属性为它们指定一个唯一的名称。

9781430249351_Fig13-03.jpg

图 13-3 。在 XAML 编辑器中连接事件处理程序

9781430249351_Fig13-04.jpg

图 13-4 。在“属性”窗口中连接事件处理程序

无论如何连接事件处理程序,Visual Studio 代码编辑器都会在代码隐藏文件中插入一个空的事件处理程序方法。以下代码显示了为按钮点击事件插入的事件处理程序方法:

private void btnSubmit_Tapped(object sender, TappedRoutedEventArgs e)
{
}

就像在 WPF 中一样,当事件被触发时,两个参数被传递给方法。第一个参数是发送方,它表示启动事件的对象。第二个参数,在本例中是类型Windows.UI.Xaml.Input.RoutedEventArgs,是一个用于传递特定事件特定信息的对象。下面的代码显示检查他们是否用笔点击。

private void btnSubmit_Tapped(object sender, TappedRoutedEventArgs e)
{
    if (e.PointerDeviceType == PointerDeviceType.Pen)
    {
         //additional code. . .
    }
}

在以下活动中,您将研究如何在简单的 Windows 应用商店应用中使用控件。

活动 13-1。使用控件

在本活动中,您将熟悉以下内容:

  • 创建 Windows 应用商店应用
  • 设计页面
  • 使用控制事件

创建 Windows 应用商店应用界面

要创建应用界面,请遵循以下步骤:

  1. 启动 Visual Studio。选择文件image新建image项目。

  2. 单击 Visual C# 模板节点下的 Windows 应用商店节点。选择空白应用模板。将名称更改为 Activity13_1,然后单击“确定”按钮。

  3. Open the MainPage.xaml in the design window. Notice it is using the default dark theme. To change this to the light theme, open the App.xaml page. Add the RequestedTheme attribute to the Application mark up.

    <Application

    x:Class="Activity13_1.App"

    xmlns="``http://schemas.microsoft.com/winfx/2006/xaml/presentation

    xmlns:x="``http://schemas.microsoft.com/winfx/2006/xaml

    xmlns:local="using:Activity13_1"

    RequestedTheme="Light">

  4. 关闭并重新打开 MainPage.xaml 文件。你现在应该看到一个白色的背景。

  5. Update the Grid XAML to define two columns and 5 rows.      

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">

    <Grid.ColumnDefinitions>

    <ColumnDefinition Width="300"/>

    <ColumnDefinition Width="Auto"/>

    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>

    <RowDefinition Height="Auto"/>

    <RowDefinition Height="Auto"/>

    <RowDefinition Height="Auto"/>

    <RowDefinition Height="Auto"/>

    <RowDefinition Height="Auto"/>

    </Grid.RowDefinitions>

    </Grid>

    </Grid>

  6. Add the following XAML after the closing Grid.RowDefinitions tag to put two text boxes on the page. One accepts a single line of text and one accepts multiple lines of text.

    <TextBlock Grid.Row="0" Grid.Column="0" Margin="10"

    Style="{StaticResource BasicTextStyle}">

    Single line text input

    </TextBlock>

    <TextBox Grid.Row="0" Grid.Column="1" Margin="10"

    Width="200" HorizontalAlignment="Left"/>

    <TextBlock Grid.Row="1" Grid.Column="0" Margin="10"

    Style="{StaticResource BasicTextStyle}">

    Multi-line input

    </TextBlock>

    <TextBox Grid.Row="1" Grid.Column="1" Margin="10"

    Width="200" Height ="100" TextWrapping="Wrap"

    AcceptsReturn="True" HorizontalAlignment="Left"/>

  7. Add a password entry box by inserting the following code.

    <TextBlock Grid.Row="2" Grid.Column="0" Margin="10"

    Style="{StaticResource BasicTextStyle}">

    Password entry

    </TextBlock>

    <PasswordBox Grid.Row="2" Grid.Column="1" Margin="10"

    Width="200" HorizontalAlignment="Left"/>

  8. Add two more TextBox controls, one for numeric input and one for email address entry.

    <TextBlock Grid.Row="3" Grid.Column="0" Margin="10"

    Style="{StaticResource BasicTextStyle}">

    Number input

    </TextBlock>

    <TextBox Grid.Row="3" Grid.Column="1" Margin="10"

    Width="200" InputScope="Number"  HorizontalAlignment="Left"/>

    <TextBlock Grid.Row="4" Grid.Column="0" Margin="10"

    Style="{StaticResource BasicTextStyle}" TextWrapping="Wrap">

    Email address

    </TextBlock>

    <TextBox Grid.Row="4" Grid.Column="1" Margin="10"

    Width="200" InputScope="EmailSmtpAddress"  />

  9. Note that as you add the XAML, the Visual Designer updates the appearance of the window. The page should look similar to the one shown in Figure 13-5.

    9781430249351_Fig13-05.jpg

    图 13-5 。数据输入页面

  10. 构建解决方案。如果有任何错误,请修复并重新构建。

  11. To test the page, change the debugger target to the Simulator in the toolbar (See Figure 13-6) and start the debugger.

![9781430249351_Fig13-06.jpg](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/516c6b3a633148418acef0b7c9d3cd37~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1771068300&x-signature=pL6aSagXZiR6Hf385g6hgk9a4oE%3D)
图 13-6 。启动模拟器

12. When the Simulator launches, change the mode to basic touch (See Figure 13-7). In this mode, the mouse is used to simulate a finger press.

![9781430249351_Fig13-07.jpg](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/2962b29626844d1cbe4cab62a3e04440~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1771068300&x-signature=iZpskjkcFPDV3HNUJMWn4Y%2BS%2Fg4%3D)

图 13-7 。改变互动模式

13. 当您在单行输入文本框中按下时,会显示软键盘。输入一些文本并尝试按回车键。您应该不能添加额外的行。测试多行输入和密码输入。 14. 在数字输入框中按。请注意,键盘显示数字键盘。它知道要这样做,因为您向控件添加了InputScope属性。 15. 在电子邮件地址输入框中按会添加“@”和“.”。com "键到键盘上。 16. 完成测试后,您可以通过按 Ctrl + Alt + F4 键来关闭模拟器。

编码控制事件

要对控制事件进行编码,请遵循以下步骤:

  1. In the XAML Editor window for the MainPage.xaml file in the Grid.RowDefinitions section add a new row to the grid. In the new row, place two button controls in a stack panel using the code below.

    <StackPanel Grid.Row="5" Grid.Column="1" Orientation="Horizontal">

    <Button Name ="btnSave" Content ="Save"  Margin="10"

    Tapped="btnSave_Tapped"/>

    <Button Name="btnCancel" Content ="Cancel"  Margin="10"

    Tapped="btnCancel_Tapped"/>

    </StackPanel>

  2. After the closing StackPanel tag, add the following markup to display a progress ring on the page.

    <ProgressRing x:Name="SaveProgress" Grid.Row="5" Grid.Column="0"

    IsActive="False" Width="113" Height="80"/>

  3. 定义上述按钮的 XAML 包括一个用于点击事件的事件处理方法。右键单击 btnSave 的Tapped事件,并在弹出菜单中选择“导航到事件处理程序”。

  4. In the Code Editor window of the codebehind file, add the following code to the btnSave_Tapped event handler method. This code makes the progress ring show on the page.

    this.SaveProgress.IsActive = true;

  5. In the btnCancel_Tapped method, deactivate the progress ring.

    this.SaveProgress.IsActive = false;

  6. 生成解决方案并修复任何错误。

  7. 使用模拟器测试按钮的点击事件。

  8. 测试完应用后,按 Ctrl + Alt + F4 键关闭模拟器并停止调试。

数据绑定控件

将 Windows Store 应用控件绑定到数据的方式非常类似于在 WPF 的处理方式(在第十章中介绍)。当使用 XAML 进行绑定时,使用每个控件可用的Binding属性。当在代码中绑定控件时,用DataContext属性设置其源。当为父元素(如 Grid 控件)设置 DataContext 时,子元素将使用相同的 DataContext,除非显式设置了它们的 DataContext。

那个 .NET Framework 封装了通过数据绑定过程将控件与数据源同步的大部分复杂性。Mode属性决定了数据绑定如何流动以及如何对数据变化做出反应。一次性绑定在创建绑定时用来自源的数据更新目标。单向绑定会导致对源属性的更改自动更新目标属性,但对目标属性的更改不会传播回源属性。这对于只读情况很有用,并且是默认绑定。双向绑定会导致源属性或目标属性的更改自动更新另一个属性。这对于完全数据更新的情况很有用。

下面的代码显示了设置为包含作者列表的CollectionViewSourceGrid控件的DataContextCollectionViewSource允许您在作者列表中移动。

cvs = new CollectionViewSource();
cvs.Source = authorList;
this.AuthorList.DataContext = cvs;
cvs.View.MoveCurrentToFirst();

下面的 XAML 代码使用Path属性将包含在Grid控件中的TextBox控件和CheckBox控件绑定到Authors类的属性。使用Binding来指定源数据意味着“查找容器层次结构,直到找到DataContext”在这种情况下,DataContext将是为Grid容器指定的。

<TextBox Grid.Column="1" Grid.Row="2" Height="23" HorizontalAlignment="Left"
                Margin="3" Name="txtLastName" Text="{Binding Path=LastName}"
                VerticalAlignment="Center"  Width="120" />
<CheckBox Name="chkContract" Content="Under Contract"
                IsChecked="{Binding Path=UnderContract}"
                Grid.Row="4" Grid.ColumnSpan="2" FlowDirection="RightToLeft" />

图 13-8 显示了一个包含绑定到作者数据的控件的页面。

9781430249351_Fig13-08.jpg

图 13-8 。显示作者数据的页面

要浏览记录,只需使用CollectionViewSource View方法。下面的代码显示了如何移动到下一条记录。

cvs.View.MoveCurrentToNext();

虽然有些控件一次只能绑定到一个记录,但其他控件(如GridView控件)可以绑定并显示整个集合。下面的代码将GridViewItemSource设置为作者列表。在这种情况下,没有必要使用CollectionViewSource

this.AuthorDataGrid.ItemsSource = authorList;

下面的 XAML 创建了GridView并绑定了视图中包含的控件。结果页面如图 13-9 中的所示。

<GridView Grid.Row="0" Grid.Column="2" x:Name="AuthorGridView"
                  Header="Authors" FontSize="14" FontWeight="Bold" HorizontalAlignment="Center" >
    <GridView.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                 <TextBlock Text="{Binding FirstName}"
                           Style="{StaticResource ItemTextStyle}" Margin="10"/>
                 <TextBlock Text="{Binding LastName}" TextWrapping="NoWrap"
                           Style="{StaticResource BodyTextStyle}" Margin="10"/>
                 <CheckBox Content="UnderContract" IsChecked="{Binding UnderContract}"
                          IsEnabled="False" Margin="10" FontSize="10"/>
                        </StackPanel>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

9781430249351_Fig13-09.jpg

图 13-9 。在 GridView 中显示作者数据的页面

在以下活动中,您将构建一个页面,其中包含绑定到一组Author对象的控件。

活动 13-2。使用数据绑定控件

在本活动中,您将熟悉以下内容:

  • 将控件绑定到集合
  • 研究绑定模式

将控件绑定到集合

要将控件绑定到集合,请遵循以下步骤:

  1. 启动 Visual Studio。选择文件image新建image项目。

  2. 选择一个 Windows 应用商店应用,然后选择空白应用模板。将项目重命名为 Activity13_2,然后单击“确定”按钮。

  3. 在解决方案资源管理器中右键单击项目节点,然后选择 Add image Class。给类命名Author

  4. At the top of the class file, add the following using statement:

    using System.ComponentModel;

  5. In the Author class, implement the INotifyPropertyChanged interface. This is needed to facilitate binding.

    class Author : INotifyPropertyChanged

    {

    public event PropertyChangedEventHandler PropertyChanged;

    void RaisePropertyChanged(string propertyName)

    {

    var handler = PropertyChanged;

    if (handler != null)

    {

    handler(this, new PropertyChangedEventArgs(propertyName));

    }

    }

    }

  6. Add the following properties. Note that when the values are changed, the PropertyChanged event is raised.

    string _firstName;

    public string FirstName

    {

    get { return _firstName; }

    set

    {

    if (_firstName != value)

    {

    _firstName = value;

    RaisePropertyChanged("FirstName");

    }

    }

    }

    string _lastName;

    public string LastName

    {

    get { return _lastName; }

    set

    {

    if (_lastName != value)

    {

    _lastName = value;

    RaisePropertyChanged("LastName");

    }

    }

    }

    Boolean _underContract;

    public Boolean UnderContract

    {

    get { return _underContract; }

    set

    {

    if (_underContract != value)

    {

    _underContract = value;

    RaisePropertyChanged("UnderContract");

    }

    }

    }

    double _royalty;

    public double Royalty

    {

    get { return _royalty; }

    set

    {

    if (_royalty != value)

    {

    _royalty = value;

    RaisePropertyChanged("Royalty");

    }

    }

    }

  7. Add the following constructor to the Author class:

    public Author(string firstName, string lastName,

    Boolean underContract, double royalty)

    {

    this.FirstName = firstName;

    this.LastName = lastName;

    this.UnderContract = underContract;

    this.Royalty = royalty;

    }

  8. 构建项目并确保没有错误。如果有,修复它们并重新构建。

  9. Open the App.xaml page and add the RequestedTheme attribute to the Application mark up.

    <Application

    x:Class="Activity13_2.App"

    xmlns="``http://schemas.microsoft.com/winfx/2006/xaml/presentation

    xmlns:x="``http://schemas.microsoft.com/winfx/2006/xaml

    xmlns:local="using:Activity13_2"

    RequestedTheme="Light">

  10. Add the following XAML markup to the MainPage.xaml file to create the page header:

`<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">`
`<Grid.RowDefinitions>`

`<RowDefinition Height="140"/>`

`<RowDefinition Height="*"/>`

`</Grid.RowDefinitions>`

`<Grid>`

`<Grid.ColumnDefinitions>`

`<ColumnDefinition Width="Auto"/>`

`<ColumnDefinition Width="*"/>`

`</Grid.ColumnDefinitions>`

`<TextBlock x:Name="PageTitle"`

`Text="Authors" Grid.Column="1"  VerticalAlignment="Center"`

`Margin="120,20,0,0" IsHitTestVisible="false"`

`Style="{StaticResource PageHeaderTextStyle}"/>`

`</Grid>`

`<!-- Add the GridView here -->`

`</Grid>`

11. You can define the DataTemplate under the ResourceDictionary of the Page.Resources section of the page. Add the following XAML markup to define the DataTemplate for the author information contained in the GridView (Add this above the first Grid tag).

`<Page.Resources>`

`<ResourceDictionary>`

`<DataTemplate x:Key="AuthorTemplate">`

`<Grid Background="LightGray"`

`Width="300"`

`Height="200">`

`<Grid.RowDefinitions>`

`<RowDefinition Height="75"/>`

`<RowDefinition Height="Auto"/>`

`<RowDefinition Height="Auto"/>`

`</Grid.RowDefinitions>`

`<StackPanel Orientation="Horizontal" Grid.Row="0">`

`<TextBlock Text="{Binding LastName}"`

`Margin="20,10,0,0"`

`FontSize="24"`

`FontWeight="SemiBold"/>`

`<TextBlock Text="{Binding FirstName}"`

`Margin="20,10,0,0"`

`FontSize="24"/>`

`</StackPanel>`

`<StackPanel Orientation="Horizontal" Grid.Row="1">`

`<TextBlock Text="Royalty: "`

`Margin="20,0,0,0"`

`FontSize="18"/>`

`<TextBlock Text="{Binding Royalty}"`

`Margin="20,0,0,0"`

`FontSize="18"/>`

`</StackPanel>`

`<StackPanel Orientation="Horizontal" Grid.Row="2">`

`<TextBlock Text="Under Contract:"`

`Margin="20,0,0,0"`

`FontSize="18"/>`

`<TextBlock Text="{Binding UnderContract}"`

`Margin="20,0,0,0"`

`FontSize="18"/>`

`</StackPanel>`

`</Grid>`

`</DataTemplate>`

`</ResourceDictionary>`

`</Page.Resources>`

12. Add the GridView to the page to display the author info. Notice the ItemTemplate is set to the AuthorTemplate you defined in step 11.

`<GridView x:Name="AuthorsGridView"`

`Grid.Row="1"`

`Margin="110,50,0,0"`

`SelectionMode="Single"`

`IsSwipeEnabled="True"`

`IsItemClickEnabled="True"`

`ItemsSource="{Binding}"`

`ItemTemplate="{StaticResource AuthorTemplate}">`

`<GridView.ItemsPanel>`

`<ItemsPanelTemplate>`

`<WrapGrid Orientation="Horizontal" />`

`</ItemsPanelTemplate>`

`</GridView.ItemsPanel>`

`</GridView>`

13. In the MainPage.xaml.cs code file, add a class level variable of Type List of Author.

`public sealed partial class MainPage : Page`

`{`

`List<Author> authors;`

14. Add the following method to load the list.

`private void PopulateAuthors()`

`{`

`authors = new List272103_1_En();`

`authors.Add(new Author("Clive", "Cussler", true, .15));`

`authors.Add(new Author("Steve", "Berry", false, .20));`

`authors.Add(new Author("Kate", "Morton", false, .20));`

`authors.Add(new Author("Karma", "Wilson", true, .18));`

`}`

15. In the onNavigatedTo event handler method, call the PopulateAuthors method and set the authors list to the GridView’s ItemSource.

`protected override void OnNavigatedTo(NavigationEventArgs e)`

`{`

`PopulateAuthors();`

`this.AuthorsGridView.ItemsSource = authors;`

`}`

16. To test the page, start the debugger with the Simulator as the target. You should see a page similar to Figure 13-10.

![9781430249351_Fig13-10.jpg](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/76579cbd9619483d8694a6a80fece83b~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1771068300&x-signature=nCEI0zUG6qLw1ztuaLfPFH%2FLpMM%3D)

图 13-10 。GridView 中的作者信息

17. 测试完页面后,按 Ctrl +Alt + F4 键关闭模拟器并停止调试。

研究绑定模式

  1. Add another row to the main Grid on the MainPage.

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">

    <Grid.RowDefinitions>

    <RowDefinition Height="140"/>

    <RowDefinition Height="Auto"/>

    <RowDefinition Height="Auto"/>

    </Grid.RowDefinitions>

  2. Below the GridView closing tag, add XAML to create a StackPanel with two buttons in it.

    <StackPanel Orientation="Horizontal" Grid.Row="2" VerticalAlignment="Top">

    <Button  Name="btnUpdate" Click="btnUpdate_Click" Content="Update" />

    <Button  Name="btnRefresh" Click="btnRefresh_Click" Content="Refresh" />

    </StackPanel>

  3. 上面定义btnUpdate按钮的 XAML 包括一个用于Click事件的事件处理程序方法。右键单击Click事件,并在弹出菜单中选择“导航到事件处理程序”。

  4. Add the following code to the Click event handler to update the first author’s last name.

    private void btnUpdate_Click(object sender, RoutedEventArgs e)

    {

    authors[0].LastName = "Webb";

    }

  5. Add the following code to the btnRefresh_Click method. This code clears and rebinds the GridView.

    private void btnRefresh_Click(object sender, RoutedEventArgs e)

    {

    this.AuthorsGridView.ItemsSource = null;

    this.AuthorsGridView.ItemsSource = authors;

    }

  6. 在模拟器中测试页面。当您单击更新按钮时,第一个作者的名字应该会在页面上更新。这是因为绑定模式默认设置为OneWay

  7. Change the binding mode of the txtFirstName TextBlock to OneTime.

    <TextBlock Text="{Binding FirstName, Mode=OneTime}"

    Margin="20,10,0,0"  FontSize="24"/>

  8. 在模拟器中测试页面。当您单击“更新”按钮时,第一作者的姓名不应在页面上更新。这是因为绑定模式被设置为OneTime,,这意味着它只在绑定时才会改变。点击刷新按钮重新绑定DataGrid。您现在应该在GridView中看到新的值。

  9. 完成测试后,关闭模拟器并退出 Visual Studio。

页面导航

Windows 应用商店应用中的页面导航在概念上类似于 web 应用中的页面导航。XAML UI 框架提供了一个内置的Frame控件来跟踪导航历史。您可以使用Frame对象的方法,例如GoBackGoForwardNavigate,轻松地浏览导航历史或跳转到特定页面。Navigation方法还提供了一个参数,允许您在页面之间传递数据。

App.xaml.cs 文件中的以下代码用于在应用启动时导航到应用的主页。如果Frame对象不能导航到MainPage,它抛出一个异常。

if (!rootFrame.Navigate(typeof(MainPage), args.Arguments))
{
    throw new Exception("Failed to create initial page");
}

下面的代码调用Navigate方法导航到一个细节页面,传递一个表示作者的对象,该作者显示在一个GridView项目(e.ClickedItem)中。

private void AuthorsGridView_ItemClick(object sender, ItemClickEventArgs e)
{
    Frame.Navigate(typeof(AuthorDetailPage), e.ClickedItem);
}

AuthorDetailPage 使用事件处理程序OnNavigateTo来检索传递的 author 对象,并将其绑定到AuthorDetails网格的DataContext

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    author = (Author) e.Parameter;
    this.AuthorDetails.DataContext = author;
}

活动 13-3。使用数据模板

在本活动中,您将熟悉以下内容:

  • 实现页面导航
  • 在页面之间传递数据

传递数据和页面导航

要实现页面导航,请按照下列步骤操作:

  1. 启动 Visual Studio。选择文件image打开image项目。

  2. 选择你在上一次活动中完成的活动 13_2 项目。

  3. 项目加载后,添加一个名为 AuthorDetail.xaml 的新空白页。

  4. Add the following markup code between the Grid tags in the AuthorDetailPage’s XAML file. This creates the page’s title and adds a back button to the page.

    <Grid.RowDefinitions>

    <RowDefinition Height="140"/>

    <RowDefinition Height="Auto"/>

    </Grid.RowDefinitions>

    <Grid>

    <Grid.ColumnDefinitions>

    <ColumnDefinition Width="Auto"/>

    <ColumnDefinition Width="*"/>

    </Grid.ColumnDefinitions>

    <Button Name="btnBack" Style="{StaticResource BackButtonStyle}"

    Click="btnBack_Click"></Button>

    <TextBlock x:Name="PageTitle"

    Text="Author Info" Grid.Column="1"  VerticalAlignment="Center"

    Margin="120,20,0,0" IsHitTestVisible="false"

    Style="{StaticResource PageHeaderTextStyle}"/>

    </Grid>

  5. Add this mark up after the TextBlock tag and before the ending Grid tag shown in step 4. This creates the display controls to display the author’s details.

    <Grid Grid.Row="1" Name="AuthorDetails" DataContext="{Binding Mode=TwoWay}"

    HorizontalAlignment="Left"  Margin="20,0,0,0">

    <Grid.ColumnDefinitions>

    <ColumnDefinition Width="Auto" />

    <ColumnDefinition Width="Auto" />

    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>

    <RowDefinition Height="Auto" />

    <RowDefinition Height="Auto" />

    <RowDefinition Height="Auto" />

    <RowDefinition Height="Auto" />

    <RowDefinition Height="Auto" />

    </Grid.RowDefinitions>

    <TextBlock Text="First Name:" Grid.Column="0"

    Grid.Row="0" HorizontalAlignment="Left"

    Margin="3" VerticalAlignment="Center" FontSize="18" />

    <TextBox Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Left"

    Margin="3" Name="txtFirstName" Text="{Binding Path=FirstName}"

    VerticalAlignment="Center" Width="120" FontSize="18" />

    <TextBlock Text="Last Name:" Grid.Column="0" Grid.Row="1"

    HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" FontSize="18" />

    <TextBox Grid.Column="1" Grid.Row="1" Height="23" HorizontalAlignment="Left"

    Margin="3" Name="txtLastName" Text="{Binding Path=LastName}" IsEnabled="False"

    VerticalAlignment="Center"  Width="120" FontSize="18" />

    <TextBlock Text="Royalty:" Grid.Column="0" Grid.Row="2"

    HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center"            FontSize="18" />

    <TextBox Grid.Column="1" Grid.Row="2" Height="23" HorizontalAlignment="Left"

    Margin="3" Name="txtRoyalty" Text="{Binding Path=Royalty}"

    VerticalAlignment="Center" Width="120" FontSize="18" />

    <CheckBox Name="chkContract" Content="Under Contract"

    IsChecked="{Binding Path=UnderContract}"

    Grid.Row="3" Grid.Column="0" FlowDirection="RightToLeft" FontSize="18" />

    </Grid>

  6. In the AuthorDetailPage.xaml.cs file, add the btnBack_Click event handler. This sends the navigation back to the calling page.

    private void btnBack_Click(object sender, RoutedEventArgs e)

    {

    Frame.GoBack();

    }

  7. In the MainPage.xaml file, find the AuthorsGridView opening tag and add an ItemClick attribute.

    <GridView x:Name="AuthorsGridView"

    Grid.Row="1"

    Margin="110,50,0,0"

    SelectionMode="Single"

    IsSwipeEnabled="True"

    IsItemClickEnabled="True"

    ItemsSource="{Binding Mode=OneTime}"

    ItemTemplate="{StaticResource AuthorTemplate}"

    ItemClick="AuthorsGridView_ItemClick">

  8. In the MainPage.xaml.cs file, add the AuthorsGridView_ItemClick event handler. This causes the application to navigate to the AuthorDetailPage.

    private void AuthorsGridView_ItemClick(object sender, ItemClickEventArgs e)

    {

    Frame.Navigate(typeof(AuthorDetailPage));

    }

  9. 在模拟器中测试导航。当MainPage显示时,点击其中一个显示作者信息的灰色方框。应该显示AuthorDetailPage。点击返回按钮,这将带您回到MainPage。测试后,关闭模拟器。

在页面间传递数据

  1. In the MainPage.xaml.cs file, update the AuthorsGridView_ItemClick event handler to pass the e.ClickedItem which represents an Author object to the AuthorDetailPage.

    private void AuthorsGridView_ItemClick(object sender, ItemClickEventArgs e)

    {

    框架。navigate(type of(author detail page),e . ClickedItem);

    }

  2. In the AuthorDetailPage.cs file, add the following code to the OnNavigatedTo event handler. This code takes the parameter passed to the page and assigns it to the DataContext of the AuthorDetails grid.

    protected override void OnNavigatedTo(NavigationEventArgs e)

    {

    作者 author = (Author) e .参数;

    这个。author details . data context = author;

    }

  3. 在模拟器中测试应用。当MainPage显示时,点击其中一个显示作者信息的灰色方框。AuthorDetailsPage应该显示加载到页面控件中的作者信息。测试后,关闭模拟器。

摘要

在本章中,您了解了如何使用新的 Windows 应用商店应用实现应用的界面层。您了解了如何使用 XAML 创建页面和布局控件。您还看到了将控件绑定到数据并呈现给用户是多么容易。这个故事还缺少关于如何从服务器上的关系数据库中检索数据的信息。为了向 Windows 应用商店应用提供服务器端数据,您需要利用 web 服务。在第十四章中,你将会看到使用 ASP.NET 通信框架(WCF)和新的 web API 来创建 Web 服务。您还将看到在客户机应用中使用 web 服务。

十四、开发和使用 Web 服务

在前三章中,您研究了创建应用的图形用户界面所需的步骤。使用 ASP.NET WPF 和 Windows 应用商店应用创建的图形用户界面为用户提供了一种与您的应用交互和使用应用提供的服务的方式。本章向您展示了如何构建另一种类型的接口,这种接口是使用 web 服务协议实现的,旨在供应用使用。这种服务为应用提供了一个可编程的接口来访问其功能,而不需要人工交互。您将首先看到创建和消费 Windows 通信基础(WCF) web 服务。WCF 服务是健壮的服务,支持多种协议上的安全消息传递。当您既要负责服务提供者又要负责服务消费者时,WCF 服务是一个很好的选择,例如,在公司的内部网上提供消费服务。您还将了解如何创建和使用 ASP.NET Web API 服务。ASP.NET Web API 提供了一个轻量级协议,用于通过 HTTP 在客户机和服务器之间传递数据。对于通过互联网在使用不同技术和/或不同组创建的客户端和服务器之间传递数据,这是一个更好的选择。

阅读完本章后,您将对以下内容有更清晰的理解:

  • 什么是 web 服务以及它们是如何产生的
  • WCF 如何处理服务请求
  • 如何创建和消费 WCF 服务
  • ASP.NET Web API 服务如何处理服务请求
  • 如何创建和使用 ASP.NET Web API 服务

什么是服务?

微软首次引入服务的概念是在 .NET 框架 1.0。web 服务为应用提供了一种请求服务和接收回复的方式。这本质上与客户机对象向应用边界内的服务器对象请求服务(方法)是一样的。区别在于客户端对象和服务器对象的位置。如果它们驻留在同一个应用中,那么它们可以发送和接收二进制消息,并且可以相互理解,因为它们说的是同一种“语言”随着您构建的应用变得越来越复杂,通常会将应用分成不同的组件。当您将一个应用分割成多个组件,每个组件都被设计来执行一个独特的专门服务时,您可以极大地增强代码的可维护性、可重用性和可靠性。此外,单独的服务器可以托管各种组件,以提高性能、更好地维护和安全性。

在引入 web 服务之前,应用的客户机和服务器依靠分布式技术进行通信,如微软的分布式组件对象模型(DCOM)和对象管理小组(OMG)的通用对象请求代理体系结构(CORBA)。如果客户机和服务器应用使用相同的技术,这些早期的面向服务的体系结构的尝试工作得相当好,但是当客户机和服务器使用不同的技术时,它就变得非常成问题。web 服务的强大之处在于它们使用一组开放的消息传递和传输协议。这意味着使用不同技术的客户端和服务器组件可以以标准的方式进行通信。例如,运行在 Apache web 服务器上的基于 Java 的应用可以向运行在 IIS 服务器上的基于. NET 的应用请求服务。此外,他们几乎可以位于世界上任何有互联网连接的地方。

WCF 网络服务

随着。在. NET Framework 3.0 中,微软引入了一种以 Windows Communication Foundation services(WCF)的形式创建 web 服务的新方法。在 WCF 之前,微软有一套强大但混乱的消息传递技术,包括 ASP.NET 网络服务、MSMQ、企业服务和 .NET 远程处理。微软决定将所有这些技术整合到一个开发面向服务的应用的框架中。这使得开发面向服务的应用更加一致,减少了开发人员的困惑。

WCF 服务由三部分组成:服务、端点和托管环境。服务是一个类,包含您希望向服务的客户端公开的方法。端点是客户端如何与服务通信的定义。值得注意的是,一个服务可以定义多个端点。一个端点由服务的基址地址,它的绑定信息,以及它的契约信息组成(这三者通常被称为 WCF 的 ABC)。托管环境指的是托管服务的应用。出于您的目的,这将是一个 web 服务器,但是根据您实现的 WCF 服务的类型,还存在其他选项。

创建 WCF Web 服务

使用 Visual Studio 2012 创建和消费基本的 WCF 服务是一个相当简单的过程。如果您使用 Visual Studio 提供的模板,大部分的管道工作已经为您完成了。图 14-1 显示了可用的模板。要创建 WCF web 服务,您可以使用 WCF 服务应用模板。

9781430249351_Fig14-01.jpg

图 14-1 。Visual Studio 提供的 WCF 模板

选择一个模板会向项目添加两个重要文件:一个使用接口定义服务契约,另一个是包含服务实现代码的类文件。在图 14-2 中,IService1.cs 文件定义了接口,Service1.svc.cs 包含了服务的类实现。

9781430249351_Fig14-02.jpg

图 14-2 。WCF 接口和类文件

当您创建服务时,您需要定义服务契约。契约由接口定义来定义。该接口定义服务公开的方法、方法预期的任何输入参数以及方法传回的任何输出参数。以下代码显示了税务服务的接口代码。接口用[ServiceContract]属性标记,任何公开的方法用[OperationContract]标记。

[ServiceContract]
public interface ITax
{
    [OperationContract]
    double GetSalesTax(string statecode);
}

一旦定义了接口,下一步就是定义实现接口的类。下面的代码实现了ITax接口,并提供了实现其公开方法的代码。

public class Tax : ITax
{
    public double GetSalesTax(string stateCode)
    {
        if (stateCode == "PA")
        {
            return .06;
        }
        else
        {
            return .05;
        }
    }
}

一旦定义了接口和类,编译并运行应用就产生了如图图 14-3 所示的测试客户端。您可以通过输入状态代码并单击 Invoke 按钮来测试 web 服务。点击 XML 选项卡显示客户端和服务器之间发送的请求和响应消息,如图图 14-4 所示。

9781430249351_Fig14-03.jpg

图 14-3 。测试网络服务

9781430249351_Fig14-04.jpg

图 14-4 。请求和响应消息

消费 WCF 网络服务

要在. NET 客户端中使用 WCF 服务,必须向项目中添加服务引用。当您在 Visual Studio 2012 中添加服务引用时,您会看到一个添加引用窗口(参见图 14-5 )。此窗口允许您发现可用的服务及其公开的操作。您还可以更改用于针对服务进行编程的命名空间。

9781430249351_Fig14-05.jpg

图 14-5 。添加服务引用

一旦服务引用被添加到项目中,Visual Studio 就会用调用服务所需的信息更新应用配置文件。这包括带有地址、绑定和契约信息的端点配置。

<endpoint address="
http://localhost:49185/TaxService.svc
" binding="basicHttpBinding"
                bindingConfiguration="BasicHttpBinding_ITaxService" contract="TaxService.                ITaxService"
                name="BasicHttpBinding_ITaxService" />

客户端代理也被添加到客户端应用中。客户端应用使用这个代理与服务进行交互。以下代码显示了一个客户端控制台应用使用 TaxServiceClient 代理调用服务,并将结果写入控制台窗口。图 14-6 显示了控制台窗口的输出。

TaxService.TaxServiceClient webService = new TaxService.TaxServiceClient();
string state1 = "PA";
double salesTax1 = webService.GetSalesTax(state1);
Console.WriteLine("The sales tax for {0} is {1}", state1, salesTax1);
string state2 = "NJ";
double salesTax2 = webService.GetSalesTax(state2);
Console.WriteLine("The sales tax for {0} is {1}", state2, salesTax2);
webService.Close();
Console.ReadLine();

9781430249351_Fig14-06.jpg

图 14-6 。调用 TaxService 的输出

使用数据协定

在前面的示例中,WCF web 服务仅使用简单类型在服务和客户端之间来回传递数据。整数、双精度和字符串等简单类型不需要任何特殊的编码就可以在客户端和服务器之间传递。有时候,您希望在客户机和服务器之间传递复杂的类型。复杂类型由简单类型组成。例如,您可能有一个服务,它采用由街道、城市、州和邮政编码组成的地址类型,并返回由经度和纬度组成的位置类型。为了促进复杂类型的交换,WCF 服务使用数据契约。像往常一样创建数据类,然后用[DataContract]属性标记它。您想要公开的类的属性被标记为[DataMember]属性。下面的代码向服务的客户端公开了Location类:

[DataContract]
public class Location
{
    double _longitude;
    double _latitude;
    [DataMember]
    public double Latitude
    {
        get { return _latitude; }
        set { _latitude = value; }
    }
    [DataMember]
    public double Longitude
    {
        get { return _longitude; }
        set { _longitude = value; }
    }
}

通过用[DataContract][DataMember]属性标记类,创建了一个描述复杂类型的 XML 模式定义(XSD)文件。客户端使用这个文件来确定提供什么服务以及期望什么作为返回类型。以下标记显示了为服务返回的Location类型创建的 XSD 文件部分。

<xs:complexType name="Location">
    <xs:sequence>
      <xs:element minOccurs="0" name="Latitude" type="xs:double" />
      <xs:element minOccurs="0" name="Longitude" type="xs:double" />
    </xs:sequence>
  </xs:complexType>
<xs:element name="Location" nillable="true" type="tns:Location" />

在以下活动中,您将创建一个 WCF 服务,并在. NET 客户端应用中使用该服务。

活动 14-1。创建和消费 WCF 服务

在本活动中,您将熟悉以下内容:

  • 创建 WCF 服务
  • 在客户端应用中使用 WCF 服务

创建 WCF 服务

要创建 WCF 服务,请执行以下步骤:

  1. 启动 Visual Studio。选择文件image新建image项目。

  2. Choose WCF under the Visual C# Templates folder and select the WCF Service Application project type. Rename the project to Activity14_1 and click the OK button. (See Figure 14-7)

    9781430249351_Fig14-07.jpg

    图 14-7 。创建 WCF 服务应用

  3. 删除 IService1.cs 和 Service1.svc 文件。在解决方案资源管理器中右键单击项目节点,并选择 Add image New Item。在“添加新项目”对话框中,选择 WCF 服务。将服务命名为 DiscountService。

  4. Open the IDiscountService.cs file in the code editor. Replace the DoWork operation contract with a GetDiscount operation contact.

    [ServiceContract]

    public interface IDiscountService

    {

    [OperationContract]

    double GetDiscount(string discountCode);

    }

  5. Open the DiscountService.svc.cs file in the code editor. Replace the DoWork method with the following GetDiscount method.

    public class DiscountService : IDiscountService

    {

    public double GetDiscount(string discountCode)

    {

    if (discountCode == "XXXX")

    {

    return 20;

    }

    else

    {

    return 10;

    }

    }

    }

  6. 构建项目。如果有任何错误,请修复它们,然后重新构建。

  7. 确保在解决方案资源管理器中选择 DiscountService.svc 节点,并启动调试器。您应该会看到 WCF 测试客户端。测试GetDiscount操作。完成测试后,关闭 WCF 测试客户端。

创造。网络客户端

  1. 在解决方案资源管理器中,右键单击 Activity14_1 解决方案节点,并选择 Add image New Project。在 Windows 模板下添加一个名为 OfficeSupply 的 WPF 应用。

  2. Add the following XAML to the MainWindow between the Grid tags.

    <TextBlock Text="Discount Code:" FontSize="20" Margin="10,25,350,250"/>

    <TextBox Name="txtDiscountCode" FontSize="20" Margin="160,25,100,250"/>

    <Button Name="btnGetDiscount" Content="Get Discount" Width="150"

    FontSize="20" Margin="150,75,213,200" />

  3. In the Solution Explorer, right-click the OfficeSupply project node and select Add Service Reference. In the Add Service Reference dialog, click the Discover button. You should see the DiscountService.svc as shown in Figure 14-8. Click the OK button to add the reference.

    9781430249351_Fig14-08.jpg

    图 14-8 。添加服务引用

    image 注意您的服务地址在本地开发时端口号可能会发生变化。

  4. 将 Click 事件处理程序方法添加到 btnGetDiscount 中。

  5. Add the following code to the click event handler. It is usually good practice to call web services asynchronously so the client application remains responsive while waiting for the response. Remember from Chapter 8 you need to use the await key word when calling an async method and also add the async key word to the method you are calling it from.

    private async void btnGetDiscount_Click(object sender, RoutedEventArgs e)

    {

    ServiceReference1.DiscountServiceClient service =

    new ServiceReference1.DiscountServiceClient();

    double discount = await service.GetDiscountAsync(txtDiscountCode.Text);

    MessageBox.Show(discount.ToString());

    }

  6. In the Solution Explorer, right-click the Activity14_1 solution node and select Properties. Click on the Startup Project node and select Multiple startup projects. Make sure the Action of each project is set to Start and click OK to continue. (See Figure 14-9)

    9781430249351_Fig14-09.jpg

    图 14-9 。设置启动项目

  7. Start the debugger. When the form shows enter a discount code of “XXXX” and click the Get Discount button. You should see a message box showing 20 (see Figure 14-10). Try a different code; you should see a discount of 10. When finished testing, stop the debugger and exit Visual Studio.

    9781430249351_Fig14-10.jpg

    图 14-10 。测试折扣 web 服务

RESTful 数据服务

尽管 WCF 是构建 web 服务的一项伟大技术,但是在客户端和服务器之间传递消息时,它包含了大量的开销。当客户端和服务器来回传递数据时,这一点尤其明显。不仅传递数据,还传递描述数据的所有元数据。通常,正在传输的实际数据只占正在发送的消息的很小一部分。通常并不需要所有这些元数据,事实上,当客户端和服务器使用不同的编程框架和不同类型的系统构建时,强类型化数据可能会导致问题。这就是基于表述性状态转移(REST)的服务派上用场的地方。

RESTful web 服务使用 HTTP 上的简单通信,并使用普通的旧 XML (POX )或 JavaScript 对象符号(JSON)交付数据。使用标准的 HTTP 动词 GET、PUT、POST 和 DELETE 来访问和更改数据。微软开发了 ASP.NET WEB API 作为 WCF 的 RESTful 替代品。它不会取代 WCF,但当您不需要支持 TCP 和 UDP 之类的传输协议或支持二进制编码时,它是 WCF 的一个不错的轻量级替代方案。这对于以松散耦合的方式将数据从客户机传递到服务器非常有用。因此,虽然 ASP.NET Web API 服务在将应用逻辑(方法)公开为服务方面非常有用,但在公开数据方面却非常出色。

创建 ASP.NET Web API 服务

当创建 ASP.NET Web API 服务时,您需要创建一个 Web 应用来托管该服务。一旦创建了 web 应用,就可以添加一个 Web API 控制器类。Web API 控制器类提供了处理请求消息、与数据模型交互以及生成响应消息所必需的功能。例如,当您想要请求一个雇员列表时,您可以请求下面的 URI。

http://localhost/DemoChapter14/employees

其中localhost是 web 服务器的名称,DemoChapter14 是 web 应用的名称,employees 是控制器的名称。这个 URI 被映射到控制器的 get 方法,该方法返回包含雇员数据的字符串数组。根据请求,输出可以是 POX 或 JSON 的形式。

如果你想得到一个雇员的数据,你可以使用同一个 URI,并在末尾加上雇员键。

http://localhost/DemoChapter14/employees/10

这被映射到控制器上的一个Get`方法,该方法有一个整数类型的输入参数。

public string Get(int id)
{
            //Retrieve employee data
 }

要将 URI 请求映射到 Web API 控制器类的适当方法,您需要将以下代码添加到 Global.asax.cs 文件中的 Application_Start 事件处理程序。这段代码将映射信息添加到RouteTable中。

RouteTable.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "DemoChapter14/{controller}/{id}",
                defaults: new
                {
                    id = RouteParameter.Optional
                }
            );

在下面的活动中,您将创建一个 Web API 服务,该服务从 Pubs 数据库中提供图书数据。

活动 14-2。创建 WEB API 服务

在本活动中,您将熟悉以下内容:

  • 创建 Web API 服务

要创建 Web API 服务,请按照下列步骤操作:

  1. 启动 Visual Studio。选择文件image新建image项目。

  2. Choose Web under the Visual C# Templates folder and select the ASP.NET Empty Web Application project type. Rename the project to Activity14_2 and click the OK button. (See Figure 14-11)

    9781430249351_Fig14-11.jpg

    图 14-11 。添加空的 Web 应用项目

  3. 右键单击活动 14_2。Web 项目节点,并选择 Add image New Folder。命名文件夹模型。

  4. 右键单击解决方案资源管理器窗口中的模型文件夹,并选择 Add image New Item。在“添加新项”窗口的“数据”节点下,选择一个 ADO.NET 实体数据模型。将模型命名为 Pubs.edmx,然后单击 Add。

  5. 在选择模型内容屏幕中,选择从数据库生成选项,然后单击下一步。

  6. 在“选择您的数据连接”屏幕中,选择一个现有连接或创建一个到 Pubs 数据库的新连接,然后选择“下一步”。

  7. 在“选择数据库对象”屏幕中,展开“表”节点;选择标题表;然后单击完成。

  8. 在解决方案资源管理器窗口中右键单击 Activity14_2 web 项目节点,并选择 Add image New Folder。命名文件夹控制器。

  9. 在 Solution Explorer 窗口中右键单击 Controllers 文件夹,并选择 Add image New Item。在“添加新项”窗口中,单击已安装模板中的 web 节点。选择 Web API 控制器类模板,将其重命名为BooksController,并单击 Add 按钮。

  10. Open the BooksController.cs file in the Code Editor. Add the following using statement to the top of the file.

`using Activity14_2.Model;`
  1. Update the code so that the Get method uses LINQ to EF (Chapter 10) to retrieve an array of books and sends it back to the caller.
`// GET api/<controller>`
`public IEnumerable<title> Get()`

`{`

`var context = new pubsEntities();`

`var query = from t in context.titles select t;`

`var titles = query.ToArray<title>();`

`return titles;`

`}`

12. 在解决方案资源管理器窗口中右键单击 Activity14_2 web 项目节点,并选择 Add image New Item。在“添加新项”窗口中,单击已安装模板中的 web 节点。选择全局应用类模板。将其命名为 Global.asax。 13. In the Global.asax.cs file add the following using statements.

`using System.Web.Routing;`

`using System.Web.Http;`

14. In the Application_Start event handler add the following code to map the URI verbs (Get, Put, etc.) to the BooksController methods.

`RouteTable.Routes.MapHttpRoute(`

`name: "DefaultApi",`

`routeTemplate: "Activity14_2/{controller}/{id}",`

`defaults: new`

`{`

`id= RouteParameter.Optional`

`}`

`);`

15. Build the solution. If there are any errors, fix them and rebuild. Figure 14-12 shows the project files in Solution Explorer.

![9781430249351_Fig14-12.jpg](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/187a43f491184c25a6941f4f79d0440c~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1771068300&x-signature=Rk0zA5LRJFAXXjcL%2F3wwdw0WFaM%3D)

图 14-12 。查看项目文件

16. Launch the debugger. In the browser window, type the following URI (http://LocalHost:port/Activity14_2/books). Replace the port with the port number of your project. You can find the port number by right clicking the project node in Solution Explorer and selecting properties. The project URL should be listed on the Web tab (See Figure 14-13).

![9781430249351_Fig14-13.jpg](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/37b3551859c3469a9bfd15f7c0ecdb9d~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1771068300&x-signature=dn7Q5ti4Wr16kEpcysTYvCl96%2BQ%3D)

图 14-13 。查找端口号

17. You should get a message asking if you want to open or save books.json. Open the file in Notepad. You should see the book information listed in JSON format (see Figure 14-14).

![9781430249351_Fig14-14.jpg](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/f78d3315418e473ca6ffec73b75f50de~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1771068300&x-signature=fDGAmkQfdLrpuboazaoQuMZJk1I%3D)

图 14-14 。在记事本中查看 books.json 文件

18. 完成测试后,停止调试器并退出 Visual Studio。

使用 ASP.NET Web API 服务

当客户端应用调用 ASP.NET Web API 服务时,它需要发出 HTTP 请求,并等待 POX 或 JSON 文本流形式的响应数据。收到响应后,客户端解析文本文件,并将数据加载到本地对象中进行处理。由于元数据不随数据一起传递,Visual Studio 无法自动生成代理对象供客户端使用。

名称空间中的 HttpClient 类用于发送请求和接收来自 Web API 服务的响应。下面的代码演示了如何使用HttpClient类从 web 服务请求图书列表。首先实例化一个HttpClient对象并设置基址。

private HttpClient httpClient;
httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("
http://localhost:49213/
");

一旦设置了基址,就可以设置想要返回的响应的格式。在这种情况下,需要一个 JSON 响应。

httpClient.DefaultRequestHeaders.Accept.Add
                (new MediaTypeWithQualityHeaderValue("application/json"));

然后,您可以发出请求并等待响应。因为这可能需要一段时间,所以您应该异步调用 web 服务。

var response = await httpClient.GetAsync("Activity14_2/books");

当响应返回时,您可以使用Windows.Data.Json名称空间中的JsonArray类解析文本(当前随 Windows 8 一起提供),并将其加载到本地对象/集合中进行处理。

var bookJSON = await response.Content.ReadAsStringAsync();
var bookArray = JsonArray.Parse(bookJSON);
ObservableCollection<Book> bc = new ObservableCollection<Book>();
foreach (var b in bookArray)
{
    Book bk = new Book();
    bk. title_id=b.GetObject()["title_id"].GetString();
    bk.title = b.GetObject()["title"].GetString();
    bk.type = b.GetObject()["type"].GetString();
    bc.Add(bk);
}

使用各种 Web API 服务 .NET 客户端(ASP.NET WPF 和一个 Windows 应用商店)是一个非常相似的过程。在以下活动中,您将从 Windows 应用商店应用调用您在活动 14-2 中创建的 web 服务。

活动 14-3。调用 WEB API 服务

在本活动中,您将熟悉以下内容:

  • 调用 Web API 服务

要调用 Web API 服务,请按照下列步骤操作:

  1. 启动 Visual Studio 并打开活动 14-2 中的项目。将名为 BookServiceClient 的新 Windows 应用商店空白应用添加到解决方案中。

  2. Add a new class to the BookServiceClient named Book and add the following properties to the class.

    class Book

    {

    public string Title_id { get; set; }

    public string Title { get; set; }

    public string Type { get; set; }

    public string Notes { get; set; }

    }

  3. Open the MainPage.xaml file in the designer. Add the following XAML markup between the Grid tags to define a GridView to display a collection of book data.

    <GridView Name="BooksView" ItemsSource="{Binding}" Margin="50,50,0,0">

    <GridView.ItemTemplate>

    <DataTemplate>

    <StackPanel Width="300" Height="300" Margin="10"                 Background="#FF161C8F">

    <TextBlock Margin="10,0" Text="{Binding Title_id}" />

    <TextBlock Margin="10,0" Text="{Binding Title}" />

    <TextBlock Margin="10,0" Text="{Binding Type}" />

    <TextBlock Margin="10,0" Text="{Binding Notes}"                     TextWrapping="Wrap"/>

    </StackPanel>

    </DataTemplate>

    </GridView.ItemTemplate>

    </GridView>

  4. Open the MainPage.xaml.cs file in the code editor window. Add the following using statements to the top of the file.

    using System.Net.Http;

    using System.Net.Http.Headers;

    using Windows.Data.Json;

    using System.Collections.ObjectModel;

  5. Add a class level HttpClient variable to the class and update the class constructor with the following code. Replace the port number with the port number being used by your web service.

    private HttpClient httpClient;

    public MainPage()

    {

    this.InitializeComponent();

    httpClient = new HttpClient();

    httpClient.BaseAddress = new Uri("``http://localhost:49213/

    httpClient.DefaultRequestHeaders.Accept.Add

    (new MediaTypeWithQualityHeaderValue("application/json"));

    }

  6. Add an asynchronous method that calls the web service, parses the result, and binds it to the GridView.

    private async void GetBooks()

    {

    var response = await httpClient.GetAsync("Activity14_2/books");

    if (response.IsSuccessStatusCode)

    {

    var bookJSON = await response.Content.ReadAsStringAsync();

    var bookArray = JsonArray.Parse(bookJSON);

    ObservableCollection<Book> bc = new ObservableCollection<Book>();

    foreach (var b in bookArray)

    {

    Book bk = new Book();

    bk.Title_id=b.GetObject()["title_id"].GetString();

    bk.Title = b.GetObject()["title1"].GetString();

    bk.Type = b.GetObject()["type"].GetString();

    bk.Notes = b.GetObject()["notes"].Stringify();

    bc.Add(bk);

    }

    this.BooksView.DataContext = bc;

    }

    }

  7. In the OnNavigatedTo event handler call the GetBooks method.

    protected override void OnNavigatedTo(NavigationEventArgs e)

    {

    GetBooks();

    }

  8. 构建解决方案。如果有任何错误,请修复并重新构建。

  9. In the Solution Explorer, right-click the Activity14_2 solution node and select Properties. Click on the Startup Project node and select Multiple startup projects. Make sure the Action of each project is set to Start. (See Figure 14-15).

    9781430249351_Fig14-15.jpg

    图 14-15 。设置启动项目

  10. Launch the debugger. You should see the book information listed in the GridView. (See Figure 14-16).

![9781430249351_Fig14-16.jpg](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/c43e2d537afa44c8872cdcd4a6a9e1b7~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1771068300&x-signature=iGOIrM6Ef3F%2FVNAtZnGjAMf%2Fdlc%3D)
图 14-16 。显示图书信息

11. 完成测试后,按 Alt + F4 键关闭应用。在 Visual Studio 中,停止调试器并退出 Visual Studio。

摘要

在本章中,您已经了解了实现 web 服务的基础知识。特别是,您看到了如何使用 ASP.NET web API 和 Windows 通信框架(WCF)创建 Web 服务。您还构建了调用这些 web 服务的客户端应用。

这是旨在向您展示各种技术和 .NET 框架类来构建 .NET 应用。这些章节仅仅触及了这些技术的表面。随着开发经验的积累 .NET 应用,您将需要更深入地研究这些技术。

到目前为止,您已经学习了 UML 设计、面向对象编程、C# 语言、 .NET 框架、创建图形用户界面和开发 Web 服务。现在,您已经准备好将这些部分放在一起,开发一个可工作的应用。在第十五章的中,你将重温你为第四章中介绍的案例研究开发的 UML 模型。您将把这些模型转换成一个功能应用。`