当前位置: 移动技术网 > IT编程>开发语言>.net > 在ASP.NET 2.0中操作数据之三十二:数据控件的嵌套

在ASP.NET 2.0中操作数据之三十二:数据控件的嵌套

2017年12月12日  | 移动技术网IT编程  | 我要评论

调皮千金玩转校园,郑睿臻,光之影奇迹

导言

  除了静态html和数据绑定语法,template也可以包含web控件和用户控件.这些控件的属性可以通过声明语法,数据绑定语法或在服务器端通过事件处理编程来设置.

  通过将控件嵌入到template里,可以自定义界面,提升用户体验.例如,在在gridview控件中使用templatefield 里,我们学习了如何通过在gridview的templatefield里加一个calendar控件来表示员工的雇佣日期.在给编辑和新增界面增加验证控件 和定制数据修改界面 里,我们学习了如何通过添加验证控件, textbox,dropdownlist和其它web控件来自定义编辑,插入界面.

  template也可以包含其它数据控件.即,我们可以让datalist在template里包含其它datalist(或者repeater,gridview,detailsview等).这个工作的挑战在于将数据绑定到里面的数据控件上.有几种不同的方法可以实现,包括从使用objectdatasource的声明语言到直接编程.

  在本章里我们将探索如何使用嵌套的repeater.外层的repeater将每个category显示为一个item,包含category的name和description.每个category的item里的repeater显示此category下的每个product(见图1).我们将分别学习如何通过声明和编程的方法创建内层的repeater.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121909375505.jpg
图1: category和属于它的product一起被列出

第一步: 创建category列表

  当创建一个使用嵌套数据控件的页时,我发现开始从最外层的控件的设计,创建和测试开始非常的有帮助,这个时候不用管内层嵌套的控件.因此,我们首先实现往页面里添加一个repeater来列出category的name和description.

  打开datalistrepeaterbasics文件夹里的nestedcontrols.aspx页.添加一个repeater控件,将id设为categorylist..通过它的智能标签,选择创建一个新的名为categoriesdatasource的objectdatasource.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121909386790.jpg
图 2: 创建一个名为categoriesdatasource的objectdatasource

用categoriesbll类的getcategories方法配置o

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121909388076.jpg
图3: 用categoriesbll类的getcategories方法配置objectdatasource

  我们需要切换到源视图来手动输入声明代码指定repeater的template内容.增加一个带<h4>的name和<p>的description的itemtemplate.用<hr>将category分开.在作完这些后,你的页面代码里的repeater和objectdatasource声明语言应该和下面差不多:

<asp:repeater id="categorylist" datasourceid="categoriesdatasource"
 enableviewstate="false" runat="server">
 <itemtemplate>
  <h4><%# eval("categoryname") %></h4>
  <p><%# eval("description") %></p>
 </itemtemplate>
 <separatortemplate>
  <hr />
 </separatortemplate>
</asp:repeater>
<asp:objectdatasource id="categoriesdatasource" runat="server"
 oldvaluesparameterformatstring="original_{0}"
 selectmethod="getcategories" typename="categoriesbll">
</asp:objectdatasource>

图4 表示现在在浏览器里浏览这个页.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121909389361.jpg
图 4:列出每个category的 name 和description , 用水平线隔开

第二步: 增加嵌套的repeater显示product

  下一步我们的任务是在categorylist的itemtemplate里添加一个repeater用来显示属于各个category下的product.有很多方法可以存取内层的repeater数据,我们将探讨两种现在我们在categorylist repeater的itemtemplate里创建product repeater.每个product里将包含name和price我们将下面的标记加到categorylist的itemtemplate里:

<asp:repeater id="productsbycategorylist" enableviewstate="false"
 runat="server">
 <headertemplate>
  <ul>
 </headertemplate>
 <itemtemplate>
  <li><strong><%# eval("productname") %></strong>
   (<%# eval("unitprice", "{0:c}") %>)</li>
 </itemtemplate>
 <footertemplate>
  </ul>
 </footertemplate>
</asp:repeater>

第三步: 将各category下的product绑定到 productsbycategorylist repeater

  如果现在你浏览这个页,你会看到象图4一样的页面,因为我们还没有在repeater里绑定任何数据.有几种方法可以将合适的product记录绑定到repeater里,其中一些会比较有效.现在主要的任务是为指定category取到合适的product.可以通过在itemtemplate里语法声明objectdatasource或者直接在后台代码编程来将数据绑定到内层的repeater.

  通过objectdatasource和itemdatabound来获取数据

  这里我们还是用objectdatasource来实现.productsbll类的getproductsbycategoryid(category)
方法可以返回特定categoryid的products信息.因此,我们将在categorylist repeater的itemtemplate里新建一个objectdatasource,并用这个方法配置它.不幸的,repeater不允许通过设计视图来修改template,因此我们需要手动添加将声明语法.见下面的代码:

<h4><%# eval("categoryname") %></h4>
<p><%# eval("description") %></p>
<asp:repeater id="productsbycategorylist" enableviewstate="false"
  datasourceid="productsbycategorydatasource" runat="server">
 <headertemplate>
  <ul>
 </headertemplate>
 <itemtemplate>
  <li><strong><%# eval("productname") %></strong> -
    sold as <%# eval("quantityperunit") %> at
    <%# eval("unitprice", "{0:c}") %></li>
 </itemtemplate>
 <footertemplate>
  </ul>
 </footertemplate>
</asp:repeater>
<asp:objectdatasource id="productsbycategorydatasource" runat="server"
   selectmethod="getproductsbycategoryid" typename="productsbll">
 <selectparameters>
  <asp:parameter name="categoryid" type="int32" />
 </selectparameters>
</asp:objectdatasource>

  当使用objectdatasource方法时我们需要设置productsbycategorylist repeater的datasourceid为objectdatasource(productsbycategorydatasource).注意objectdatasource有一个<asp:parameter>来指定传给getproductsbycategoryid(categoryid)的categoryid.但是我们怎么来指定这个值呢?我们可以设置defaultvalue属性为<asp:parameter>,见下面的代码:

<asp:parameter name="categoryid" type="int32"
  defaultvalue='<%# eval("categoryid")' />

  不幸的,数据绑定语法只能用在有databinding事件的控件里.parameter类没有这样的事件,因此这样使用会出错.我们需要为categorylist repeater的itemdatabound创建一个事件处理来设置这个值.每个item绑定到repeater时激发itemdatabound事件.因此每次外层的repeater激发这个时间时,我们可以将当前的caegoryid的值传给productsbycategorydatasource objectdatasource的categoryid参数.下面的代码是为categorylist repeater的itemdatabound创建一个event handler:

protected void categorylist_itemdatabound(object sender, repeateritemeventargs e)
{
 if (e.item.itemtype == listitemtype.alternatingitem ||
  e.item.itemtype == listitemtype.item)
 {
  // reference the categoriesrow object being bound to this repeateritem
  northwind.categoriesrow category =
   (northwind.categoriesrow)((system.data.datarowview)e.item.dataitem).row;
  // reference the productsbycategorydatasource objectdatasource
  objectdatasource productsbycategorydatasource =
   (objectdatasource)e.item.findcontrol("productsbycategorydatasource");
  // set the categoryid parameter value
  productsbycategorydatasource.selectparameters["categoryid"].defaultvalue =
   category.categoryid.tostring();
 }
} 
       

  这个event handler首先保证我们操作的是data item而不是header,footer或separator item.然后,引用刚刚绑定到当前repeateritem的categoriesrow实例.最后,引用在itemtemplate里的objectdatasource并将当前repeateritem的categoryid传给categoryid参数.

  在这个event handler里,每个repeateritem里的productsbycategorylist repeater都绑定到repeateritem的category里的product.见图5.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121909381648.jpg
图 5: 外层的repeater 列出每个category; 内层的repeater 列出属于category的products

  直接编程来获取category 下的products

  除了使用objectdatasource来获取当前category下的proudct外,我们还可以在asp.net页的code-behind里(或app_code文件夹里或一个单独的类项目里)来创建一个根据传入的categoryid返回合适的product集的方法.假设在asp.net页的code-behind里有一个名为getproductsincategory(categoryid)方法.我们可以使用这个方法来将当前category下的product绑定到内层的repeater.见下面的代码:

<asp:repeater runat="server" id="productsbycategorylist" enableviewstate="false"
  datasource='<%# getproductsincategory((int)(eval("categoryid"))) %>'>
 ...
</asp:repeater>

  repeater的datasource属性通过绑定语法来指定它的数据是通过getproductsincategory(categoryid)得到.由于eval("categryid")返回的是object类型,我们在它传入getproductsincategory(categoryid)前将它转化成integer.注意这里的categoryid是通过外层repeater(categorylist)的categoryid(已经绑定到categories table)获取的.因此它不可能是一个null值.所以我们在绑定前没有检查.

  我们现在需要创建getproductsincategory(categoryid)方法.在这里简单使用productsbll类的getproductsbycategoryid(categoryid)方法返回的productsdatatable就可以了.我们在nestedcontrols.aspx页的code-behind里创建getproductsincategory(categoryid).见下面的代码:

protected northwind.productsdatatable getproductsincategory(int categoryid)
{
 // create an instance of the productsbll class
 productsbll productapi = new productsbll();
 // return the products in the category
 return productapi.getproductsbycategoryid(categoryid);
}

  这个方法仅仅是创建一个productsbll实例然后返回getproductsbycategoryid(categoryid)方法的返回值.注意这个方法必须标记为public或protected.如果标记为private,asp.net页的声明标记里将不能调用它.
做完以上操作后,在浏览器里浏览页面.页面看起来应该和使用objectdatasource 和itemdatabound event handler方法差不多(图5).

  注意:在asp.net页的code-behind里创建getproductsincategory(categoryid)方法好象只是一个形式,毕竟这个方法只是调用bll里的方法.为什么不直接在内层repeater里的绑定语法里直接调用这个方法.比如:
datasource='<%#productsbll.getproductsbycategoryid(ctype(eval("categoryid"),integer))%>')
虽然这个声明是不起作用的(因为getproductsbycategoryid(categoryid)方法是一个实例方法),你可以修改productsbll来包含一个这样的静态方法.这样的修改可以满足asp.net页的getproductsincategory(categoryid)方法的需要,但是写在code-behind里可以更灵活的获取数据,我们在后面会看到这点.

获取所有的product 信息

  前面两个方法我们通过调用productsbll类的getproductsbycategoryid(categoryid)方法来获取当前category的product(第一种通过objectdatasource,第二种通过getproductsincategory(categoryid)).每次方法被调用时,bll调用dal,dal通过sql查询数据库,返回特定的记录.

  如果有n个category,这个方法会访问数据库n+1次— 一次返回所有的category,n次返回特定category下的product.然而我们可以通过访问数据库两次来获取所有需要的数据— 一次返回所有的category,一次返回所有的product.一旦我们得到所有的product,我们可以根据categoryid来过滤,然后再绑定.

  我们只需要稍微修改asp.net页的code-behind里的getproductsincategory(categoryid)方法来实现这个功能.我们首先来返回所有的product,然后根据传入的categoryid里过滤.

private northwind.productsdatatable allproducts = null;
protected northwind.productsdatatable getproductsincategory(int categoryid)
{
 // first, see if we've yet to have accessed all of the product information
 if (allproducts == null)
 {
  productsbll productapi = new productsbll();
  allproducts = productapi.getproducts();
 }
 // return the filtered view
 allproducts.defaultview.rowfilter = "categoryid = " + categoryid;
 return allproducts;
}

  注意allproducts变量.它在第一次调用getproductsincategory(categoryid)时返回所有product信息.确定allproducts对象被创建后,在根据categoryid来对datatable过滤.这个方法将访问数据库的次数从n+1减少到2次.
这个改进没有修改页面的声明语言.仅仅只是减少了数据库的访问次数.

  注意:可能想当然的觉得减少了数据库访问次数会提高性能.但是这个不一定.如果你有大量的categoryid为null的product,这样使用getproducts方法返回的product有一部分不会被显示.而且如果你只需要显示一部分category的proudct(分页时就是这样),而返回所有的product,这样对资源也是一种浪费.通常对两种技术进行性能分析,唯一正确的方法是设置程序常见的场景来进行压力测试.

总结

  本章我们学习了如何嵌套web控件.通过如何在外层repeater显示各个category,内层repeater显示每个category下的product来作为例子.主要的任务在于获取正确的数据并绑定到内层的web控件上.有很多方法可以使用,我们这里讨论了两种.第一种是使用在外层控件的itemtemplate里objectdatasource来绑定到内层控件.第二种是使用asp.net页的code-behind里的方法.它通过内层控件的datasource属性来绑定.本章使用的控件是repeater,也可以将repeater嵌套在gridview里,或gridview嵌套在datalist里等.

  祝编程快乐!

作者简介

  scott mitchell,著有六本asp/asp.net方面的书,是4guysfromrolla.com的创始人,自1998年以来一直应用 微软web技术。scott是个独立的技术咨询顾问,培训师,作家,最近完成了将由sams出版社出版的新作,24小时内精通asp.net 2.0。他的联系电邮为,也可以通过他的博客与他联系。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网