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

描述 DataGrid 递归版本的文章

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.42/5 (14投票s)

2005年10月13日

4分钟阅读

viewsIcon

59456

downloadIcon

560

本文探讨了多功能的 asp:DataGrid 的又一个方面,并展示了如何使用它来显示和选择递归数据。

The expected output in a webpage

引言

本文探讨了多功能的 asp:DataGrid 的又一个方面,并展示了如何使用它来显示和选择递归数据。此处呈现的用户界面与 TreeView 非常相似,但在当前版本 (1.1) 之前,TreeView 并不是 ASP.NET 附带的标准控件。此外,我不确定即将推出的标准 TreeView (ASP.NET 2.0) 是否允许选择数据。

问题

您多久会遇到需要呈现给用户并由用户选择的层次化数据?我最近遇到了一个这样的需求,在找到解决方案的过程中,我探索了 DataGrid 的递归实现。如果简化,本文的三个目标是:

  • 探讨如何将递归数据设置为控件,以便在用户界面中显示,
  • 如何让用户从用户界面选择/取消选择这些数据元素,以及
  • 如何以编程方式获取/设置这些数据元素的选中状态。

数据

此处提出的解决方案仅适用于递归数据——其中对象包含其自身类型的对象集合。

我在这里使用的例子是经典的员工-经理关系。一个 Employee 可能有一个 Team,而 Team 本质上就是零个或多个 Employee 对象的集合。

The data

其他例子可以是门户网站中的项目-类别。一个类别可能有一个子类别集合,其中包含零个或多个类别对象。

解决方案 - “递归”网格

由于数据的本质是递归的,呈现这些数据的解决方案也必须是递归的!让我们看看如何实现上述所有目标。

将递归数据设置为此控件

  • 请参考我们对“递归”数据的定义:对象包含其自身类型的对象集合。可以使用简单的“名称”和“集合”属性模式将网格绑定到此递归数据。‘name’是您希望与每个可选项一起显示的属性。‘collection’是代表其自身类型对象集合的对象属性。在我们上面的数据示例中,Employee::Name 表示‘name’属性,Employee::Team 表示‘collection’属性。
  • RecursiveGrid 可以通过其 DataSource 属性来使用此递归数据。

递归显示数据

  • 重写的 ‘DataBind’ 方法用于递归显示数据。
  • 网格有一个模板列 (RecursiveColumnTemplate),它在当前单元格中添加一个 Label(绑定到项名称)和一个新的 RecursiveGrid(绑定到项集合)。
  • 两个绑定控件——Label 和新的 RecursiveGrid——使用它们的 DataItem 属性并通过反射调用 NamePropertyNameCollectionPropertyName
    • Label 将自身绑定到 InvokeMember(NamePropertyName)
      void BindLabelData(Object sender, EventArgs e)
      {
          if (_namePropertyName != string.Empty)
          {
              Label label = (Label)sender;
              object dataGridItem = 
                ((DataGridItem)(label).NamingContainer).DataItem;
              Type dataGridItemType = dataGridItem.GetType();
      
              label.Text = (string) dataGridItemType.InvokeMember(
                  _namePropertyName, 
                  System.Reflection.BindingFlags.GetProperty,
                  null,
                  dataGridItem,
                  null);
          }
      }
    • 同样,RecursiveGrid 控件将自身绑定到 InvokeMember(CollectionPropertyName)
      void BindGridData(Object sender, EventArgs e)
      {
          if (_collectionPropertyName != string.Empty)
          {
              RecursiveCheckedGrid grid = (RecursiveCheckedGrid)sender;
              object dataGridItem = 
                ((DataGridItem)(grid).NamingContainer).DataItem;
              Type dataGridItemType = dataGridItem.GetType();
      
              grid.DataSource = dataGridItemType.InvokeMember(
                  _collectionPropertyName, 
                  System.Reflection.BindingFlags.GetProperty,
                  null,
                  dataGridItem,
                  null);
          }
      }

从用户界面选择/取消选择这些数据元素

  • 为了满足此要求,我提出了 RecursiveCheckedGrid。这(除了是一个花哨的术语之外!)只是 RecursiveGrid 概念的扩展——它在每个 RecursiveColumnTemplate 列中都有一个 CheckBox 单元格。此复选框旨在让用户从用户界面中选择/取消选择项目。
  • 正如您所猜测的,这个新列也是一个模板。它被称为 CheckBoxColumnTemplate
  • 因此,我们的 RecursivecheckedGrid 有两个模板列——一个称为 CheckBoxColumnTemplate,另一个称为 RecursiveColumnTemplate(包含 LabelRecursiveCheckedGrid)。

以编程方式获取/设置这些数据元素的选中状态

  • 让用户从网格中选择有什么用,如果无法知道他们选择了什么呢?
  • 我也有一个递归的解决方案(您难道不喜欢递归吗?)。如果 Sn 表示网格 (n) 中选定元素的列表,则 Sn 可以通过以下递归公式得出:
    n = {n, Schildren of Grid-n}
    (if Grid-n has children)
    
    Sn = {n}
    (if Grid-n has no children)
  • {} 在这里表示数字列表 (n)。请注意,“n”是一个带小数点的数字(0, 0.1, 1, 1.1.1, 2 等)。

    The expected output in a webpage

  • 我在两个方法中使用了这个公式:SetSelectedIndexesGetSelectedIndexes
    public void SetSelectedIndexes(string indices)
    {
        ArrayList indicesArray = new ArrayList(indices.Split('.'));
    
        //find the Nth item.
        //N is represented by 0th position in the passed string
        DataGridItem item = Items[Convert.ToInt32(indicesArray[0])];
    
        if (item != null)
        {
            if (indicesArray.Count == 1)
            {
                Control foundControl = item.FindControl("SelectorCheckBox");
                CheckBox checkBox = foundControl as CheckBox;
                if ( checkBox != null )  
                    checkBox.Checked = true;
            }
            else
            {
                indicesArray.RemoveAt(0);
    
                Control foundControl = item.FindControl("RecursiveGrid");
                RecursiveCheckedGrid grid = foundControl as RecursiveCheckedGrid;
                if ( grid != null ) 
                    grid.SetSelectedIndexes(String.Join(".", 
                        (string[])indicesArray.ToArray(typeof(string))));
            }
        }
    }
    private string[] GetSelectedIndexes(string previousLevel)
    {
        ArrayList selectedIndexList = new ArrayList();
        foreach( DataGridItem item in Items ) 
        {
            Control foundControl;
    
            //check whether the current item is checked
            foundControl = item.FindControl("SelectorCheckBox");
            CheckBox checkBox = foundControl as CheckBox;
            if ( checkBox != null && checkBox.Checked )  
                selectedIndexList.Add( previousLevel + item.ItemIndex.ToString() );
    
                //recursively, check if the children are checked
                foundControl = item.FindControl("RecursiveGrid");
                RecursiveCheckedGrid grid = foundControl as RecursiveCheckedGrid;
                if ( grid != null ) 
                    selectedIndexList.AddRange(grid.GetSelectedIndexes
                        (previousLevel + item.ItemIndex.ToString() + "."));
        }
    
        return (string[])selectedIndexList.ToArray(typeof( string ) );
    }

探索示例...

  • 下载 Code.zip(从上面的链接)。
  • 将“Code”文件夹的内容复制到您的本地 wwwroot 位置。如果需要,可以重命名它。
  • 创建一个指向您在 wwwroot 中创建的文件夹的 IIS 虚拟目录。
  • 在 Visual Studio IDE 中,打开 wwwroot 位置的 Web 项目。
  • 运行!

在您的项目中使用的解决方案...

您只需要 RecursiveCheckedGrid 类。需要为 RecursiveCheckedGrid 设置的三个属性是:

  • DataSource:这是根数据对象。
  • NamePropertyName:这是表示“名称”属性的属性名称(应与每个数据项一起显示)。
  • CollectionPropertyName:这是表示“集合”属性的属性名称。

此解决方案的一些扩展/修改...

  • SetSelectedIndexesGetSelectedIndexes 方法可以修改为返回/接受选定的对象,而不是它们的索引。
  • 在某些情况下,如果父项被选中,则希望自动选中子项。这可以通过在 RecursiveCheckedGrid 中的 CheckBox 列上使用客户端脚本来实现。

结语

通过对该解决方案的介绍,我希望您能够将此网格用于您的项目中。我欢迎您对此主题提出的所有建议/反馈。

历史

  • 2005年10月13日:创建。
  • 2005年10月20日:添加了 SetSelectedIndexesGetSelectedIndexes 方法的代码。
© . All rights reserved.