描述 DataGrid 递归版本的文章






4.42/5 (14投票s)
2005年10月13日
4分钟阅读

59456

560
本文探讨了多功能的 asp:DataGrid 的又一个方面,并展示了如何使用它来显示和选择递归数据。
引言
本文探讨了多功能的 asp:DataGrid
的又一个方面,并展示了如何使用它来显示和选择递归数据。此处呈现的用户界面与 TreeView
非常相似,但在当前版本 (1.1) 之前,TreeView
并不是 ASP.NET 附带的标准控件。此外,我不确定即将推出的标准 TreeView
(ASP.NET 2.0) 是否允许选择数据。
问题
您多久会遇到需要呈现给用户并由用户选择的层次化数据?我最近遇到了一个这样的需求,在找到解决方案的过程中,我探索了 DataGrid
的递归实现。如果简化,本文的三个目标是:
- 探讨如何将递归数据设置为控件,以便在用户界面中显示,
- 如何让用户从用户界面选择/取消选择这些数据元素,以及
- 如何以编程方式获取/设置这些数据元素的选中状态。
数据
此处提出的解决方案仅适用于递归数据——其中对象包含其自身类型的对象集合。
我在这里使用的例子是经典的员工-经理关系。一个 Employee
可能有一个 Team
,而 Team
本质上就是零个或多个 Employee
对象的集合。
其他例子可以是门户网站中的项目-类别。一个类别可能有一个子类别集合,其中包含零个或多个类别对象。
解决方案 - “递归”网格
由于数据的本质是递归的,呈现这些数据的解决方案也必须是递归的!让我们看看如何实现上述所有目标。
将递归数据设置为此控件
- 请参考我们对“递归”数据的定义:对象包含其自身类型的对象集合。可以使用简单的“名称”和“集合”属性模式将网格绑定到此递归数据。‘name’是您希望与每个可选项一起显示的属性。‘collection’是代表其自身类型对象集合的对象属性。在我们上面的数据示例中,
Employee::Name
表示‘name’属性,Employee::Team
表示‘collection’属性。 RecursiveGrid
可以通过其DataSource
属性来使用此递归数据。
递归显示数据
- 重写的 ‘
DataBind
’ 方法用于递归显示数据。 - 网格有一个模板列 (
RecursiveColumnTemplate
),它在当前单元格中添加一个Label
(绑定到项名称)和一个新的RecursiveGrid
(绑定到项集合)。 - 两个绑定控件——
Label
和新的RecursiveGrid
——使用它们的DataItem
属性并通过反射调用NamePropertyName
和CollectionPropertyName
。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
(包含Label
和RecursiveCheckedGrid
)。
以编程方式获取/设置这些数据元素的选中状态
- 让用户从网格中选择有什么用,如果无法知道他们选择了什么呢?
- 我也有一个递归的解决方案(您难道不喜欢递归吗?)。如果 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 等)。
- 我在两个方法中使用了这个公式:
SetSelectedIndexes
和GetSelectedIndexes
。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
:这是表示“集合”属性的属性名称。
此解决方案的一些扩展/修改...
SetSelectedIndexes
和GetSelectedIndexes
方法可以修改为返回/接受选定的对象,而不是它们的索引。- 在某些情况下,如果父项被选中,则希望自动选中子项。这可以通过在
RecursiveCheckedGrid
中的CheckBox
列上使用客户端脚本来实现。
结语
通过对该解决方案的介绍,我希望您能够将此网格用于您的项目中。我欢迎您对此主题提出的所有建议/反馈。
历史
- 2005年10月13日:创建。
- 2005年10月20日:添加了
SetSelectedIndexes
和GetSelectedIndexes
方法的代码。