treeview| Control | control | Algorithm ONE, INTRODUCTION
In application system development, the TreeView is a control that uses a high frequency. Its main feature is to be able to achieve a more clear classification, navigation, browsing and other functions. Therefore, the use of its methods and programming skills have been the attention of technical personnel. As application requirements change, in many cases we need to implement the control of the data display, that is, the data that the user sees is filtered, or contiguous, or some discrete value. In the case of the TreeView, it is possible to display a complete set of nodes with a strict parent-child relationship, and the nodes to be displayed after permission filtering may become discrete and no longer have a complete inheritance relationship. In this paper, an improved algorithm is proposed to solve this problem by analyzing the existing implementation methods. The attached sample program further explains the algorithm design idea.
Simple analysis of two or three kinds of common generation methods
As described in [2,3], there are basically three ways in which the TreeView is generated:
1. Interface design to populate the TreeView node directly in the TreeView designer or code.
This way, by dragging and dropping the control to generate the tree, the application scope is narrow, is a kind of non programming way;
2. Create a tree structure from the XML file.
This approach is more intuitive in form of XML file (string) spanning trees. Because XML itself is a tree, in the automatic generation Code of the TreeView under the. NET platform, the actual content of the TreeView is represented by XML. In addition, XML file spanning tree is very important for distributed application in heterogeneous environment. In fact, the use of XML as a universal data transfer format has been universally recognized;
3. Get the data from the database (in. NET, we can understand as a dataset, and build a tree structure.
This way, the recursive generation of trees through parent-child relationships is the easiest way to understand the programming implementation. Generally, top-down recursive generation is widely used.
Here, let's give a practical example to illustrate that we have such a dataset (which can be seen as a list of departments of a company):
Tagvalue
Contentvalue
ParentID
G01
Marketing Department
G02
Consulting Department
G03
Research and Development Department
G04
Testing Department
GS01
Marketing a Department
G01
GS02
Marketing Department Two
G01
GS03
Marketing Department Three
G01
GSL01
Marketing of a Beijing office
GS01
GSL02
Marketing a Shanghai office
GS01
GS04
Consultant
G02
GS05
Consultant Department Two
G02
GS06
Develop a
G03
GS07
Research and Development Two department
G03
GS08
Test A
G04
GS09
Test Unit Two
G04
GSL03
Research and development of a Hangzhou branch
GS06
GSL04
Develop an XI ' an branch
GS06
Table 1 Sample datasets
Where the tagvalue is the actual value of the node, Contentvalue is the value of the node displayed on the user interface or the label value, ParentID is the tagvalue of the node's parent node. If the node is the root node, generally set ParentID to be empty or equal to Tagvalue.
By default, we can assemble all the nodes into a tree according to the following algorithm,
Algorithm 1: Generating tree basic algorithm by parent-child relationship recursion
L Step 0: Data preparation, given data sets.
Generally, a dataset follows such a format, that is, (Tagvalue,contentvalue,parentid);
L Step 1: Given the node to be added (usually the root node at initial time), the Curnode, and the ParentID value of the node to be added (the initial parentid of the root node), is recorded as Curparentid;
L Step 2: Find all nodes in the dataset with the specified ParentID value, get the node set objarr[],
if (Objarr = null)
Return
Else
{
Traversal node Set
for (int i=0; i<objarr.length;i++)
{
Add Objarr[i] As a curnode child node, and recursively (soon Objarr[i] as Curnode,objarr[i tagvalue as Curparentid,goto step 1);
}
}
Finally you can get the TreeView as shown in the following illustration:
Figure 1 TreeView Effect Chart
The disadvantage of this approach is that the traversal order of "parent and child nodes" means that the parent node of each child node must exist, otherwise it will not be searched, that is, there may be a fault phenomenon. In many practical applications, we found that this implementation can not be fully effective, the most typical case is when the nodes are represented by the actual values (such as the list of institutions, personnel lists, resource lists, etc.) to control the rights, then often filtered from the database node will appear in the data set fault phenomenon. For example, we assume that when you set permissions on a given data such as table 2, that is, the first line of the "marketing department" to remove (note: Permission filtering operation is beyond the scope of this article, where the data set is already quasi-good), then use algorithm 1 generated TreeView as shown in Figure 2
Tagvalue
Contentvalue
ParentID
G02
Consulting Department
G03
Research and Development Department
G04
Testing Department
GS01
Marketing a Department
G01
GS02
Marketing Department Two
G01
GS03
Marketing Department Three
G01
GSL01
Marketing of a Beijing office
GS01
GSL02
Marketing a Shanghai office
GS01
GS04
Consultant
G02
GS05
Consultant Department Two
G02
GS06
Develop a
G03
GS07
Research and Development Two department
G03
GS08
Test A
G04
GS09
Test Unit Two
G04
GSL03
Research and development of a Hangzhou branch
GS06
GSL04
Develop an XI ' an branch
GS06
Table 2 Given data sets
Figure 2 TreeView Effect Chart
As you can see, this creates a node omission. In general, we can solve the problem from two aspects, on the one hand can be fixed data sets, on the other hand, can modify the spanning tree algorithm. It is obvious that the direct correction of datasets is complex and can bring about efficiency problems. And the unilateral modification of the spanning tree algorithm is not very good (that is, the missing node directly into the root node), because there will be parents and the younger sibling phenomenon.
Third, the tree algorithm by the depth number recursive generation
Reviewing some of the existing methods (text [1~5]), the method based on the node depth spanning tree gives us some inspiration, we can increase the depth field when we construct the dataset, but the depth here is not a simple level number, it is an extended concept, specifically a depth number, There is a certain correspondence between it and the parents number. For example, the dataset shown in table 1 can be numbered as follows:
Tagvalue
Contentvalue
ParentID
Depthid
G01
Marketing Department
a001
G02
Consulting Department
A002
G03
Research and Development Department
A003
G04
Testing Department
a004
GS01
Marketing a Department
G01
a001001
GS02
Marketing Department Two
G01
a001002
GS03
Marketing Department Three
G01
a001003
GSL01
Marketing of a Beijing office
GS01
a001001001
GSL02
Marketing a Shanghai office
GS01
a001001002
GS04
Consultant
G02
a002001
GS05
Consultant Department Two
G02
a002002
GS06
Develop a
G03
a003001
GS07
Research and Development Two department
G03
a003002
GS08
Test A
G04
a004001
GS09
Test Unit Two
G04
a004002
GSL03
Research and development of a Hangzhou branch
GS06
a003001001
GSL04
Develop an XI ' an branch
GS06
a003001002
Table 3 data sets with depth numbering
Where Depthid is the depth number of the node. The process of generating depth numbering is actually not complicated, first we can set number rules, such as the prefix of the hierarchy number, encoding length, starting value, and so on. When you number a node, just find the maximum number of the hierarchy and then increase by 1. The implementation process is no longer detailed here.
Therefore, we naturally think of the idea of using algorithm 1 to design a tree based on depth numbering program. At this point, we can find the descendant node set based on the depth number of the current node, but give a maximum span (which can be understood as the interval series between the highest and lowest levels) because it is impossible to find it indefinitely. This method can partly compensate for the "parent node and child node" traversal of the defect, because when the fault occurs along the number will continue to look backward. But it can still be missed, like our given dataset ("Research and development" filtered out):
Tagvalue
Contentvalue
ParentID
Depthid
G01
Marketing Department
a001
G02
Consulting Department
A002
G03
Research and Development Department
A003
G04
Testing Department
a004
GS01
Marketing a Department
G01
a001001
GS02
Marketing Department Two
G01
a001002
GS03
Marketing Department Three
G01
a001003
GSL01
Marketing of a Beijing office
GS01
a001001001
GSL02
Marketing a Shanghai office
GS01
a001001002
GS04
Consultant
G02
a002001
GS05
Consultant Department Two
G02
a002002
GS07
Research and Development Two department
G03
a003002
GS08
Test A
G04
a004001
GS09
Test Unit Two
G04
a004002
GSL03
Research and development of a Hangzhou branch
GS06
a003001001
GSL04
Develop an XI ' an branch
GS06
a003001002
Table 4 Given data sets
In the spanning tree process, when looking for a a003 from the "Research and Development Department" (R), it should be found that "R two" (A003002) because it is the nearest node. The following sequence is to follow the "research and development two" and then look down, it is obviously impossible to find "research and development of a Hangzhou branch" and "development of an XI ' an division", because the number sequence is not the same, so that the resulting tree will also miss the node.
We propose a new algorithm, that is, to break the traditional traversal order, using a bottom-up traversal sequence. Figuratively speaking, the traditional approach is to continuously derive a new child node (as shown in Figure 3 (a)) through an existing root node or parent node. The new algorithm is to create a subtree by constantly aggregating nodes, and finally to converge into a tree (shown in Figure 3 (b)).
Fig. 3 Diagram of the TreeView node generation process
Algorithm 2: The tree algorithm is generated from bottom up by hierarchy (depth) Traversal method
L Step 0: Data preparation, given data set (Tagvalue,contentvalue,depthid), Tagvalue is the actual value of the node, Contentvalue is the value of the node display or tag value, Depthid is the depth number of the node. If the node is the root node, the Depthid length is the shortest. Given maximum depth imaxdeplen and minimum depth imindeplen. Given the Hashtable used to store the current subtree;
L Step 1: Given the current traversal of the hierarchy length Icurdeplen, initially set to Imaxdeplen;
L Step 2: In the DataSet, find the level of the satisfying condition according to the given Icurdeplen, and get the node set objarr[],
if (Objarr = null)
Return
Else
{
Traversal node Set
for (int i=0; i<objarr.length;i++)
{
Step 2.1 finds the parent of objarr[i], if no parent node, join directly, goto step 2.2; If there is a parent node, first find whether the parent node is already in Hashtable. If so, it is removed from the Hashtable and recorded as Tempparnode; otherwise the new node is generated Tempparnode;goto step 2.3;
Step 2.2 If the current node Objarr[i] is not in the Hashtable, add objarr[i];continue to the Hashtable;
Step 2.3 If the current node Objarr[i] is not in the Hashtable, the node Tempnode is generated according to Objarr[i], otherwise it is removed from the Hashtable and recorded as Tempnode The tempnode is inserted into the tempparnode and will be deposited in Hashtable.
}
}
L Step 3: If the current level of Icurdeplen is greater than the minimum level imindeplen, then continue backtracking, will icurdeplen minus 1 and as the current Icurdeplen,goto step 2; otherwise goto step 4.
L Step 4: After getting the table of nodes stored with Hashtable (actually a subtree), traverse the Hashtable and insert each Shang tree into the TreeView.
In this algorithm, we calculate the minimum length and maximum length of the node depth number in the dataset in the beginning, so as to not blindly search. However, if the depth number of each level in the dataset is fixed, you can simplify the search process. The key value of the Hashtable that holds the temporary subtree is the tag value of the current child tree root node, and the advantage is that it is convenient to find and does not need to traverse a node in the TreeView. So, each time you process the node at the previous level, you can add the hashtable item by simply looking at its parent node in the Hashtable, or inserting it into a subtree.
The Appendix example program implements this algorithm, and here is a few key functions.
function form and its parameter explanation
Function
1. Objtreeview is the final TreeView to be generated;
2. Dssource is a given dataset;
3. strtreecaption specifies the name of the TreeView root node;
4. Itagindex is the column number of the Tagvalue field in the dataset;
5. Icontentindex is the column number of the Contentvalue field in the dataset;
6. Idepthindex is the column number of the Depthid field in the dataset;
1. Using hierarchical (depth) traversal method to generate the tree keynote function;
2. Strsubdepth refers to the depth number of the current node (because it is a recursive lookup)
1. Find the closest control level because it is possible that the parent node hierarchy does not exist.
At this point, if a given dataset (we filter out "research and development" and "marketing"),
Tagvalue
Contentvalue
ParentID
Depthid
G01
Marketing Department
a001
G02
Consulting Department
A002
G04
Testing Department
a004
GS02
Marketing Department Two
G01
a001002
GS03
Marketing Department Three
G01
a001003
GSL01
Marketing of a Beijing office
GS01
a001001001
GSL02
Marketing a Shanghai office
GS01
a001001002
GS04
Consultant
G02
a002001
GS05
Consultant Department Two
G02
a002002
GS07
Research and Development Two department
G03
a003002
GS08
Test A
G04
a004001
GS09
Test Unit Two
G04
a004002
GSL03
Research and development of a Hangzhou branch
GS06
a003001001
GSL04
Develop an XI ' an branch
GS06
a003001002
Table 5 Given data sets
The spanning tree is shown in the following illustration,
Figure 4 TreeView Effect Chart
This is the result we need.
Of course, sometimes we will take the so-called "neutral" approach for structural needs. For example, for the TreeView control node generation problem mentioned in this article, if you do not want to write algorithms to generate depth numbers, then we can also by adding a bit to the DataSet method, that is, using a flag bit to identify whether the data has been filtered. After using the traditional algorithm to generate the tree, then check to see if there are unfiltered data, and if so, find the ancestor node, insert it into the ancestor node. But the "Find ancestor node" Here is done on the TreeView, and when the nodes are many, their efficiency is definitely not high enough to search the dataset directly.
In addition, the introduction of depth numbering will not only bring convenience to the spanning tree, but also make the permissions set more flexible. Specifically to our example, if we want to filter out some departments, then we will take these departments one by one, we call it "discrete value set way." And when the structure of the system is large, we would prefer to select a range, such as a department and its control of the N-level filter out, this is a "continuous value set", then contains the hierarchy of the depth of the concept number can be a good solution to this problem. In the actual system development, we also found that the use of this approach is feasible.
Iv. other ways of TreeView generation
The TreeView can also be generated from the XML file (string) as mentioned earlier. The key to implementing this approach is to construct a TreeView-like XML document or string. The basic idea should be similar to the algorithms discussed earlier, but only slightly more complex in the implementation of the program (where the index of an XML node can be based on the Document Object Model (DOM)). Also note that there are a lot of third-party TreeView controls, and the format of the XML documents they support is different. Limited to space, this article does not discuss the specific implementation process in detail.
V. Summary
This article mainly discusses. NET platform, the design method of TreeView control node is designed, and the method is studied, and a complete solution is given.
In the concrete application of the tree, in addition to spanning the tree, the node increase, delete, change, check even the node upgrade and downgrade are very common. In essence, these operations are related to the operation of the database operations, so in the "bottom-up hierarchy (depth) traversal" generated in the TreeView, these operations are implemented in accordance with the traditional method, the extra action is nothing more than adding or modifying the depth number. Of course, the actual demand is changeable, the corresponding algorithm design and analysis is endless.
Reference documents (Reference):
[1] Zane Thomas. Dataviewtree for Windows forms,http://www.abderaware.com/whitepapers/datatreeview.htm
[2] Lee Honggen. Application of tree-shaped structure in development, http://www.microsoft.com/china/community/Column/21.mspx
[3] Lee Honggen. . NET platform, the design of Web tree structure, http://www.microsoft.com/china/community/Column/30.mspx
[4] Don schlichting. Populating the TreeView control from a Database, http://www.15seconds. Com/issue/030827.htm
[5] How to to:populate a TreeView control from a Dataset in Visual Basic. NET, Http://support. microsoft.com/?kbid=320755
[6] Scott Mitchell. displaying XML Data in the Internet Explorer TreeView control,http://aspnet. 4guysfromrolla.com/articles/051403-1.aspx
-------------
Source code:
Using System;
Using System.Data;
Using System.Windows.Forms;
Using System.Collections;
Namespace Poptreeview
{
<summary>
Summary description of the treeoperator.
</summary>
public class Treeoperator
{
Public Treeoperator ()
{
//
TODO: Add constructor logic here
//
}
<summary>
Using hierarchical (depth) traversal method to generate trees
</summary>
<param name= "Objtreeview" > Target tree </param>
<param name= "Dssource" > DataSet </param>
<param name= "strtreecaption" > Tree display name </param>
<param name= "Itagindex" > Value index </param>
<param name= "Icontentindex" > Content index </param>
<param name= "Idepthindex" > Hierarchy index </param>
public static void Populatecompletetree (ref System.Windows.Forms.TreeView Objtreeview,dataset dssource,string Strtreecaption,int itagindex,int icontentindex,int Idepthindex)
{
From the bottom start traversal, open up a hashtable (with the tag value as the keyword), the current calculation of the node
ObjTreeView.Nodes.Clear ();
int imaxlen = Getmaxdepthlen (Dssource,idepthindex);
int iminlen = Gettopdepthlen (Dssource,idepthindex);
Hashtable Objarrnode = new Hashtable ();
Collectnodes (Dssource,itagindex,icontentindex,idepthindex,imaxlen,iminlen,ref objArrNode);
TreeNode Objrootnode = new TreeNode (strtreecaption);
After you get the node table, insert the tree
foreach (Object Objnode in Objarrnode.values)
{
TreeNode Objnewnode = new TreeNode ();
Objnewnode = (TreeNode) objnode;
OBJROOTNODE.NODES.ADD (Objnewnode);
}
If no parent node, join directly
if (Strarrparentinfo = null)
{
The current node is not in Hashtable
if (objarrnode[strtagvalue]==null)
{
Add current node
Tempnode = new TreeNode (strcontentvalue);
Tempnode.tag = Strtagvalue;
Objarrnode.add (Strtagvalue,tempnode);
}
}
else//has a parent node to find if the parent node is already in Hashtable
{
String strpartagvalue = Strarrparentinfo[0]. Trim ();
String strparcontentvalue = Strarrparentinfo[1]. Trim ();
The parent node is already in the Hashtable
if (objarrnode[strpartagvalue]!= null)
{
The current node is not in Hashtable
if (objarrnode[strtagvalue]==null)
{
Tempnode = new TreeNode (strcontentvalue);
Tempnode.tag = Strtagvalue;
}
Else
{
Remove and remove the node, and then insert the parent node
Tempnode = new TreeNode ();
Tempnode = (TreeNode) objarrnode[strtagvalue];
Objarrnode.remove (Strtagvalue);
}
Insert into parent node
TreeNode Tempparnode = new TreeNode ();
Tempparnode = (TreeNode) objarrnode[strpartagvalue];
TEMPPARNODE.NODES.ADD (Tempnode);
Objarrnode[strpartagvalue] = Tempparnode;
}
else//parent node is not in Hashtable
{
The current node is not in Hashtable
if (objarrnode[strtagvalue]==null)
{
Tempnode = new TreeNode (strcontentvalue);
Tempnode.tag = Strtagvalue;
}
Else
{
Remove and remove the node, and then insert the parent node
Tempnode = new TreeNode ();
Tempnode = (TreeNode) objarrnode[strtagvalue];
Objarrnode.remove (Strtagvalue);
}
Create the parent node and insert the current node into the parent node
TreeNode Tempparnode = new TreeNode (strparcontentvalue);
Tempparnode.tag = Strpartagvalue;
TEMPPARNODE.NODES.ADD (Tempnode);
Objarrnode.add (Strpartagvalue,tempparnode);
}
}
}
#endregion
And a layer that's not traversed.
if (Icurdeplen>imindeplen)
{
Collectnodes (Dssource,itagindex,icontentindex,idepthindex,icurdeplen-1,imindeplen,ref objArrNode);
}
}
<summary>
Find Father Node
</summary>
<param name= "Dssource" ></param>
<param name= "Idepthindex" ></param>
<param name= "Strsubdepth" ></param>
<param name= "Itagindex" ></param>
<param name= "Icontentindex" ></param>
<returns> find returns an array of strings consisting of tag values, content values, and depth values, otherwise return null</returns>
private static string[] Lookupparentnode (DataSet dssource,int idepthindex,string strsubdepth,int itagindex,int Icontentindex)
{
System.Data.DataView DV;
int isublen = Strsubdepth.length;
if (isublen<=1)
{
return null;
}
int i=1;
Todo
{
DV = new DataView (dssource.tables[0]);
String strexpr = "TRIM (" +dssource.tables[0]. Columns[idepthindex]. columnname+ ") = '" +strsubdepth.substring (0,isublen-i) + "'";
Dv. RowFilter = strexpr;
i++;
}while (I<isublen && dv. count<=0);
<summary>
Get maximum depth value (length of depth)
</summary>
<param name= "Dssource" > DataSet </param>
<param name= "Idepthindex" > Depth index (column number) </param>
<returns> Maximum Depth value </returns>
private static int Getmaxdepthlen (DataSet dssource,int idepthindex)
{
DataRowCollection Objrowcol = dssource.tables[0]. Rows;
int iMax = Objrowcol[0][idepthindex]. ToString (). Trim (). Length;
foreach (DataRow objrow in Objrowcol)
{
int icurlen = Objrow[idepthindex]. ToString (). Trim (). Length;
if (Imax<icurlen)
{
IMax = Icurlen;
}
}
return iMax;
}
<summary>
Get minimum depth value (length of depth)
</summary>
<param name= "Dssource" > DataSet </param>
<param name= "Idepthindex" > Depth index (column number) </param>
<returns> Minimum depth value </returns>
private static int Gettopdepthlen (DataSet dssource,int idepthindex)
{
DataRowCollection Objrowcol = dssource.tables[0]. Rows;
int imin = Objrowcol[0][idepthindex]. ToString (). Trim (). Length;
foreach (DataRow objrow in Objrowcol)
{
int icurlen = Objrow[idepthindex]. ToString (). Trim (). Length;
if (Imin>icurlen)
{
Imin = Icurlen;
}
}
return imin;
}
}
}
-----------
Memo: Originally I wanted to publish in the publication, the length is very long, here has been abridged. Welcome correction, Communication!
The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion;
products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the
content of the page makes you feel confusing, please write us an email, we will handle the problem
within 5 days after receiving your email.
If you find any instances of plagiarism from the community, please send an email to:
info-contact@alibabacloud.com
and provide relevant evidence. A staff member will contact you within 5 working days.