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

ASP.NET/AJAX 3.5 配合 Aquarium Express

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.60/5 (7投票s)

2008年9月18日

Ms-PL

12分钟阅读

viewsIcon

38383

downloadIcon

957

学习使用免费的 Aquarium Express 框架构建现代化的 AJAX 和 ASP.NET 3.5 应用程序

Aquarium Express 入门指南

Aquarium Express 入门指南是作为Data Aquarium Framework功能的一个免费入门介绍。该示例项目可作为任何复杂度的 Web 2.0 应用程序的坚实基础。它使用了最先进的ASP.NET 3.5Ajax Control Toolkit

查看Aquarium Express 入门指南在线演示

下载示例应用程序和Aquarium Express的完整源代码(VB.NETVisual C#版本)(AquariumExpressPrimer.zip - 908.77 KB)。基础代码是由免费的Code OnTime Generator生成的。

我们将为一家邮购公司Northwind开发一个订单管理应用程序,使用Northwind数据库和Microsoft SQL Server 2005以及Visual Studio 2008。免费的Visual Web Developer 2008 Express Edition也可以用于开发基于Aquarium Express的应用程序。

Image01.png

启动代码生成器并创建一个名为Northwind的新Aquarium Express项目。保留默认设置并完成项目源代码生成。浏览器窗口将打开并显示一个默认的示例页面。关闭浏览器并运行您的Visual Studio版本。选择文件 | 打开网站菜单选项,然后浏览到[我的文档]\Code OnTime\Aquarium Express\Northwind文件夹。单击打开按钮以打开项目。

或者,您也可以下载本文提供的源代码,并在您的Visual Studio版本中打开 VB.NET 或 C# 版本的源代码。此处描述的所有代码修改都已包含在源代码中。

客户列表

在网站根目录下创建一个名为Forms的文件夹。右键单击Forms文件夹并选择添加新项选项。创建一个名为CustomersWeb 窗体,并确保选择位于网站根目录下的主页MasterPage.master。像下面这样修改页面。

<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true"
    CodeFile="Customers.aspx.cs" Inherits="Forms_Customers" Title="Customers" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="Header1Placeholder" runat="Server">
    Customers
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="Header2Placeholder" runat="Server">
    Northwind
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="BodyPlaceholder" runat="Server">
    <div id="CustomerList" runat="server" />
    <aquarium:DataViewExtender ID="CustomerExtender" runat="server" 
        Controller="Customers" View="grid1" TargetControlID="CustomerList" />
</asp:Content>      

DataViewExtender控件是使用Data Aquarium Framework创建的 Web 应用程序的基石。两个关键属性ControllerTargetControlID分别提供了数据控制器描述符和表示容器目标。如果您在浏览器中查看页面,将会显示以下内容。

Image02.png

请注意,每当您单击最左侧列中的任何链接时,网格视图都会切换到窗体模式并显示客户详细信息。让我们更改此行为,让页面重定向到另一个 Web 窗体,并将选定的客户ID 传递过去。打开~/Controllers/Customers.aspx页面,并将Grid范围内的操作修改为如下所示。

<actionGroup scope="Grid">
  <action commandName="Navigate" headerText="Show Orders" 
     commandArgument="Orders.aspx?CustomerID={CustomerID}"/>
  <action commandName="Select" commandArgument="editForm1" 
     headerText="View Customer Information"/>
  <action commandName="Edit" />
  <action commandName="Delete" confirmation="Delete?" />
  <action whenLastCommandName="Edit" commandName="Update" headerText="Save" />
  <action whenLastCommandName="Edit" commandName="Cancel" />
</actionGroup>       

在 Web 浏览器中执行第一个操作时,花括号中的CustomerID字段引用将被实际值替换。如果用户单击链接或选择Show Orders上下文菜单选项,就会发生这种情况。

Image04.png

订单列表

按照上述相同步骤,在~/Forms文件夹中添加Orders Web 窗体。使页面标记如下。

<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true"
    CodeFile="Orders.aspx.cs" Inherits="Forms_Orders" Title="Orders" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="Header1Placeholder" runat="Server">
    Orders
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="Header2Placeholder" runat="Server">
    Northwind
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="BodyPlaceholder" runat="Server">
    <div id="Customer" runat="server" class="CustomerInfo" />
    <aquarium:DataViewExtender ID="CustomerExtender" runat="server" 
        Controller="Customers"
        View="editForm1" TargetControlID="Customer" ShowActionBar="false" />
    <div id="OrderList" runat="server" />
    <aquarium:DataViewExtender ID="OrderExtender" runat="server" Controller="Orders"
        View="grid1" TargetControlID="OrderList" />
</asp:Content>     

在 Web 浏览器中打开Customers窗体并选择任何客户。将会显示以下视图。

Image05.png

窗体会自动根据浏览器 URL 中的客户 ID 过滤订单。您可以立即编辑客户信息并对订单进行各种更改。尽管此屏幕看起来不错,但仍有相当多的不足之处。窗体太长了,我们也不知道每个订单的总金额。此外,我们实际上不想允许任何订单更改,而是希望使用专用的订单编辑窗体。

首先,我们将使窗体变短。在Orders Web 窗体的Content4中的任何位置添加以下标记。

<div id="Customers_editForm1" style="display: none">
    <table style="width: 100%">
        <tr>
            <td>
                {CustomerID}
            </td>
            <td style="font-weight: bold">
                {CompanyName}
            </td>
            <td>
                {ContactName}
            </td>
            <td>
                {ContactTitle}
            </td>
            <td>
                {Phone}
            </td>
            <td>
                {City}
            </td>
            <td>
                {Country}
            </td>
        </tr>
    </table>
</div> 

这将创建一个不可见的窗体模板,该模板由Data Aquarium FrameworkWeb.DataView JavaScript 组件自动使用。该组件将使用花括号中的字段名作为在浏览器中呈现的数据字段的占位符。刷新Orders页面,将显示以下视图。

Image06.png

请注意,当为Customers控制器呈现模板 ID 时,它会与editForm1视图匹配。您可以在此处阅读关于窗体模板的更多讨论。

这种新的外观比以前的版本有了实质性的改进。让我们移除客户窗体下方的按钮行以及窗体上方的描述性文本。这将留下一个干净的面板,其中包含客户信息,位于订单上方。

~/App_Themes/MyCompany文件夹中创建一个名为Northwind.css的 CSS 样式表。在样式表正文中输入以下文本。

.CustomerInfo .HeaderTextRow, .CustomerInfo .BottomButtonsRow
{
    display:none;
}       

窗体的第一个Div元素使用 CSS 类名进行定义。

 <div id="Customer" runat="server" class="CustomerInfo" />      

CustomerInfo类允许我们自定义带有描述性文本的标题行和底部的操作按钮行的表示。自定义非常简单——我们不显示它们。刷新页面以像这样查看它。

Image07.png

现在,让我们为每个订单行添加总金额。Data Aquarium Framework会自动解析数据控制器中的查询,以构建selectupdateinsertdelete SQL 语句。我们将创建Northwind数据库中的一个附加视图,并将该视图连接到~/Controllers/Orders.xml文件中的查询。请使用SQL Management Studio或您喜欢的数据库管理工具创建以下 SQL 视图。

CREATE view [dbo].[OrderTotals]
as
select
    "OrderDetails"."OrderID", 
    sum("OrderDetails"."UnitPrice" * "OrderDetails"."Quantity" * 
        (1 - "OrderDetails"."Discount")) "ItemsTotal"
from "dbo"."Order Details" "OrderDetails"
group by "OrderDetails"."OrderID"     

然后打开数据控制器文件,并将command1的文本修改为如下所示。

<text>
        <![CDATA[
select
    "Orders"."OrderID" "OrderID"
    ,"Orders"."CustomerID" "CustomerID"
    ,"Customer"."CompanyName" "CustomerCompanyName"
    ,"Orders"."EmployeeID" "EmployeeID"
    ,"Employee"."LastName" "EmployeeLastName"
    ,"Orders"."OrderDate" "OrderDate"
    ,"Orders"."RequiredDate" "RequiredDate"
    ,"Orders"."ShippedDate" "ShippedDate"
    ,"Orders"."ShipVia" "ShipVia"
    ,"ShipVia"."CompanyName" "ShipViaCompanyName"
    ,"Orders"."Freight" "Freight"
    ,"Orders"."ShipName" "ShipName"
    ,"Orders"."ShipAddress" "ShipAddress"
    ,"Orders"."ShipCity" "ShipCity"
    ,"Orders"."ShipRegion" "ShipRegion"
    ,"Orders"."ShipPostalCode" "ShipPostalCode"
    ,"Orders"."ShipCountry" "ShipCountry"
    ,(cast("OrderTotals"."ItemsTotal" as money)) "ItemsTotal"
    ,(cast("OrderTotals"."ItemsTotal" + "Orders"."Freight" as money)) 
    "OrderTotal"
from "dbo"."Orders" "Orders"
    left join "dbo"."Customers" "Customer" on 
    "Orders"."CustomerID" = "Customer"."CustomerID"
    left join "dbo"."Employees" "Employee" on 
    "Orders"."EmployeeID" = "Employee"."EmployeeID"
    left join "dbo"."Shippers" "ShipVia" on 
    "Orders"."ShipVia" = "ShipVia"."ShipperID"
    left join "OrderTotals" on "Orders"."OrderID" = "OrderTotals"."OrderID"
]]>
      </text>    

主要关注点是查询底部的left join以及field 子句末尾的两个字段。这两个字段都定义为表达式,必须用括号括起来,以帮助Data Aquarium Framework的正则表达式正确识别表达式和相应的别名。left join是将新视图连接进来。

在 /dataController/fields元素中添加两个新字段。

<field name="ItemsTotal" type="Decimal" readOnly="true" label="Items Total" 
    dataFormatString="c"/>
<field name="OrderTotal" type="Decimal" readOnly="true" label="Total" 
    dataFormatString="c"/>     

请确保将这两个字段都标记为只读。数据格式字符串将确保这两个字段都显示为货币。您可以使用替代语法的数据格式字符串:{0:c},它与您熟悉的String.Format函数兼容。

将网格视图grid1移动到窗体视图editForm1之后,以确保编辑窗体成为Orders控制器的默认表示视图。如下所示格式化grid1中的字段。

<view id="grid1" type="Grid" commandId="command1" label="Orders">
  <headerText>This is a list of orders. </headerText>
  <dataFields>
    <dataField fieldName="OrderID" />
    <dataField fieldName="CustomerID" aliasFieldName="CustomerCompanyName" />
    <dataField fieldName="EmployeeID" aliasFieldName="EmployeeLastName" />
    <dataField fieldName="OrderDate" columns="10" />
    <dataField fieldName="RequiredDate" columns="10" />
    <dataField fieldName="ShippedDate" columns="10" />
    <dataField fieldName="ShipVia" aliasFieldName="ShipViaCompanyName" />
    <dataField fieldName="Freight" dataFormatString="c" columns="15" />
    <dataField fieldName="ItemsTotal"/>
    <dataField fieldName="OrderTotal"/>
  </dataFields>
</view>     

我们引入了主键字段OrderID,您可能会希望将其作为最终用户的视觉参考。项目和订单总计字段位于列表末尾。刷新Orders页面以查看以下内容。请确保您可以按orderitems总计进行排序。

Image08.png

最后一步是更改网格视图上下文菜单中可用的操作。同时删除所有应该显示在标题文本为Actions的操作栏上的操作。

<actionGroup scope="Grid">
  <action commandName="Navigate" 
    commandArgument="~/Forms/Details.aspx?OrderID={OrderID}" 
    headerText="Order Details"/>
</actionGroup>    

这将显示在图片所示的效果中。

Image09.png

订单详情

按照创建Customers.aspxOrders.aspx窗体时解释的相同步骤,向~/Forms文件夹添加Details.aspx Web 窗体。按照示例中的说明格式化页面。

<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" 
    AutoEventWireup="true" CodeFile="Details.aspx.cs" 
    Inherits="Forms_Details" Title="Order Details" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="Header1Placeholder" runat="Server">
    Order Details
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="Header2Placeholder" runat="Server">
    Northwind
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="BodyPlaceholder" runat="Server">
    <div id="Order" runat="server" class="Order" />
    <aquarium:DataViewExtender ID="OrderExtender" runat="server" Controller="Orders"
        View="editForm1" TargetControlID="Order" ShowActionBar="false" />
    <div id="Details" runat="server" />
    <aquarium:DataViewExtender ID="DetailsExtender" runat="server" 
    Controller="OrderDetails"
        View="grid1" TargetControlID="Details" />
</asp:Content>  

如我们之前所见,该页面功能齐全,但使用起来有些困难。

Image10.png

我们将从order窗体的模板开始。

<div id="Orders_editForm1" style="display: none">
    <table style="width: 100%">
        <tr>
            <td colspan="3">
                <table style="width: 100%; border: solid 1px silver;">
                    <tr>
                        <td style="font-weight: bold">
                            {OrderID}
                        </td>
                        <td style="font-weight: bold">
                            {CustomerID}
                        </td>
                        <td style="font-weight: bold">
                            {EmployeeID}
                        </td>
                        <td style="font-weight: bold">
                            {ShipVia}
                        </td>
                        <td style="font-weight: bold">
                            {Freight}
                        </td>
                        <td style="font-weight: bold">
                            {ItemsTotal}
                        </td>
                        <td style="font-weight: bold">
                            {OrderTotal}
                        </td>
                    </tr>
                </table>
            </td>
        </tr>
        <tr>
            <td valign="top">
                <div>
                    {OrderDate}</div>
                <div>
                    {RequiredDate}</div>
                <div>
                    {ShippedDate}</div>
            </td>
            <td valign="top">
                <div>
                    {ShipName}</div>
                <div>
                    {ShipAddress}</div>
                <div>
                    {ShipCity}</div>
            </td>
            <td valign="top">
                <div>
                    {ShipRegion}</div>
                <div>
                    {ShipPostalCode}</div>
                <div>
                    {ShipCountry}</div>
            </td>
        </tr>
    </table>
</div>     

此模板将订单字段分为三组。这立即改善了显示效果。

Image11.png

呈现的显示中缺少ItemsTotalOrderTotal字段。对Orders.xml中的editForm1进行以下更改,以将丢失的字段添加到窗体。

<view id="editForm1" type="Form" commandId="command1" label="Order">
  <headerText>Please review orders information below. Click 
    Edit to change this record, click Delete to delete the record, or 
    click Cancel/Close to return back.</headerText>
  <categories>
    <category headerText="Orders">
      <description>These are the fields of the orders record 
    that can be edited.</description>
      <dataFields>
        <dataField fieldName="CustomerID" aliasFieldName="CustomerCompanyName" />
        <dataField fieldName="EmployeeID" aliasFieldName="EmployeeLastName" />
        <dataField fieldName="OrderDate" columns="10" />
        <dataField fieldName="RequiredDate" columns="10" />
        <dataField fieldName="ShippedDate" columns="10" />
        <dataField fieldName="ShipVia" aliasFieldName="ShipViaCompanyName" />
        <dataField fieldName="Freight" dataFormatString="c" columns="15" />
        <dataField fieldName="ShipName" columns="40" />
        <dataField fieldName="ShipAddress" />
        <dataField fieldName="ShipCity" columns="15" />
        <dataField fieldName="ShipRegion" columns="15" />
        <dataField fieldName="ShipPostalCode" columns="10" />
        <dataField fieldName="ShipCountry" columns="15" />
        <dataField fieldName="ItemsTotal"/>
        <dataField fieldName="OrderTotal"/>
      </dataFields>
    </category>
  </categories>
</view>     

我们将通过替换Cancel和Close命令的定义来更改操作按钮的行为。

<actionGroup scope="Form">
  <action commandName="Edit" />
  <action commandName="Delete" confirmation="Delete?" />
  <action commandName="Back" headerText="Close"/>
  <action whenLastCommandName="Edit" commandName="Update" headerText="OK" />
  <action whenLastCommandName="Edit" commandName="Delete" 
    confirmation="Delete?" />
  <action whenLastCommandName="Edit" commandName="Select" 
    commandArgument="editForm1" headerText="Cancel" />
  <action whenLastCommandName="New" commandName="Insert" headerText="OK" />
  <action whenLastCommandName="New" commandName="Cancel" />
</actionGroup>   

Close按钮只是将浏览器重定向到历史记录中的上一步,这将使用户返回到订单列表。标有Cancel的按钮会选择当前记录并在editForm1中显示它,从而取代显示数据控制器中定义的第一个视图的标准行为。

此外,将字段ShipVia的项目表示样式从Lookup更改为DropDownList。数据库中只有三个运输商,因此使用弹出窗口选择运输商意义不大。对查找字段的标签进行一些美化修改,将使您在编辑任何订单时,窗体看起来与图片中的相似。

Image12.png

接下来,我们将为Order Details引入ExtendedPrice字段,并删除上面已经显示的订单客户、员工和送货信息。打开~/Controllers/OrderDetails.xml数据控制器描述符,并进行以下更改。

修改command1text元素,通过考虑项的数量和折扣来计算扩展项价格。请确保在表达式周围使用括号。

<text>
    <![CDATA[
select
    "OrderDetails"."OrderID" "OrderID"
    ,"Order"."CustomerID" "OrderCustomerID"
    ,"OrderCustomer"."CompanyName" "OrderCustomerCompanyName"
    ,"OrderEmployee"."LastName" "OrderEmployeeLastName"
    ,"OrderShipVia"."CompanyName" "OrderShipViaCompanyName"
    ,"OrderDetails"."ProductID" "ProductID"
    ,"Product"."ProductName" "ProductProductName"
    ,"ProductCategory"."CategoryName" "ProductCategoryCategoryName"
    ,"ProductSupplier"."CompanyName" "ProductSupplierCompanyName"
    ,"OrderDetails"."UnitPrice" "UnitPrice"
    ,"OrderDetails"."Quantity" "Quantity"
    ,"OrderDetails"."Discount" "Discount"
    ,("OrderDetails"."UnitPrice" * "OrderDetails"."Quantity" * 
    (1 - "OrderDetails"."Discount")) "ExtendedPrice"
from "dbo"."Order Details" "OrderDetails"
    left join "dbo"."Orders" "Order" on 
    "OrderDetails"."OrderID" = "Order"."OrderID"
    left join "dbo"."Customers" "OrderCustomer" on 
    "Order"."CustomerID" = "OrderCustomer"."CustomerID"
    left join "dbo"."Employees" "OrderEmployee" on 
    "Order"."EmployeeID" = "OrderEmployee"."EmployeeID"
    left join "dbo"."Shippers" "OrderShipVia" on 
    "Order"."ShipVia" = "OrderShipVia"."ShipperID"
    left join "dbo"."Products" "Product" on 
    "OrderDetails"."ProductID" = "Product"."ProductID"
    left join "dbo"."Categories" "ProductCategory" on 
    "Product"."CategoryID" = "ProductCategory"."CategoryID"
    left join "dbo"."Suppliers" "ProductSupplier" 
    on "Product"."SupplierID" = "ProductSupplier"."SupplierID"
]]></text>    

OrderDetails.xmlfields元素中添加扩展价格字段。

<field name="ExtendedPrice" type="Decimal" allowNulls="true" 
   readOnly="true" label="Extended Price" dataFormatString="c"/>      

根据此代码段中的显示方式修改视图grid1

<view id="grid1" type="Grid" commandId="command1" label="Order Details">
  <headerText>This is a list of order details. </headerText>
  <dataFields>
    <dataField fieldName="OrderID" aliasFieldName="OrderCustomerID" />
    <dataField fieldName="ProductID" aliasFieldName="ProductProductName" />
    <dataField fieldName="UnitPrice" dataFormatString="c" columns="15" />
    <dataField fieldName="Quantity" columns="15" />
    <dataField fieldName="Discount" columns="15" />
    <dataField fieldName="ProductCategoryCategoryName" columns="15" />
    <dataField fieldName="ProductSupplierCompanyName" columns="40" />
    <dataField fieldName="ExtendedPrice"/>
  </dataFields>
</view> 

将以下 CSS 类定义添加到Northwind.css中,以隐藏顶部的操作按钮行。这里我们利用了窗体第一个div元素的Orders类名属性。

.Order .TopButtonsRow
{
    display: none;
}  

订单详情以扩展价格显示在最后一列,为查看者提供了精美的呈现。

Image13.png

更改订单详情时自动刷新

当您保存更改时,订单运费的更改会立即反映在屏幕上。但订单详情更改时则不然。每当订购新商品、修改现有商品或删除商品时,订单总计都必须重新计算。在添加新商品时,产品价格不会传输到Order Details记录的UnitPrice中。您需要自行输入产品单价。如果留空,则默认为零。

如果我们在将order details记录插入数据库后自动复制产品价格,则可以安全地从createForm1视图中省略UnitPrice字段。将OrderDetails.xml中的createForm1修改为如下所示。

<view id="createForm1" type="Form" commandId="command1" label="New Order Details">
  <headerText>Please fill this form and click OK button to 
    create a new order details record. Click Cancel to return 
    to the previous screen.</headerText>
  <categories>
    <category headerText="New Order Details">
      <description>Complete the form. Make sure to enter 
    all required fields.</description>
      <dataFields>
        <dataField fieldName="OrderID" aliasFieldName="OrderCustomerID" />
        <dataField fieldName="ProductID" aliasFieldName="ProductProductName" />
        <!--<dataField fieldName="UnitPrice" dataFormatString="c" columns="15" />-->
        <dataField fieldName="Quantity" columns="15" />
        <dataField fieldName="Discount" columns="15" />
      </dataFields>
    </category>
  </categories>
</view>

当您添加新商品时,这将产生以下显示。请注意,由于OrderID字段在Details.aspx中通过页面 URL 中的OrderID参数进行过滤,因此未显示。

Image14.png

接下来,我们将编写一些代码。右键单击项目树中的App_Code文件夹,然后将Class1类添加到网站代码库中。此类将实现OrderDetails数据控制器的自定义操作处理程序。修改OrderDetails.xml以将自定义操作处理程序链接到Data Aquarium Framework

<dataController .... xmlns="urn:schemas-codeontime-com:data-aquarium" 
 actionHandlerType="Class1">

这是您需要输入的C#代码。

using System;
using MyCompany.Data;
 
public class Class1 : ActionHandlerBase
{
    protected override void AfterSqlAction(ActionArgs args, ActionResult result)
    {
        if (args.CommandName == "Insert")
        {
            double price = 0;
            using (SqlText findPrice = new SqlText(
                "select UnitPrice from Products where ProductId = @ProductID"))
            {
                findPrice.AddParameter("@ProductId", args["ProductID"].Value);
                price = Convert.ToDouble(findPrice.ExecuteScalar());
            }
            using (SqlText updatePrice = new SqlText(
                "update [Order Details] set UnitPrice = @UnitPrice " +
           "where OrderID = @OrderID and ProductID = @ProductID))
            {
                updatePrice.AddParameter("@UnitPrice", price);
                updatePrice.AddParameter("@OrderID", args["OrderID"].Value);
                updatePrice.AddParameter("@ProductID", args["ProductID"].Value);
                updatePrice.ExecuteNonQuery();
            }
        }
        if (args.ContextKey.EndsWith("DetailsExtender"))
            result.ClientScript =
                "Web.DataView.find('OrderExtender').goToPage(-1);" +
                "this.set_lastCommandName(null);" +
                "this.goToView('grid1');";
    }
}     

这是同一类的VB.NET等效代码。

Imports Microsoft.VisualBasic
Imports MyCompany.Data
 
Public Class Class1
    Inherits ActionHandlerBase
 
    Protected Overrides Sub AfterSqlAction(ByVal args As MyCompany.Data.ActionArgs, _
    ByVal result As MyCompany.Data.ActionResult)
        If args.CommandName = "Insert" Then
            Dim price As Double = 0
            Using findPrice As SqlText = New SqlText( _
                "select UnitPrice from Products where ProductId = @ProductID")
                findPrice.AddParameter("@ProductID", args("ProductID").Value)
                price = Convert.ToDouble(findPrice.ExecuteScalar())
            End Using
            Using updatePrice As SqlText = New SqlText( _
                "update [Order Details] set UnitPrice = @UnitPrice " & _
           "where OrderID = @OrderID and ProductID = @ProductID")
                updatePrice.AddParameter("@UnitPrice", price)
                updatePrice.AddParameter("@OrderID", args("OrderID").Value)
                updatePrice.AddParameter("@ProductID", args("ProductID").Value)
                updatePrice.ExecuteNonQuery()
            End Using
        End If
        If args.ContextKey.Contains("DetailsExtender") Then
            result.ClientScript = _
                "Web.DataView.find('OrderExtender').goToPage(-1);" + _
                "this.set_lastCommandName(null);" + _
                "this.goToView('grid1');"
        End If
    End Sub
 
End Class
    

让我们分析一下代码。

每当 JavaScript Web.DataView组件在浏览器中请求操作执行并且涉及OrderDetails数据控制器时,我们的操作处理程序就会被框架自动调用。

我们希望拦截每个Insert命令,并将UnitPricedbo.Products表复制到dbo.[Order Details]表中。Data Aquarium Framework提供了两个实用类SqlStoredProcedureSqlText,以简化 SQL 存储过程和任意 SQL 命令的执行。您可以使用您喜欢的数据访问库与数据库进行交互。

第二个条件语句的目的是拦截来自由名为DetailsExtenderDataViewExtender实例化的客户端 JavaScript Web.DataView组件的任何操作。此条件语句将客户端 JavaScript 语句简单地分配给result参数的CientScript属性。当操作将控制权从服务器代码返回时,此 JavaScript 将在浏览器中执行。

第一个 JavaScript 语句将找到OrderExtender并要求它导航到索引为 -1 的页面。这将导致order摘要视图刷新。

接下来,清除最后执行的命令,以允许Web.DataView的操作状态机继续正确运行。操作始终在最后一个操作命令名的上下文中执行。在这里,我们希望清除最后一个操作命令名,就像默认情况下所做的那样。

最后一个 JavaScript 语句将强制订单详细信息视图显示网格视图grid1InsertUpdate命令是从窗体视图发起的,此语句对于确保窗体视图切换到网格表示样式是必需的。

如果操作执行完成后未从服务器返回客户端脚本,则Web.DataView会自动执行 JavaScript 语句二和三。我们决定强制刷新order视图,现在我们必须自己处理底层细节。

尝试操作订单详情,使订单摘要自动更新以反映订单更改。

库存管理器

现在,我们将为我们的邮购公司创建一个库存管理器。在网站的~/Forms文件夹中创建一个新页面Inventory.aspx。将页面格式化为包含一个TabContainer和几个TabPanel组件。这两种控件都属于Ajax Control Toolkitact的标签前缀已在您网站的web.config文件中注册。Data Aquarium Framework在很大程度上依赖于此工具包来实现其客户端功能。

<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true"
    CodeFile="Inventory.aspx.cs" Inherits="Forms_Inventory" Title="Inventory" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="Header1Placeholder" runat="Server">
    Inventory
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="Header2Placeholder" runat="Server">
    Northwind
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="BodyPlaceholder" runat="Server">
    <act:TabContainer ID="InventoryTabs" runat="server">
        <act:TabPanel ID="ProductsTab" runat="server" HeaderText="Products">
            <ContentTemplate>
                <div id="Products" runat="server" />
                <aquarium:DataViewExtender ID="ProductsExtender" runat="server" 
                    Controller="Products"
                    TargetControlID="Products" />
            </ContentTemplate>
        </act:TabPanel>
        <act:TabPanel ID="SuppliersTab" runat="server" HeaderText="Suppliers">
            <ContentTemplate>
                <div id="Suppliers" runat="server" />
                <aquarium:DataViewExtender ID="SuppliersExtender" runat="server" 
                    Controller="Suppliers"
                    TargetControlID="Suppliers" PageSize="5" />
                <br />
                <div id="SupplierProducts" runat="server" />
                <aquarium:DataViewExtender ID="SupplierProductsExtender" runat="server" 
                    Controller="Products"
                    TargetControlID="SupplierProducts" FilterSource="SuppliersExtender" 
                    FilterFields="SupplierID" PageSize="5" />
            </ContentTemplate>
        </act:TabPanel>
        <act:TabPanel ID="CategoriesTab" runat="server" HeaderText="Categories">
            <ContentTemplate>
                <div id="Categories" runat="server" />
                <aquarium:DataViewExtender ID="CategoriesExtender" runat="server" 
                    Controller="Categories" TargetControlID="Categories" PageSize="5" />
                <br />
                <div id="CategoryProducts" runat="server" />
                <aquarium:DataViewExtender ID="CategoryProductsExtender" runat="server" 
                    Controller="Products" TargetControlID="CategoryProducts" 
                    FilterSource="CategoriesExtender" FilterFields="CategoryID"
                    PageSize="5" />
            </ContentTemplate>
        </act:TabPanel>
    </act:TabContainer>
</asp:Content>

在 Web 浏览器中打开Inventory.aspx

Image15.png

Products选项卡显示产品列表,而SuppliersCategories选项卡则显示相应的实体,并将产品与它们以主详细关系链接。用户可以快速创建新的库存实体并根据需要浏览它们。

Image16.png

通过引入附加的产品视图,我们可以对产品管理进行一些进一步的增强。打开~/Controllers/Products.xml并在command1之后添加两个命令。

<command id="command2" type="Text">
      <text>
        <![CDATA[
select
    "Products"."ProductID" "ProductID"
    ,"Products"."ProductName" "ProductName"
    ,"Products"."SupplierID" "SupplierID"
    ,"Supplier"."CompanyName" "SupplierCompanyName"
    ,"Products"."CategoryID" "CategoryID"
    ,"Category"."CategoryName" "CategoryCategoryName"
    ,"Products"."QuantityPerUnit" "QuantityPerUnit"
    ,"Products"."UnitPrice" "UnitPrice"
    ,"Products"."UnitsInStock" "UnitsInStock"
    ,"Products"."UnitsOnOrder" "UnitsOnOrder"
    ,"Products"."ReorderLevel" "ReorderLevel"
    ,"Products"."Discontinued" "Discontinued"
from "dbo"."Products" "Products"
    left join "dbo"."Suppliers" "Supplier" on 
    "Products"."SupplierID" = "Supplier"."SupplierID"
    left join "dbo"."Categories" "Category" on 
    "Products"."CategoryID" = "Category"."CategoryID"
where
  "Products"."Discontinued" = 0
]]>
      </text>
    </command>
    <command id="command3" type="Text">
      <text>
        <![CDATA[
select
    "Products"."ProductID" "ProductID"
    ,"Products"."ProductName" "ProductName"
    ,"Products"."SupplierID" "SupplierID"
    ,"Supplier"."CompanyName" "SupplierCompanyName"
    ,"Products"."CategoryID" "CategoryID"
    ,"Category"."CategoryName" "CategoryCategoryName"
    ,"Products"."QuantityPerUnit" "QuantityPerUnit"
    ,"Products"."UnitPrice" "UnitPrice"
    ,"Products"."UnitsInStock" "UnitsInStock"
    ,"Products"."UnitsOnOrder" "UnitsOnOrder"
    ,"Products"."ReorderLevel" "ReorderLevel"
    ,"Products"."Discontinued" "Discontinued"
from "dbo"."Products" "Products"
    left join "dbo"."Suppliers" "Supplier" on 
    "Products"."SupplierID" = "Supplier"."SupplierID"
    left join "dbo"."Categories" "Category" on 
    "Products"."CategoryID" = "Category"."CategoryID"
where
  "Products"."Discontinued" = 0 and
  "Products"."UnitsInStock" <= "Products"."ReorderLevel"
]]>
      </text>
    </command>
     

命令command2选择未停产的产品。命令command3选择低库存的当前产品。请注意,所有三个命令都具有相同的输出列集。这一点非常重要,它允许Web.DataView组件和框架的支持服务器代码正确显示相同数据的多个视图。where子句允许您根据某些预定义的条件限制数据。您还可以添加order by子句,如果您愿意,可以为每个命令提供默认的排序顺序。

接下来,在网格视图grid1的上方插入两个类型为grid的视图。

<view id="grid2" type="Grid" commandId="command2" label="Current Products">
  <headerText>This is a list of current products. </headerText>
  <dataFields>
    <dataField fieldName="ProductName" columns="40" />
    <dataField fieldName="SupplierID" aliasFieldName="SupplierCompanyName" />
    <dataField fieldName="CategoryID" aliasFieldName="CategoryCategoryName" />
    <dataField fieldName="QuantityPerUnit" columns="20" />
    <dataField fieldName="UnitPrice" dataFormatString="c" columns="15" />
    <dataField fieldName="UnitsInStock" columns="15" />
    <dataField fieldName="UnitsOnOrder" columns="15" />
    <dataField fieldName="ReorderLevel" columns="15" />
  </dataFields>
</view>
<view id="grid3" type="Grid" commandId="command3" label="Products To Order">
  <headerText>This is a list of products with low stock. </headerText>
  <dataFields>
    <dataField fieldName="ProductName" columns="40" />
    <dataField fieldName="SupplierID" aliasFieldName="SupplierCompanyName" />
    <dataField fieldName="CategoryID" aliasFieldName="CategoryCategoryName" />
    <dataField fieldName="QuantityPerUnit" columns="20" />
    <dataField fieldName="UnitPrice" dataFormatString="c" columns="15" />
    <dataField fieldName="UnitsInStock" columns="15" />
    <dataField fieldName="UnitsOnOrder" columns="15" />
    <dataField fieldName="ReorderLevel" columns="15" />
  </dataFields>
</view> 

由于相应的命令生成的记录集只包含活动产品,因此这两个视图都不显示Discontinued字段。我们还为每个视图提供了替代的标题文本和标签,以便更容易理解每个视图的目的。数据视图可以拥有一组与命令不同但必须始终返回相同列集的字段。

现在,这是inventory管理器所有选项卡上显示给用户的视图选择器。

Image17.png

当用户添加新产品或更改现有订单详情时,在订单管理屏幕中也可以使用相同的视图。

Image18.png

结论

市面上很少有产品能像免费的Aquarium Express那样,帮助您创建现代化的AJAX应用程序。Data Aquarium Framework为专业开发人员提供了额外的工具,使其能够将框架用于 Web 应用程序开发的许多方面。

下载 AquariumExpressPrimer.zip - 908.77 KB

© . All rights reserved.