ASP.NET 父/子(分层)关系树状视图用户控件开发






4.94/5 (45投票s)
自定义TreeView Web 用户控件,
引言
对于有 ASP.NET 经验的人来说,用户控件的开发非常简单。Treeview
是 ASP.NET 中已有的一个控件。它主要用于创建数据树。它可以用来根据父/子数据表构建无限深度的树状数据。
背景
在本演示中,我将展示如何在 ASP.NET 中创建一个树状视图用户控件,该控件可以通过设置一些属性,根据父/子关系数据表构建数据树。
入门
- 打开 SQL Server 2005。
- 设计一个名为
Department
的表,其结构如下:
在上面的表结构中,IsActive
是一个位字段,仅在您希望激活或禁用该记录时使用。默认情况下,在向此表中插入每条记录时,通过赋予值 1 来激活它。
在 Department
表中插入一些父级和相关的子级数据。您可以参考下图。
让我们看看在本演示的 Department
表中,我们如何在 SQL 表中找到父记录下的子记录。这在您的实际情况中可能会有所不同。您还需要识别父记录。实际上,记录在这里指的是单个 Department
。单个部门可能有子部门,在这种情况下,顶层的 Department
是父部门或记录,而其下方的分层直接子部门或记录就是子部门或记录。分层下方的部门或记录被称为其上方直接分层部门的子级。这就是父子关系(分层)。通过这种机制,在一个具有上述 department
表结构的表中,您可能会拥有无限数量的父子记录。这种关系是通过表中自连接在主键和每个记录(在本例中是每个 department
)的父键之间维护的。
您可以通过检查父 ID 列中的 null
来找到层次结构中的最顶层父级。对于我们的 department
表,可以通过以下 SQL 查询获得:
SELECT * FROM Department WHERE Departmentidparent IS NULL
查询的输出
您可以通过以下 SQL 查询找到父记录的子记录:
SELECT * FROM Department WHERE Departmentidparent=1
结果是:
我们将把上述 SQL 操作应用到我们的控件创建代码中,以构建父/子关系记录的树状视图。
在 Visual Studio 2010 中创建 ASP.NET 网站
- 创建一个网站
- 添加 **Web 用户控件**
添加以下代码来创建控件代码隐藏页中的一些属性,以便可以从使用此树状视图用户控件的网页中进行配置。这些属性用于:
DataSource
- 父/子关系数据的来源DisplayMember
- 用于树叶节点ValueMember
- 保存显示成员的键值ParentMember
- 父成员是子数据的父数据KeyMember
- 键成员是父/子关系数据的主键
#region Property
public DataTable DataSource
{
get;
set;
}
public String DisplayMember
{
get;
set;
}
public String ValueMember
{
get;
set;
}
public String ParentMember
{
get;
set;
}
public String KeyMember
{
get;
set;
}
#endregion
在控件的代码隐藏页中添加方法。要将父/子关系数据填充到树状视图控件中,您需要编写递归方法,如下面的代码所示。GetChildNode
是一个递归方法,它会一直调用,直到数据源中存在父子关系,并且它以父 ID 作为参数。PopulateTree
是递归方法 GetChildNode
的起始点。实际上,PopulateTree
通过将父 ID 作为参数调用 GetChildNode
来启动递归。请记住,父 ID 是当前行的主键。
GetChildNode
方法构建一个节点集合(TreeNodeCollection),直到最后一个子节点,并将其返回给 PopulateTree
,以便它可以绑定到 Tree View 控件。
它通过过滤已加载到其中的数据源来查找子级,并为父 ID 构建条件。
CommonLoad
是一个调用 PopulateTree
方法的方法。最终,由加载事件调用。
在 Web 用户控件的代码隐藏页中添加 PopulateTree
方法。首先,您需要检查数据源中是否存在父/子关系数据。
private void PopulateTree(TreeView objTreeView)
{
if (DataSource != null)
{
}
}
使用 foreach
循环从上到下遍历所有记录。
foreach (DataRow dataRow in DataSource.Rows)
{
}
现在检查数据源中的第一个父记录。因此,对于所有从上到下遍历的记录,第一个父级是其父成员值为 null
的记录。
if (dataRow[ParentMember] == DBNull.Value)
{
}
将第一个父级作为根节点添加到树状视图中。
TreeNode treeRoot = new TreeNode();
treeRoot.Text = dataRow[DisplayMember].ToString();
treeRoot.Value = dataRow[ValueMember].ToString();
treeRoot.ImageUrl = "~/Images/Folder.gif";
treeRoot.ExpandAll();
objTreeView.Nodes.Add(treeRoot);
现在,让我们创建一个递归方法,并在递归方法 GetChildNode
完成后回到 PopulateTree
。我们都知道递归是一种可以调用自身直到满足某个条件的方法。GetChildNode
也是一个类似的递归方法,但它接受一个参数,该参数是用于查找该父级下的子级的父成员,然后由 PopulateTree
方法传递。最终,GetChildNode
方法返回一个 TreeNodeCollection,它是所有父节点及其下相关子节点的集合。
private TreeNodeCollection GetChildNode(long parentid)
{
}
现在进入 GetChildNode
方法。创建一个本地变量 TreeNodeCollecton
,以便您可以临时保存节点集合,最后将其返回。
TreeNodeCollection childtreenodes = new TreeNodeCollection();
根据参数(即父成员)查找子记录。为此,您需要过滤由网页中的 DataSource
属性设置的数据源。因此,您需要将 DataSource 属性转换为 DataView
,以便可以使用 data view 的 filter 属性对其进行过滤。请注意,DataTable 没有 filter 属性。
DataView dataView1 = new DataView(DataSource);
String strFilter = "" + ParentMember + "=" + parentid.ToString() + "";
dataView1.RowFilter = strFilter;
成功过滤后,您将仅获得参数传递过来的父成员下的子级。现在需要将这些子节点添加到作为参数传入的父节点下。
TreeNode childNode = new TreeNode();
childNode.Text = dataRow[DisplayMember].ToString();
childNode.Value = dataRow[ValueMember].ToString();
childNode.ImageUrl = "~/Images/oInboxF.gif";
childNode.ExpandAll();
在添加每个子节点时,您需要通过以父 ID 作为参数递归调用 GetChildNode
方法来为其检查子节点。在这种情况下,子记录的主键就是父 ID。如果存在子节点,则将它们作为子节点添加到当前作为父节点的节点下。
foreach (TreeNode cnode in GetChildNode(Convert.ToInt64(dataRow[KeyMember])))
{
childNode.ChildNodes.Add(cnode);
}
最后,将这些节点添加到 TreeNodeCollection
中。
childtreenodes.Add(childNode);
最后,当遍历完成时,返回 TreeNodeCollection
。
return childtreenodes;
现在让我们回到 PopulateTree
。您将获得所有节点的集合,并且需要将所有节点添加到树状视图中。到目前为止,请按照以下代码操作:
foreach (TreeNode childnode in GetChildNode(Convert.ToInt64(dataRow[KeyMember])))
{
treeRoot.ChildNodes.Add(childnode);
}
全部集合
#region Method
private void CommonLoad()
{
PopulateTree(treeView);
}
private void PopulateTree(TreeView objTreeView)
{
if (DataSource != null)
{
foreach (DataRow dataRow in DataSource.Rows)
{
if (dataRow[ParentMember] == DBNull.Value)
{
TreeNode treeRoot = new TreeNode();
treeRoot.Text = dataRow[DisplayMember].ToString();
treeRoot.Value = dataRow[ValueMember].ToString();
treeRoot.ImageUrl = "~/Images/Folder.gif";
treeRoot.ExpandAll();
objTreeView.Nodes.Add(treeRoot);
foreach (TreeNode childnode in GetChildNode
(Convert.ToInt64(dataRow[KeyMember])))
{
treeRoot.ChildNodes.Add(childnode);
}
}
}
}
}
private TreeNodeCollection GetChildNode(long parentid)
{
TreeNodeCollection childtreenodes = new TreeNodeCollection();
DataView dataView1 = new DataView(DataSource);
String strFilter = "" + ParentMember + "=" + parentid.ToString() + "";
dataView1.RowFilter = strFilter;
if (dataView1.Count > 0)
{
foreach (DataRow dataRow in dataView1.ToTable().Rows)
{
TreeNode childNode = new TreeNode();
childNode.Text = dataRow[DisplayMember].ToString();
childNode.Value = dataRow[ValueMember].ToString();
childNode.ImageUrl = "~/Images/oInboxF.gif";
childNode.ExpandAll();
foreach (TreeNode cnode in GetChildNode
(Convert.ToInt64(dataRow[KeyMember])))
{
childNode.ChildNodes.Add(cnode);
}
childtreenodes.Add(childNode);
}
}
return childtreenodes;
}
#endregion
在控件的代码隐藏页中添加以下事件。
#region event
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
CommonLoad();
}
}
#endregion
这样,父/子树状视图 Web 用户控件就创建好了。您可以通过设置一些属性在网站中按需使用它。
在网站项目中添加一个测试页面。并将该控件放置在测试页的 aspx 页面中。
<%@ Register Src="~/Controls/ParentChildTreeView.ascx" TagName="VTSTreeView"
TagPrefix="uc1" %>
<body>
<form id="form1" runat="server">
<uc1:VTSTreeView id="VTSTreeView1" runat="server"/>
</form>
</body>
在测试页的代码隐藏页中添加以下代码。并配置您在此页面中放置的树状视图用户控件。
首先,您需要为基础 SQL 表创建数据源。在本例中是 Department
表。并为 VTSTreeView1
控件设置以下属性,例如:
VTSTreeView1.DataSource = dataSet.Tables[0]
- 数据表作为数据源VTSTreeView1.KeyMember =DepartmentId
- 主键VTSTreeView1.DisplayMember =Name
- 树叶节点VTSTreeView1.ValueMember = DepartmentId
- 树叶节点的值VTSTreeView1.ParentMember = DepartmentIdParent
- 当前树叶节点的父 ID。
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
CommonLoad();
}
private void CommonLoad()
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings
["ApplicationServices"].ToString()))
{
SqlDataAdapter dataAdapter = new SqlDataAdapter
("SELECT * FROM Department WHERE IsActive = 1 ", conn);
DataSet dataSet = new DataSet();
dataAdapter.Fill(dataSet);
VTSTreeView1.DataSource = dataSet.Tables[0];
VTSTreeView1.KeyMember = "DepartmentId";
VTSTreeView1.DisplayMember = "Name";
VTSTreeView1.ValueMember = "DepartmentId";
VTSTreeView1.ParentMember = "DepartmentIdParent";
}
}
运行应用程序。您将在浏览器中看到以下输出:
结论
本演示将帮助您构建用于父/子(分层)关系数据的应用程序。