



















渐层光棒
不喜欢GridView控件单调的Header区、单调的选取光棒吗?这里有个小技巧可以让你的GridView控件看起来与众不同,请先准备两张图形。
图4-8-50(4-71.tif)
这种渐层图形可以用Photoshop或PhotoImpact轻易做出来,接着将这两个图形文件加到项目的Images目录中,左边取名为titlebar.gif、右边取名为gridselback.gif,然后开启一个新网页,组态SqlDataSource控件连结到任一数据表,再加入GridView控件系结至此SqlDataSource控件,接着将Enable Selection打勾,切换至网页Source页面,加入CSS的程序代码。
程序4-8-10
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="GrandientSelGrid.aspx.cs" Inherits="GrandientSelGrid" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<style type="text/css">
.grid_sel_back
{
background-image:url(Images/gridselback.gif);
background-repeat:repeat-x
}
.title_bar
{
background-image:url(Images/titlebar.gif);
background-repeat:repeat-x
}
</style>
完成后切回设计页面,设定GridView控件的SelectedRowStyle及HeaderStyle属性。
图4-8-51(4-72.tif)
完成后执行网页,你会见到很不一样的GridView。
图4-8-52(4-73.tif)
2 Footer or 2 Header
前面曾提及,GridView控件并没有限制我们只能在里面加入一个Footer,因此我们可以透过程序4-8-11的方式,添加另一个Footer至GridView控件中。
程序4-8-11
protected void GridView1_PreRender(object sender, EventArgs e)
{
//if no-data in datasource,GridView will not create ChildTable.
if (GridView1.Controls.Count > 0 && GridView1.Controls[0].Controls.Count > 1)
{
GridViewRow row2 = new GridViewRow(-1, -1,
DataControlRowType.Footer, DataControlRowState.Normal);
TableCell cell = new TableCell();
cell.Text = "Footer 2";
cell.Attributes["colspan"] = GridView1.Columns.Count.ToString(); //merge columns
row2.Controls.Add(cell);
GridView1.Controls[0].Controls.AddAt(GridView1.Controls[0].Controls.Count - 1, row2);
}
}
相同的,同样的方法也可以用于添加另一个Header至GridView控件中,这个范例看起来无用,但是却给了无限的想象空间,这是实现GridView Insert及Collapsed GridView功能的基础。
Group Header
想合并Header中的两个字段为一个吗?很简单!只要在RowCreated事件中将欲被合并的字段移除,将另一字段的colspan设为2即可。
程序4-8-12
protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.Header)
{
e.Row.Cells.RemoveAt(3);
e.Row.Cells[2].Attributes["colspan"] = "2";
e.Row.Cells[2].Text = "Contact Information";
}
}
图4-8-53为执行画面。
图4-8-53(4-74.tif)
我想,应该不需要我再解释2这个数字从何而来了吧。 ^_^
Group Row
想将同值的字段合成一个吗?4-8-13的程序代码可以帮你达成。
程序4-8-13
private void PrepareGroup()
{
int lastSupID = -1;
GridViewRow currentRow = null;
List<GridViewRow> tempModifyRows = new List<GridViewRow>();
foreach (GridViewRow row in GridView1.Rows)
{
if (row.RowType == DataControlRowType.DataRow)
{
if (currentRow == null)
{
currentRow = row;
int.TryParse(row.Cells[2].Text, out lastSupID);
continue;
}
int currSupID = -1;
if (int.TryParse(row.Cells[2].Text, out currSupID))
{
if (lastSupID != currSupID)
{
currentRow.Cells[2].Attributes["rowspan"] = (tempModifyRows.Count+1).ToString();
currentRow.Cells[2].Attributes["valign"] = "center";
foreach (GridViewRow row2 in tempModifyRows)
row2.Cells.RemoveAt(2);
lastSupID = currSupID;
tempModifyRows.Clear();
currentRow = row;
lastSupID = currSupID;
}
else
tempModifyRows.Add(row);
}
}
}
if (tempModifyRows.Count > 0)
{
currentRow.Cells[2].Attributes["rowspan"] = (tempModifyRows.Count + 1).ToString();
currentRow.Cells[2].Attributes["valign"] = "center";
foreach (GridViewRow row2 in tempModifyRows)
row2.Cells.RemoveAt(2);
}
}
protected void GridView1_PreRender(object sender, EventArgs e)
{
PrepareGroup();
}
这段程序代码应用了先前所提过的GridViewRow控件及TableCell的使用方式,图4-8-54为执行结果。
图4-8-54
Master-Detail GridView
Master-Detail,也就是主明细表的显示,是数据库应用常见的功能,运用DataSource及GridView控件可以轻易做到这点,请建立一个网页,加入两个GridView控件,一名为GridView1,用于显示主表,二名为GridView2,用于显示明细表,接着加入两个SqlDataSource控件,一个连结至Northwind数据库的Orders数据表,另一个连结至Order Details资料表,于连结至Order Details资料表的SqlDataSource中添加WHERE条件来比对OrderID字段,值来源设成GridView1的SelectedValue属性。
图4-8-61
接下来请将GridView1的DataSoruce设为Orders的SqlDataSource,GridView2的DataSource设为Order Details的SqlDataSource,最后将GridView1的Enable Selection打勾即可完成Master-Detail的范例。
图4-8-62
那这是如何办到的呢?当使用者点选GridView1上某笔数据的Select连结时,GridView1的SelectedValue属性便会设成该笔数据的DataKeyName属性所指定的字段值,而连结至Order Details的SqlDataSource又以该属性做为比对OrderID字段时的值来源,结果便成了,使用者点选了Select连结,PostBack发生,GridView2向连结至Order Details的SqlDataSource索取资料,该SqlDataSource以GridView1.SelectedValue做为比对OrderID字段的值,执行选取数据的SQL指令后,该结果集便是GridView1所选取那笔数据的明细了。
Master-Detail GridView Part 2
前面的Master-Detail GridView控件应用,相信你已在市面上的书、或网络上见过,但此节中的GridView控件应用包你没看过,但一定想过!请见图4-8-63。
图4-8-63
图4-8-64
你一定很想惊呼?这是GridView吗??不是第三方控件的效果吧?是的!这是GridView控件,而且只需要不到100行程序代码!!请先建立一个UserControl:DetailsGrid.ascx,加入一个SqlDataSource控件连结至Northwind的Order Details数据表,选取所有字段,接着在WHERE区设定如图4-8-65的条件。
图4-8-65
接着加入一个GridView控件系结至此SqlDataSource控件,并将Enable Editing打勾,然后于原始码中键入4-8-17的程序代码。
程序4-8-17
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class DetailsGrid : System.Web.UI.UserControl
{
public int OrderID
{
get
{
object o = ViewState["OrderID"];
return o == null ? -1 : (int)o;
}
set
{
ViewState["OrderID"] = value;
SqlDataSource1.SelectParameters[0].DefaultValue = value.ToString();
}
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
接着建立一个新网页,加入SqlDataSource控件系结至Northwind的Orders数据表,然后加入一个GridView控件,并于其字段编辑器中加入一个TemplateField,于其内加入一个LinkButton控件,设定其属性如图4-8-66。
图4-8-66
然后设定LinkButton的DataBindings如图4-8-67。
图4-8-67
然后于原始码中键入4-8-18的程序代码。
程序4-8-18
using System;
using System.Collections.Generic;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class CollapseGridView : System.Web.UI.Page
{
private List<int> _collaspedRows = new List<int>();
private List<GridViewRow> _delayAddRows = new List<GridViewRow>();
private bool RowIsCollasped(GridViewRow row)
{
if(_collaspedRows.Count > 0)
return _collaspedRows.Contains((int)GridView1.DataKeys[row.RowIndex].Value);
return false;
}
private void CreateDetailRow(GridViewRow gridRow)
{
if (RowIsCollasped(gridRow))
{
GridViewRow row = new GridViewRow(gridRow.RowIndex, -1,
DataControlRowType.DataRow, DataControlRowState.Normal);
TableCell cell = new TableCell();
row.Cells.Add(cell);
TableCell cell2 = new TableCell();
cell2.Attributes["colspan"] = (GridView1.Columns.Count - 1).ToString();
Control c = LoadControl("DetailsGrid.ascx");
((DetailsGrid)c).OrderID = (int)GridView1.DataKeys[gridRow.RowIndex].Value;
cell2.Controls.Add(c);
row.Cells.Add(cell2);
_delayAddRows.Add(row);
}
}
protected void Page_Load(object sender, EventArgs e)
{
}
protected override void LoadViewState(object savedState)
{
Pair state = (Pair)savedState;
base.LoadViewState(state.First);
_collaspedRows = (List<int>)state.Second;
}
protected override object SaveViewState()
{
Pair state = new Pair(base.SaveViewState(), _collaspedRows);
return state;
}
}
接下来在TemplateField中的LinkButton的Click事件中键入4-8-19的程序代码。
程序4-8-19
protected void LinkButton1_Click(object sender, EventArgs e)
{
LinkButton btn = (LinkButton)sender;
int key = int.Parse(btn.CommandArgument);
if (_collaspedRows.Contains(key))
{
_collaspedRows.Remove(key);
GridView1.DataBind();
}
else
{
_collaspedRows.Clear(); // clear.
_collaspedRows.Add(key);
GridView1.DataBind();
}
}
最后在GridView控件的RowCreated、PageIndexChanging事件中键入4-8-20的程序代码。
程序4-8-20
protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
if(e.Row.RowType == DataControlRowType.DataRow)
CreateDetailRow(e.Row);
else if (e.Row.RowType == DataControlRowType.Pager && _delayAddRows.Count > 0)
{
for (int i = 0; i < GridView1.Rows.Count; i++)
{
if (RowIsCollasped(GridView1.Rows[i]))
{
GridView1.Controls[0].Controls.AddAt(GridView1.Rows[i].RowIndex + 2,
_delayAddRows[0]);
_delayAddRows.RemoveAt(0);
}
}
}
}
protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
_collaspedRows.Clear();
}
执行后你就能看到前图的效果了,那具体是如何做到的呢?从前面的说明,我们知道了可以在GridView控件中动态的插入一个 GridViewRow控件,而GridViewRow控件可以拥有多个Cell,每个Cell可以拥有子控件,那么当这个子控件是一个UserControl呢 ?相信说到这份上,读者已经知道整个程序的运行基础及概念了,剩下的细节如LoadViewState、SaveViewState皆已在前面章节提过,看懂这个范例后!你应该也想到了其它的应用了(UserControl中放DetailsView、FormView、MultiView,哈!),对于GridView!你已经毫无疑问了!
4-8-4、GridView的效能
OK,GridView控件功能很强大,但是如果你仔细思考下GridView控件的分页是如何做的,会发现她的做法其实隐含着一个很大的效能问题,GridView控件在分页功能启动的情况下,会建立一个PageDataSource对象,由这个对象负责向DataSource索取数据,于索取数据时一并传入DataSourceSelectArgument对象,此对象中便包含了起始的列及需要的列数,看起来似乎没啥问题吗?其实不然,当DataSource控件不支持分页时,PageDataSource对象只能以该DataSource所传回的数据来做分页,简略的说!SqlDataSource控件是不支持分页的,这时PageDataSource会要求SqlDataSource控件传回资料,而SqlDataSource控件就用SelectQuery中的SQL指令向数据库要求数据,结果便是,当该SQL指令选取100000笔数据时,SqlDataSource所传回给PageDataSource的资料也是100000笔!!这意味着,GridView每次做数据系结显示时,是用100000笔资料在分页,不管显示的是几笔,存在于内存中的都是100000笔!如果同时有10个人、100个人在使用此网页,可想而知Server的负担有多重了,即使有Cache加持,一样会有100000笔数据在内存中!以往在ASP.NET 1.1时,可以运用DataGrid控件的CustomPaging功能来解决此问题,但GridView控件并未提供这个功能,我们该怎么处理这个问题呢?在提出解决方案前,我们先谈谈GridView控件为何将这么有用的功能移除了?答案很简单,这个功能已经被移往DataSource控件了,这是因为DataSource控件所需服务的不只是GridView,FormView、DetailsView都需要她,而且她们都支持分页,如果将CustomPaging直接做在这些控件上,除了控件必须有着重复的程序代码外,设计师于撰写分页程序时,也需针对不同的控件来处理,将这些移往DataSource控件后,便只会有一份程序代码。说来好听,那明摆着SqlDataSource控件就不支持分页了,那该如何解决这个问题了,答案是ObjectDataSource,这是一个支持分页的DataSource控件,只要设定几个属性及对数据提供者做适当的修改后,便可以达到手动分页的效果了。请建立一个WebiSte项目,添加一个DataSet连结到Northwind的Customers资料表,接着新增一个Class,档名为NorthwindCustomersTableAdapter.cs,键入4-8-26的程序代码。
程序4-8-26
using System;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace NorthwindTableAdapters
{
public partial class CustomersTableAdapter
{
[System.ComponentModel.DataObjectMethodAttribute(
System.ComponentModel.DataObjectMethodType.Select, true)]
public virtual Northwind.CustomersDataTable GetData(int startRowIndex, int maximumRows)
{
this.Adapter.SelectCommand =
new System.Data.SqlClient.SqlCommand("SELECT {COLUMNS} FROM " +
"(SELECT {COLUMNS},ROW_NUMBER() OVER(ORDER BY {SORT}) As RowNumber FROM {TABLE} {WHERE}) {TABLE} " + "WHERE RowNumber > {START} AND RowNumber < {FETCH_SIZE}",Connection);
this.Adapter.SelectCommand.CommandText = this.Adapter.SelectCommand.CommandText.Replace("{COLUMNS}", "*");
this.Adapter.SelectCommand.CommandText = this.Adapter.SelectCommand.CommandText.Replace("{TABLE}", "Customers");
this.Adapter.SelectCommand.CommandText = this.Adapter.SelectCommand.CommandText.Replace("{SORT}", "CustomerID");
this.Adapter.SelectCommand.CommandText = this.Adapter.SelectCommand.CommandText.Replace("{WHERE}", "");
this.Adapter.SelectCommand.CommandText = this.Adapter.SelectCommand.CommandText.Replace("{START}", startRowIndex.ToString());
this.Adapter.SelectCommand.CommandText = this.Adapter.SelectCommand.CommandText.Replace("{FETCH_SIZE}", (startRowIndex+maximumRows).ToString());
Northwind.CustomersDataTable dataTable = new Northwind.CustomersDataTable();
this.Adapter.Fill(dataTable);
return dataTable;
}
public virtual int GetCount(int startRowIndex, int maximumRows)
{
SqlCommand cmd = new System.Data.SqlClient.SqlCommand("SELECT COUNT(*) AS TOTAL_COUNT FROM Customers", Connection);
Connection.Open();
try
{
return (int)cmd.ExecuteScalar();
}
finally
{
Connection.Close();
}
}
}
}
此程序提供了两个函式,GetData函式需要两个参数,一个是起始的笔数,一个是选取的笔数,利用这两个参数加上SQL Server 2005新增的RowNumber,便可以向数据库要求传回特定范围的数据。那这两个参数从何传入的呢?当GridView向ObjectDataSource索取数据时,便会传入这两个参数,例如当GridView的PageSize是10时,第1页时传入的startRowIndex便是10,maximumRows就是10,以此类推。第二个函式是GetCount,对于GridView来说,她必须知道系结数据的总页数才能显示Pager区,而此总页数必须由资料总笔数算出,此时GridView会向PageDataSource要求资料的总笔数,而PageDataSource在DataSource控件支持分页的情况下,会要求其提供总笔数,这时此函式就会被呼叫了。大致了解这个程序后,回到设计页面,加入一个ObjectDataSource控件,于SELECT页次选取带startRowIndex及maximumRows参数的函式。
图4-8-68
按下Next按纽后,精灵会要求我们设定参数值来源,请直接按下Finish来完成组态。
图4-8-69
接着设定ObjectDataSource的EnablePaging属性为True,SelectCountMethod属性为GetCount(对应了程序4-8-26中的GetCount函式),最后放入一个GridView控件,系结至此ObjectDataSource后将Enable Paging打勾,就完成了手动分页的GridView范例了。
图4-8-70
在效能比上,手动分页的效能在数据量少时绝对比Cache来的慢,但数据量大时,手动分页的效能及内存耗费就一定比Cache来的好。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。