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

自定义 DataGrid 列:自定义 DataGrid BoundColumn,可在 DataGrid 页脚自动显示总和/平均值。并为 DataField 分配自定义数据类型。

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.84/5 (6投票s)

2007年2月14日

9分钟阅读

viewsIcon

36142

downloadIcon

296

我想展示一个示例,其中您可以找到 DataGrid 边界列的自定义设计。通常,我们的基本要求是以高性能的方式在页脚中显示值的总和与平均值。在这里,我创建了一个自定义列,并为其分配了自定义数据类型。

下载源代码 - 20.9 Kb

Sample image

引言


我想展示一个示例,其中您可以找到 DataGrid 边界列的自定义设计。通常,我们的基本要求是以高性能的方式在页脚中显示值的总和与平均值。在此简单的示例应用程序中,我将展示自定义边界列的设计,该设计在页脚中显示总和/平均值。此外,我还将展示如何为 DataField 分配自定义数据类型,例如 Address,它是一个自定义数据类型,包含地区和城市作为字符串数据类型。在本文中,我们将比较常规方法,然后创建我们自己的自定义 DataGrid 列“SumColumn”、“AddressColumn”,它们派生自 BoundColumn,以避免重复相同的代码。

背景


让我们以一个显示员工列表的 DataGrid 为例。列为 EmpID、EmpName、EmpAge、EmpAddress 和 Salary。要显示页脚的总薪资和平均薪资,有多种解决方案。其中一种解决方案是

DataGrid DataItemBound 事件

在这里,我设置了一个简单的 DataGrid,用于显示 Employee 表中的 EmpID、Name 和 Salary。

<asp:DataGrid id="dgEmployee" runat="server" Width="183px" Height="154px" AutoGenerateColumns=False ShowFooter=True>

<HeaderStyle Font-Bold="True" ForeColor="#000033" BackColor="#993366"></HeaderStyle>

<ItemStyle ForeColor="#330099" BackColor="#f0f2f8"></ItemStyle>

<AlternatingItemStyle ForeColor="#330070" BackColor="#d8e4f8"></AlternatingItemStyle>

<FooterStyle ForeColor="#330099" BackColor="#ffffe1"></FooterStyle>

<SelectedItemStyle ForeColor="#663399" BackColor="#d4d0c8"></SelectedItemStyle>

<Columns>

<asp:BoundColumn DataField="EmpID" HeaderText="ID"></asp:BoundColumn>

<asp:BoundColumn DataField="EmpFirstName" HeaderText="Name"></asp:BoundColumn>

<asp:BoundColumn DataField="Salary" HeaderText="Salary"></asp:BoundColumn>

</Columns>

</asp:DataGrid>

然后在代码隐藏中:

private void Page_Load(object sender, System.EventArgs e)

{

DataClass _dataClass = new DataClass();

dgEmployee.DataSource = _dataClass.GetEmpInfo();

dgEmployee.DataBind();

}

private void dgEmployee_ItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)

{

decimal decValue = 0M;

if((e.Item.ItemType == ListItemType.Item) || (e.Item.ItemType == ListItemType.AlternatingItem))

{

decValue = decimal.Parse(e.Item.Cells[2].Text);

_decSum += decValue;

_intCount++;

}

if(e.Item.ItemType == ListItemType.Footer)

{

string s = "Sum: " + _decSum.ToString();

if(_intCount>0)

s += ("<BR>"+"Average:" + Convert.ToString(Math.Round((_decSum/_intCount),2)));

e.Item.Cells[2].Text = s;

}

}

输出

Sample image

注释

上面的代码没有问题。但是,让我们考虑一下有很多列包含数据,并且需要在页脚显示其值的总和。因此,使用自定义 DataGrid 列,我可以避免重复相同的代码。此解决方案提供了代码的可重用性和更高的灵活性。

自定义 SumColumn 和 AddressColumn
解决方案简介

现在是时候揭晓答案了。解决此问题的方法是将所需的逻辑直接内置到自定义 DataGridColumn 中,然后我们可以在任何页面上使用它!不明白?让我来解释一下。我们将创建自己的自定义 DataGrid 列,我们将其称为“SumColumn”、“AddressColumn”,它派生自 DataGrid BoundColumn。由于其基类,新的 SumColumn 类将拥有 BoundColumn 类中已有的所有内置功能。我们只需要实现我们的求和功能。继承 BoundColumn 类这样强大的控件并将新功能添加到它们是 .NET Framework 的面向对象特性之一。

SumColumn 我分配了 decimal 数据字段,而 AddressColumn 我分配了自定义数据类型 Address。自定义数据类型 Address 如下所示

public struct Address

{

private string _strArea;

private string _strCity;

public string Area

{

get

{

return _strArea;

}

set

{

_strArea = value;

}

}

public string City

{

get

{

return _strCity;

}

set

{

_strCity = value;

}

}

}

我创建了一个 Employee 类来存储员工信息:“Employee.cs”

using System;

namespace WebPractice

{

/// <summary>

/// Employee 的摘要说明。

/// </summary>

public class Employee

{

#region constructor

public Employee()

{

//

// TODO: 添加构造函数逻辑

//

}

#endregion

#region custom data type Address

public struct Address

{

private string _strArea;

private string _strCity;

public string Area

{

get

{

return _strArea;

}

set

{

_strArea = value;

}

}

public string City

{

get

{

return _strCity;

}

set

{

_strCity = value;

}

}

}

#endregion

#region Private variable

private int _intEmpID;

private decimal _decAge;

private string _strEmpFirstName;

private string _strEmpLastName;

private decimal _salary;

private Address _Address;

#endregion

#region Public Property

public int EmpID

{

get

{

return _intEmpID;

}

set

{

_intEmpID = value;

}

}

public decimal Age

{

get

{

return _decAge;

}

set

{

_decAge = value;

}

}

public decimal Salary

{

get

{

return _salary;

}

set

{

_salary = value;

}

}

public string EmpFirstName

{

get

{

return _strEmpFirstName;

}

set

{

_strEmpFirstName = value;

}

}

public string EmpLastName

{

get

{

return _strEmpLastName;

}

set

{

_strEmpLastName = value;

}

}

public Address EmpAddress

{

get

{

return _Address;

}

set

{

_Address = value;

}

}

#endregion

}

}

我创建了一个名为“SumColumn.cs”的 SumColumn 类,它派生自 BoundColumn 类。

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.ComponentModel;

namespace WebPractice

{

/// <summary>

/// Summary description for SumColumn.

/// </summary>

public class SumColumn:BoundColumn

{

#region Constructor

public SumColumn()

{

//

// TODO: 添加构造函数逻辑

//

}

#endregion

#region Private Variables

private decimal _decSum = 0M;

private int _intCount = 0;

private bool _showSum = false;

private bool _showAverage = false;

#endregion

#region Public Property

public bool ShowSum

{

get

{

return _showSum;

}

set

{

_showSum = value;

}

}

public bool ShowAverage

{

get

{

return _showAverage;

}

set

{

_showAverage = value;

}

}

#endregion

#region Methods

public override void InitializeCell(TableCell cell, int columnIndex, ListItemType itemType)

{

base.InitializeCell (cell, columnIndex, itemType);

switch(itemType)

{

case ListItemType.Item

cell.DataBinding += new EventHandler(this.CellItemDataBound);

break;

case ListItemType.AlternatingItem

cell.DataBinding += new EventHandler(this.CellItemDataBound);

break;

case ListItemType.Footer

cell.DataBinding += new EventHandler(this.CellItemDataBound);

break;

}

}

private void CellItemDataBound(object s,EventArgs e)

{

TableCell tblCell = (TableCell)s;

DataGridItem dgiAge = (DataGridItem)tblCell.NamingContainer;

decimal _decValue = 0;

switch(dgiAge.ItemType)

{

case ListItemType.Item

_decValue = this.GetUnderlyingValue(dgiAge.DataItem);

_decSum += _decValue;

_intCount++;

break;

case ListItemType.AlternatingItem

_decValue = this.GetUnderlyingValue(dgiAge.DataItem);

_decSum += _decValue;

_intCount++;

break;

case ListItemType.Footer

if(_showSum && _showAverage)

{

tblCell.Text = "Average: " + this.FormatDataValue(_decSum/_intCount)+

"<BR>"+ "Total: " + this.FormatDataValue(_decSum);

}

else if(_showSum)

tblCell.Text = "Total: " + this.FormatDataValue(_decSum);

else if(_showAverage)

tblCell.Text = "Average: " + this.FormatDataValue(_decSum/_intCount);

break;

}

}

protected decimal GetUnderlyingValue(object dataItem)

{

PropertyDescriptor _prpDesc = TypeDescriptor.GetProperties(dataItem).Find(this.DataField,true);

if(_prpDesc == null)

{

throw new HttpException("Field Not Found: " + this.DataField);

}

return decimal.Parse((_prpDesc.GetValue(dataItem)).ToString());

}

#endregion

}

}

我还创建了一个 AddressColumn 类,它派生自 BoundColumn 类,用于如下自定义我的地址,类名为“AddressColumn.cs”。

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.HtmlControls;

using System.ComponentModel;

using System.Text;

namespace WebPractice

{

/// <summary>

/// Summary description for AddressColumn.

/// </summary>

public class AddressColumn:BoundColumn

{

#region Constructor

public AddressColumn()

{

//

// TODO: 添加构造函数逻辑

//

}

#endregion

#region Variables

private string _strCssClass;

#endregion

#region Public Property

public string CssClass

{

get

{

return _strCssClass;

}

set

{

_strCssClass = value;

}

}

#endregion

public override void InitializeCell(TableCell cell, int columnIndex, ListItemType itemType)

{

base.InitializeCell (cell, columnIndex, itemType);

switch(itemType)

{

case ListItemType.Item

cell.DataBinding += new EventHandler(this.CellItemDataBound);

break;

case ListItemType.AlternatingItem

cell.DataBinding += new EventHandler(this.CellItemDataBound);

break;

case ListItemType.Footer

cell.DataBinding += new EventHandler(this.CellItemDataBound);

break;

}

}

#region Methods

private void CellItemDataBound(object s,EventArgs e)

{

TableCell tblCell = (TableCell)s;

DataGridItem dgiAge = (DataGridItem)tblCell.NamingContainer;

Employee.Address _adr = new WebPractice.Employee.Address();

StringBuilder _sbHtmlTag;

switch(dgiAge.ItemType)

{

case ListItemType.Item

_adr = this.GetUnderlyingValue(dgiAge.DataItem);

_sbHtmlTag = new StringBuilder();

if(_adr.Area != null && _adr.Area.Trim() != "")

{

_sbHtmlTag.Append("<div class='" + CssClass + "'>");

_sbHtmlTag.Append("Area: ");

_sbHtmlTag.Append(_adr.Area);

_sbHtmlTag.Append("</div>");

}

if(_adr.City != null && _adr.City.Trim() != "")

{

_sbHtmlTag.Append("<div class='" + CssClass + "'>");

_sbHtmlTag.Append("City: ");

_sbHtmlTag.Append(_adr.City);

_sbHtmlTag.Append("</div>");

}

tblCell.Text = _sbHtmlTag.ToString();

break;

case ListItemType.AlternatingItem

_adr = this.GetUnderlyingValue(dgiAge.DataItem);

_sbHtmlTag = new StringBuilder();

if(_adr.Area != null && _adr.Area.Trim() != "")

{

_sbHtmlTag.Append("<div class='" + CssClass + "'>");

_sbHtmlTag.Append("Area: ");

_sbHtmlTag.Append(_adr.Area);

_sbHtmlTag.Append("</div>");

}

if(_adr.City != null && _adr.City.Trim() != "")

{

_sbHtmlTag.Append("<div class='" + CssClass + "'>");

_sbHtmlTag.Append("City: ");

_sbHtmlTag.Append(_adr.City);

_sbHtmlTag.Append("</div>");

}

tblCell.Text = _sbHtmlTag.ToString();

break;

case ListItemType.Footer

break;

}

}

protected Employee.Address GetUnderlyingValue(object dataItem)

{

PropertyDescriptor _prpDesc = TypeDescriptor.GetProperties(dataItem).Find(this.DataField,true);

if(_prpDesc == null)

{

throw new HttpException("Field Not Found: " + this.DataField);

}

return ((Employee.Address)_prpDesc.GetValue(dataItem));

}

#endregion

}

}

我的 Aspx 页面如下:在此 ItemTemplate 中,我调用一个函数“ConcatnatFirstNameLastName(string,string)”,它将连接员工的名字和姓氏并给出员工姓名。此函数定义在 aspx 页面的代码隐藏中,并且其访问修饰符为 protected,private 无法访问。

<asp:datagridid="dgEmployee"runat="server"ShowFooter="True"AutoGenerateColumns="False" ID="Datagridid1" NAME="Datagridid1">
<FooterStyle ForeColor="#330099"></FooterStyle>
<SelectedItemStyle ForeColor="#663399" </SelectedItemStyle>
<ItemStyle ForeColor="#330099"></ItemStyle>
<HeaderStyle Font-Bold="True" ForeColor="#FFFFCC"></HeaderStyle>
<Columns>

<asp:BoundColumn DataField="EmpID" HeaderText="Emp ID"></asp:BoundColumn>

<asp:TemplateColumn HeaderText="Employee Name">

<ItemTemplate>

<label>

<%# ConcatnatFirstNameLastName((string)DataBinder.Eval(Container.DataItem,"EmpFirstName"),DataBinder.Eval(Container.DataItem,"EmpLastName").ToString()) %>

</label>

</ItemTemplate>

</asp:TemplateColumn>

<Custom:AddressColumn DataField="EmpAddress" HeaderText="Address" CssClass="BlueBlock"></Custom:AddressColumn>

<Custom:SumColumn DataField="Age" ShowAverage="true" DataFormatString="{0:F}" HeaderText="Age"></Custom:SumColumn>

<Custom:SumColumn DataField="Salary" ShowSum="true" ShowAverage="true" DataFormatString="{0:C}" HeaderText="Salary"></Custom:SumColumn>

</Columns>

</asp:datagrid>

在代码隐藏中,我们将在 Page_Load 事件中执行以下操作:

private void Page_Load(object sender, System.EventArgs e)

{

DataClass _dataClass = new DataClass();

dgEmployee.DataSource = _dataClass.GetEmpInfo();

dgEmployee.DataBind();

}

protected string ConcatnatFirstNameLastName(string subject,string teacher)

{

string strResult = "";

if(subject != null)

{

strResult += subject;

}

if(teacher != null)

{

if(strResult != "")

{

strResult += " ";

}

strResult += teacher;

}

return strResult;

}

上述示例 DataClass 只是一个创建虚拟员工数据的类,GetEmpInfo() 方法返回一个包含员工记录的 ArrayList。

我认为上面的代码相当自明。

结论

在这个小例子中,我试图向您展示如何根据我们的要求以结构化且高性能的方式自定义我们的 DataGrid 列。这就是为什么我在这里提供了三个不同的示例,使用三个不同的列:“EmpName”,它通过连接员工的名字和姓氏来显示员工姓名;“Age”和“Salary”,它们是通过我们自己新的自定义列 SumColumn 实现的,该列派生自 BoundColumn,具有在 DataGrid 的页脚中显示列值总和/平均值的功能。这只是一个可重用 DataGrid 列的示例,您可以自行检查自己的应用程序,找出哪些可以很好地封装到自定义 DataGrid 列中。

注意

  • 可下载的代码不适用于生产环境。其唯一目的是激发一个想法:重复的代码可以封装在一个模块中。

参考文献

下载源代码 - 20.9 Kb下载源代码 - 20.9 Kb
© . All rights reserved.