ASP.NET 绑定:DataGrid 到垂直表






4.42/5 (8投票s)
2005年1月3日
4分钟阅读

146005

1648
一种在 ASP.NET 中将垂直表与 DataGrid 绑定的简单方法

背景
传统上,数据库方法将表表示为记录列表。在编程语言中,这相当于结构的**一维数组(列表)**。
public struct Account {                  // record
    public string Name { get; set; }
    public double Balance { get; set; }
}
public Account[] Accounts;               // table
然而,在某些应用程序中,出于性能和其他原因,将数据结构**转置**为一个列表集合是可行的,其中集合的项是给定列的值列表。在编程语言中,可以表示为:
public class Accounts {                 // vertical table
    public string[] Name;
    public double[] Balance;
}
由列集合组成的数据结构称为**垂直表**。
绑定 DataTable
ASP.NET 中最常见的数据源之一是 DataSet 成员,也称为 DataTable。我们将使用这种类型的绑定作为与其他数据类型进行比较的起点。
代码中的所有示例都包含相同类型的 .aspx 文件以及不同的代码隐藏文件。.aspx 文件的主体包含一个 DataGrid。
<form id="Form1" method="post" runat="server">
    <asp:datagrid id="DataGrid1" runat="server">
    </asp:datagrid>
</form>
代码隐藏类的骨架如下:
注意:只有标记为省略号 (...) 的部分会在数据源之间有所不同,即 DataType、CreateDataSource 和 GenerateColumns。
public class DataSetBind : System.Web.UI.Page
{
    protected System.Web.UI.WebControls.DataGrid DataGrid1;
    protected DataType dt = ... // Particular data source type
    private void CreateDataSource() {
        // ...
    }
    private void GenerateColumns(DataGrid grid) {
        // ...
    }
    private void Page_Load(object sender, System.EventArgs e)
    {
        CreateDataSource();
        DataGrid1.DataSource = dt;
        GenerateColumns(DataGrid1);
        DataBind();
    }
    #region Web Form Designer generated code
    // ...
}
对于 DataTable 源类型,我们通过指定架构并填充行来创建数据源。
protected DataTable dt = new DataTable("ACCOUNT");
private void CreateDataSource() {
    DataColumn col = null;
    
    col = dt.Columns.Add("NAME", typeof(string));
    col.Caption = "Name";
    col = dt.Columns.Add("BALANCE", typeof(double));
    col.Caption = "Balance";
    // Fill data table
    dt.Rows.Add(new object[]{"One", 123.45});
    dt.Rows.Add(new object[]{"Two", 0});
    dt.Rows.Add(new object[]{"Three", -123.45});
}
原则上,这足以绑定到 DataGrid,除非您关心设置与列数据成员名称不同的标题(此处为 Title case 而非全大写)。DataGrid 的 AutoGenerateColumns 属性默认是打开的,但它会忽略 DataColumn 的 Caption。因此,我们需要特别努力手动设置它。
private void GenerateColumns(DataGrid grid) {
    grid.AutoGenerateColumns = false;
    foreach (DataColumn col in dt.Columns) {
        BoundColumn dc = new BoundColumn();
        dc.DataField = col.ColumnName;
        dc.HeaderText = col.Caption;
        grid.Columns.Add(dc);
    }
}
绑定自定义记录数组
在 ASP.NET 中,有一个内置机制可以绑定到自定义记录的 Array 或 ArrayList。这是根据 BaseDataList.DataSource 属性定义中的说明得出的。
我们将使用以下形式的自定义记录:
public struct TestStruct {
    private string _NAME;
    private double _BALANCE;
    [View("Name")]
    public string NAME { get { return _NAME; } }
    [View("Balance")]
    public double BALANCE { get { return _BALANCE; } }
    
    public TestStruct(string NAME, double BALANCE) {
        _NAME = NAME;
        _BALANCE = BALANCE;
    }
}
数据源将是我们 struct 的一个简单数组:
protected TestStruct[] dt = null;
private void CreateDataSource() {
    dt = new TestStruct[]{
                new TestStruct("One", 123.45), 
                new TestStruct("Two", 0),
                new TestStruct("Three", -123.45),
    };
}
使用属性以声明方式指定标题
我们将在此处发挥创意,将标题封装在 struct 的声明中,并在列生成例程中通用地发现它们。首先,我们定义一个自定义属性类,如下所示:
public class ViewAttribute : Attribute {
    protected string _caption = "";
    protected string _format = "";
    
    public ViewAttribute(string caption) : this(caption, "") { }
    public ViewAttribute(string caption, string format) {
        _caption = caption;
        _format = format;
    }
    public string Caption { get { return _caption; } }
    public string Format { get { return _format; } }
}
该属性在上面的 struct 声明中用作 [View("Name")]。
然后,列生成将如下所示:
注意:这样的发现将允许选择性地显示用 View 属性标记的 struct 属性,隐藏所有其他属性,从而为其他用途提供更多属性。
private static void GenerateColumns(DataGrid grid) {
    grid.AutoGenerateColumns = false;
    foreach (PropertyInfo pi in typeof(TestStruct).GetProperties()) {
        ViewAttribute[] attrs = 
            pi.GetCustomAttributes(typeof(ViewAttribute), false) 
                                        as ViewAttribute[];
        if (attrs.Length <= 0)
            continue;
        BoundColumn dc = new BoundColumn();
        dc.DataField = pi.Name;
        dc.HeaderText = attrs[0].Caption;
        dc.DataFormatString = attrs[0].Format;
        grid.Columns.Add(dc);
    }
}
绑定垂直表
对于垂直表,ASP.NET DataGrid 没有像 DataTable 或自定义记录的 Array 和 ArrayList 那样的自动支持。
我们的策略将基于 DataSource 属性的定义,该属性指出数据源必须是实现 System.Collections.IEnumerable 接口的对象。枚举器将遍历记录列表,类似于自定义记录示例,但是记录本身不会是数据的实际容器,而是一个访问器,通过其属性公开实际位于垂直表列中的数据。这种设计模型称为 享元模式(Flyweight Pattern)。
成员是值列数组的 VerticalTable 类将作为 IEnumerable。
public class TestTable : IEnumerable {
    internal string[] _NAME = null;
    internal double[] _BALANCE = null;
    public TestTable(string[] NAME, double[] BALANCE) {
        _NAME = NAME;
        _BALANCE = BALANCE;
    }
    public int Count { get { 
                  return _NAME == null ? 0 : _NAME.Length; }}
    #region IEnumerable Members
    public IEnumerator GetEnumerator() {
        return new TestRecord(this);
    }
    #endregion
}
IEnumerable.GetEnumerator() 将返回我们自定义记录访问器的一个新实例,该实例还将充当自身上的 IEnumerator。它将简单地迭代对所引用垂直表中数组位置的索引。
public class TestRecord : IEnumerator {
    TestTable _testTable = null;
    int _pos = -1;
    [View("Name", "")]
    public string NAME { get { return _testTable._NAME[_pos]; } }
    [View("Balance", "<tt>{0:F2}</tt>")]
    public double BALANCE { get { return _testTable._BALANCE[_pos]; } }
    public TestRecord(TestTable testTable) {
        _testTable = testTable;
    }
    #region IEnumerator Members
    public bool MoveNext() {
        if (_pos >= _testTable.Count - 1)
            return false; 
        _pos++; 
        return true;
    }
    public object Current { get { return this; } }
    public void Reset() { _pos = -1; }
    #endregion
}
请注意,我们如何使用相同的 View 属性进行列生成。只有这样标记的属性才会进入 DataGrid 的列。因此,我们使用(几乎)相同的 GenerateColumns 例程,发现访问器记录的自定义属性。
另外,请注意此处用于设置 DataFormatString 的技巧,通过 View 属性的格式部分,部分使用了格式良好的 HTML(<tt>),DataGrid 渲染器会适当地使用它来获得格式化效果。
创建数据源如下所示:
protected TestTable dt = null;
private void CreateDataSource() {
    dt = new TestTable(
        new string[]{"One", "Two", "Three"},
        new double[]{123.45, 0, -123.45});
}
关注点
此处使用的设计决策用于说明 DataGrid 绑定的目的。在实际情况中,可以进行进一步的增强,例如将自定义记录和垂直表通用化,以动态处理列和记录的数量。
尽管 Windows.Forms 和 Web.UI 之间存在对称性的承诺,但当跳出 DataSet 的标准情况时,DataGrid 绑定工作方式却大不相同。这是因为 Windows.Forms 绑定的双向交互性与 Web.UI 不同。类似地使用 Windows.Forms 绑定将是另一个独立研究的主题。
参考文献
- Mihail Stefanov,将二维数组绑定到 DataGrid
- Tarun Jain, .NET Windows Forms 中的数据绑定概念
- Alastair Stell,DataGrid Zen 初学者
- Neeraj Jain,在 DataGrid 中显示垂直行
- Jan Tielens,通过 Web 服务公开自定义类并将其绑定到 DataGrid
- Marcie Robillard,(Datagrid Girl)分离 DataGrid 项
- omri,DataGrid101 - 使用 Windows.Forms DataGrid
- Dino Esposito,深入了解 .NET Windows Forms 数据绑定
历史
- 2005 年 1 月 2 日:Oleg Kobchenko - 初稿
许可证
本文没有明确的许可证,但可能包含文章文本或下载文件本身的使用条款。如有疑问,请通过下面的讨论区与作者联系。作者可能使用的许可证列表可在此处找到。
