Algorithm for generating a Treeview control node with permission control on the. NET platform

Source: Internet
Author: User
Generate a Treeview control node with permission control on the. NET platformAlgorithm

I. Introduction

In application system development, Treeview is a frequently used control. Its main feature is its ability to clearly implement functions such as classification, navigation, and browsing. As a result, its usage and programming skills have also been concerned by technical staff. As application requirements change, we need to implement permission Control for data display in many cases, that is, the data you see is filtered, continuous, or discrete. As far as Treeview is concerned, the entire set of nodes with strict parent-child relationship may be displayed, and the nodes to be displayed after permission filtering may become discrete, there is no longer a complete inheritance relationship. This article analyzes the existing implementation methods and proposes an improved algorithm. ExampleProgramIt further explains the algorithm design idea.

Ii. Simple Analysis of three common generation methods

As described in [2, 3], there are basically three ways to generate a Treeview:

1. The Treeview designer orCodeDirectly fill in the Treeview node.
In this way, the tree is generated by dragging and dropping controls, and the application scope is narrow. This is a non-programming method;

2. Create a tree structure from the XML file.
This method uses XML files (strings) to generate trees. In terms of form, this method is intuitive. Because XML itself is a "Tree", in the automatically generated code of the Treeview on the. NET platform, the actual content of the Treeview is also represented by XML. In addition, the XML file generation tree is of great significance to distributed applications in heterogeneous environments. In fact, XML has been widely recognized as a common data transmission format;

3. Get data from the database (in. net, we can understand it as a dataset) and create a tree structure.
This method uses the parent-child relationship to recursively generate a tree, which is the easiest way to implement programming. It is generally generated recursively from top to bottom and is widely used.

Here, let's take a practical example to illustrate that we have such a dataset (which can be viewed as a list of departments of a company ):

Tagvalue
Contentvalue
Parentid

G01
Sales Department

G02
Consultant Department

G03
R & D department

G04
Testing Department

Gs01
Marketing Department 1
G01

Gs02
Marketing Department 2
G01

Gs03
Marketing Department 3
G01

Gsl01
Marketing Department 1 Beijing Office
Gs01

Gsl02
Marketing Department 1 Shanghai Office
Gs01

Gs04
Consultant I
G02

Gs05
Consultant Department 2
G02

Gs06
R & D department I
G03

Gs07
R & D department 2
G03

Gs08
Test 1
G04

Gs09
Test Department 2
G04

Gsl03
R & D department, Hangzhou Branch
Gs06

Gsl04
R & D department, Xi'an Branch
Gs06

Table 1 sample dataset

Here, tagvalue is the actual value of the node, contentvalue is the value or label value displayed on the node on the user interface, and parentid is the tagvalue of the parent node of the node. If the node is the root node, it is generally set to null or equal to its tagvalue.

By default, we can assemble all nodes into a tree according to the following algorithm,

Algorithm 1: uses the parent-child relationship recursion to generate a tree basic algorithm

L step 0: Prepare the data and specify the dataset.
Generally, a dataset follows the format (tagvalue, contentvalue, parentid );

L step 1: Specify the node (usually the root node at the initial stage) to be added as the curnode and the parentid value of the node to be added (the parentid of the root node at the initial stage ), as curparentid;

L step 2: Find all nodes with the specified parentid value in the dataset, and obtain the node set objarr [].
If (objarr = NULL)
Return;
Else
{
// Traverse the Node Set
For (INT I = 0; I <objarr. length; I ++)
{
Add objarr [I] As a subnode of curnode, and use recursion (that is, objarr [I] As the curnode, And the tagvalue of objarr [I] As the curparentid, Goto step 1 );
}
}

Finally, we can get the following Treeview:

Figure 1 Treeview

The disadvantage of this method is that the traversal order of "parent node and child node" means that the parent node of each child node must exist. Otherwise, it cannot be searched, which may lead to faults. In many practical applications, we find that this implementation method cannot work completely. The most typical situation is that the actual values (such as the organization list, personnel list, resource list, etc.) for permission control, the nodes in the data set that are usually filtered out from the database may be faulty. For example, if we assume that the given data is set as Table 2 when the permission is set, we will remove the "Sales Department" in the first line (Note: The permission filtering operation is beyond the scope discussed in this article, if the dataset is expected to be ready), use treeview2 generated by algorithm 1.

Tagvalue
Contentvalue
Parentid

G02
Consultant Department

G03
R & D department

G04
Testing Department

Gs01
Marketing Department 1
G01

Gs02
Marketing Department 2
G01

Gs03
Marketing Department 3
G01

Gsl01
Marketing Department 1 Beijing Office
Gs01

Gsl02
Marketing Department 1 Shanghai Office
Gs01

Gs04
Consultant I
G02

Gs05
Consultant Department 2
G02

Gs06
R & D department I
G03

Gs07
R & D department 2
G03

Gs08
Test 1
G04

Gs09
Test Department 2
G04

Gsl03
R & D department, Hangzhou Branch
Gs06

Gsl04
R & D department, Xi'an Branch
Gs06

Table 2 given dataset

Figure 2 Treeview

We can see that node omission occurs here. In general, we can solve the problem from two aspects: on the one hand, we can modify the dataset, and on the other hand, we can modify the Spanning Tree Algorithm. Obviously, directly correcting a dataset is complex and may cause efficiency problems. It is not good to modify the Spanning Tree Algorithm unilaterally (that is, to insert the missing node directly under the root node), because the parent and the younger generation will be at the same level.

3. Generate a tree algorithm through deep serial number Recursion

Review some existing methods (article [1 ~ 5]) the method of generating tree based on node depth gives us some inspiration. We can add depth fields when constructing a data set, but the depth here is not a simple level number, it is an extended concept. Specifically, it is actually a deep number, which corresponds to the parent number. For example, the dataset shown in Table 1 can be numbered as follows:

Tagvalue
Contentvalue
Parentid
Depthid

G01
Sales Department

A001

G02
Consultant Department

A002

G03
R & D department

A003

G04
Testing Department

A004

Gs01
Marketing Department 1
G01
A001001

Gs02
Marketing Department 2
G01
A001002

Gs03
Marketing Department 3
G01
A001003

Gsl01
Marketing Department 1 Beijing Office
Gs01
A001001001

Gsl02
Marketing Department 1 Shanghai Office
Gs01
A001001002

Gs04
Consultant I
G02
A002001

Gs05
Consultant Department 2
G02
A002002

Gs06
R & D department I
G03
A003001

Gs07
R & D department 2
G03
A003002

Gs08
Test 1
G04
A004001

Gs09
Test Department 2
G04
A004002

Gsl03
R & D department, Hangzhou Branch
Gs06
A003001001

Gsl04
R & D department, Xi'an Branch
Gs06
A003001002

Table 3 datasets with deep numbers

Depthid indicates the node depth number. The process of generating deep numbers is not complex. First, we can develop numbering rules, such as the prefix, encoding length, and start value of the hierarchical numbers. When a node number is assigned, you only need to find the maximum number of the node level and Add 1. The specific implementation process is not described here.

As a result, we naturally think of using the idea of algorithm 1 to design a tree-generating program based on deep numbers. In this case, we can find the child node set based on the depth Number of the current node, but we need to provide a maximum span (which can be understood as the interval series between the highest and lowest levels ), because it is impossible to find it without limit. This method can partially compensate for the defect of "traversal by the parent node and the child node", because when a fault occurs, it will continue searching for the fault along the serial number. However, it may still be missed. For example, if we specify a dataset (filter out "R & D ):

Tagvalue
Contentvalue
Parentid
Depthid

G01
Sales Department

A001

G02
Consultant Department

A002

G03
R & D department

A003

G04
Testing Department

A004

Gs01
Marketing Department 1
G01
A001001

Gs02
Marketing Department 2
G01
A001002

Gs03
Marketing Department 3
G01
A001003

Gsl01
Marketing Department 1 Beijing Office
Gs01
A001001001

Gsl02
Marketing Department 1 Shanghai Office
Gs01
A001001002

Gs04
Consultant I
G02
A002001

Gs05
Consultant Department 2
G02
A002002

Gs07
R & D department 2
G03
A003002

Gs08
Test 1
G04
A004001

Gs09
Test Department 2
G04
A004002

Gsl03
R & D department, Hangzhou Branch
Gs06
A003001001

Gsl04
R & D department, Xi'an Branch
Gs06
A003001002

Table 4 given dataset

In the process of generating a tree, when looking for a subnode from the R & D department (a003), we should find the "R & D department 2" (a003002) because it is the nearest node. However, the following sequence is followed by "R & D department 2". Obviously, it is impossible to find "R & D department 1 in Hangzhou" or "R & D department 1 in Xi'an" because the numbering rules are different, the generated tree also misses the node.

We propose a new algorithm that breaks the traditional traversal order and uses the bottom-up traversal order. In an image, the traditional method is to continuously derive new subnodes through a root node or parent node (as shown in 3 (), the new algorithm is to aggregate nodes to form a sub-tree set, which is finally merged into a tree (as shown in 3 (B ).

Figure 3 Treeview node generation process

Algorithm 2: generate a Tree Algorithm Based on the bottom-up (depth) Traversal method

L step 0: Prepare the data. For a given dataset (tagvalue, contentvalue, depthid), tagvalue is the actual value of the node, and contentvalue is the value or tag value displayed by the node, depthid is the node depth number. If the node is the root node, its depthid length is generally the shortest. Given the maximum depth imaxdeplen and the minimum depth imindeplen. Specifies the hashtable used to store the current subtree;

L step 1: specify the level length icurdeplen of the current traversal and set it to imaxdeplen;

L step 2: Find a qualified level based on the given icurdeplen in the dataset, and obtain the node set objarr [] at this level.
If (objarr = NULL)
Return;
Else
{
// Traverse the Node Set
For (INT I = 0; I <objarr. length; I ++)
{
Step 2.1: Find the parent node of objarr [I]. If there is no parent node, add it directly to goto Step 2.2. If there is a parent node, first check whether the parent node is already in hashtable. If any, remove it from hashtable and mark it as tempparnode; otherwise, a new node tempparnode is generated; goto Step 2.3;
Step 2.2 if the current node objarr [I] is not in hashtable, add objarr [I]; Continue to hashtable;
Step 2.3 if the current node objarr [I] is not in hashtable, generate a node tempnode Based on objarr [I]; otherwise, remove it from hashtable and mark it as tempnode; insert tempnode into tempparnode and save it to hashtable.
}
}

L step 3: if the current level of icurdeplen is greater than the minimum level of imindeplen, continue tracing. Reduce icurdeplen by 1 and use it as the current icurdeplen, Goto Step 2; otherwise, Goto step 4.

L step 4: after obtaining the node table stored in hashtable (actually a subtree table), traverse hashtable and insert each subtree into the Treeview.

In this algorithm, the minimum length and maximum length of the node depth number in the dataset are calculated from the beginning to avoid blind search. However, if the depth numbers of each layer in the dataset are fixed and long, the search process can be simplified. The key value of the hashtable that stores the temporary subtree is the tag value of the current subtree node. The advantage is that it is quite convenient to search and does not need to traverse nodes in the Treeview. Therefore, each time you process a node of the previous level, you only need to check that its parent node is not in hashtable. If you are inserting it into the subtree, otherwise the hashtable item is added.

The appendix sample program implements this algorithm. Here we will introduce several key functions.

Function Form and parameter explanation
Function

Populatecompletetree (Ref system. Windows. Forms. Treeview objtreeview, dataset dssource, string strtreecaption, int itagindex, int icontentindex, int idepthindex)

1. objtreeview is the final Treeview to be generated;

2. dssource is a given dataset;

3. strtreecaption specifies the name of the root node of the Treeview;

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. hierarchy (depth) traversal is used to generate the main tree function;

2. Call collectnodes (Dataset dssource, int itagindex, int icontentindex, int idepthindex, int icurdeplen, int imindeplen, ref hashtable objarrnode)

Collectnodes (Dataset dssource, int itagindex, int icontentindex, int idepthindex, int icurdeplen, int imindeplen, ref hashtable objarrnode)

1. dssource, itagindex, icontentindex, and idepthindex are the same as above;

2. icurdeplen indicates the length of the Depth serial number of the current level;

3. imindeplen indicates the minimum depth, that is, the maximum depth number length;

4. objarrnode refers to the hashtable used to store the middle subtree.
1. clustering nodes from the bottom up;

2. Call lookupparentnode (Dataset dssource, int idepthindex, string strsubdepth, int itagindex, int icontentindex)

Lookupparentnode (Dataset dssource, int idepthindex, string strsubdepth, int itagindex, int icontentindex)

1. dssource, itagindex, icontentindex, and idepthindex are the same as above;

2. strsubdepth indicates the depth Number of the current node (because it is a recursive search)
1. Find the nearest upper control level, because the parent node level may not exist.

If a dataset is given (we filter out the "R & D department" and "Marketing Department ),

Tagvalue
Contentvalue
Parentid
Depthid

G01
Sales Department

A001

G02
Consultant Department

A002

G04
Testing Department

A004

Gs02
Marketing Department 2
G01
A001002

Gs03
Marketing Department 3
G01
A001003

Gsl01
Marketing Department 1 Beijing Office
Gs01
A001001001

Gsl02
Marketing Department 1 Shanghai Office
Gs01
A001001002

Gs04
Consultant I
G02
A002001

Gs05
Consultant Department 2
G02
A002002

Gs07
R & D department 2
G03
A003002

Gs08
Test 1
G04
A004001

Gs09
Test Department 2
G04
A004002

Gsl03
R & D department, Hangzhou Branch
Gs06
A003001001

Gsl04
R & D department, Xi'an Branch
Gs06
A003001002

Table 5 given dataset

Shows the Spanning Tree,

Figure 4 Treeview

This is what we need.

Of course, sometimes we also adopt the so-called "neutral" Method for Structural needs. For example, if you do not want to write an algorithm to generate a deep number for the Treeview control node mentioned in this article, you can add a flag to the dataset, the flag is used to identify whether the data has been filtered. After using traditional algorithms to generate a tree, check whether there is any unfiltered data. If there is any, search for its parent node and insert it into the parent node. However, the "Search for ancestor nodes" here is carried out on the Treeview. When there are many nodes, the efficiency is certainly not high.

In addition, the introduction of deep numbers not only facilitates the generation tree, but also makes permission settings more flexible. In our example, if we want to filter out some departments, we will pick out these departments one by one. We call it the "discrete value setting method ". When the system structure is large, we prefer to select a range, for example, filtering out the n-level of a department and its down control. This is a "continuous value setting method ", at this time, the depth number containing the hierarchical concept can solve this problem well. In actual system development, we also find that this method is feasible.

4. Other Treeview generation methods

As mentioned above, Treeview can also be generated through XML files (strings. The key to this implementation is to construct an XML document or string similar to Treeview. The basic idea should be similar to the algorithm discussed earlier, but it is a little more complicated in program implementation (the XML node index can be implemented based on the Document Object Model (DOM ). In addition, there are many third-party Treeview controls that support different XML document formats. This article does not discuss the specific implementation process in detail.

V. Summary

This article mainly discusses the design of the Treeview control node generation program on the. NET platform. Based on the existing methods and actual requirements, it studies the design method and provides a complete solution.

In the specific application of the tree, in addition to the Spanning Tree, node addition, deletion, modification, query, and even node upgrade and downgrade are common. Essentially, these operations involve business-related database operations. Therefore, in the Treeview generated by the "bottom-up (depth) Traversal method, the implementation of these operations is the same as that of the traditional method. The additional operation is nothing more than adding or modifying the depth number. Of course, the actual needs are varied, and the design and analysis of the corresponding algorithms are endless.

References ):

[1] Zane Thomas. dataviewtree for Windows Forms, http://www.abderaware.com/WhitePapers/ datatreeview.htm

[2] Li honggen. Application of tree structure in development, http://www.microsoft.com/china/community/Column/ 21. mspx

Design of Web tree structure program based on Li honggen. NET platform, 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: 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
{< br> ///


/// Summary of treeoperator.
//
public class treeoperator
{< br> Public treeoperator ()
{< br> //
// todo: add the constructor logic here
//
}

///


// uses a hierarchy (depth) tree generated by Traversal method
///
/// Target tree
/// dataset
// tree display name
/// value index
// content index
/// hierarchical index
Public static void populatecompletetree (Ref system. windows. forms. treeview objtreeview, dataset dssource, string strtreecaption, int itagindex, int icontentindex, int idepthindex)
{< br> // traverse from the underlying layer, create a hashtable (with the tag value as the keyword) to store the currently calculated 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 );

// Insert the tree after the node table is obtained
Foreach (Object objnode in objarrnode. values)
{
Treenode objnewnode = new treenode ();
Objnewnode = (treenode) objnode;
Objrootnode. nodes. Add (objnewnode );
}

Objtreeview. nodes. Add (objrootnode );
}

///


// cluster nodes from the bottom up
///
///
//
//
///
///
//
//
private static void collectnodes (Dataset dssource, int itagindex, int icontentindex, int idepthindex, int icurdeplen, int imindeplen, ref hashtable objarrnode)
{< br> // collection node
system. data. dataview DV;
system. windows. forms. treenode tempnode;

// Search for a given layer node
Int I = icurdeplen;
Do
{
DV = new dataview (dssource. Tables [0]);
String strexpr = "Len (TRIM (" + dssource. Tables [0]. Columns [idepthindex]. columnname + ") =" + convert. tostring (I );
DV. rowfilter = strexpr;
I --;
} While (I> = imindeplen & DV. Count <= 0 );
Icurdeplen = I + 1;

# Region layer-by-layer backtracking to collect nodes
Foreach (system. Data. datarowview Drow in DV)
{
// Find the parent node
String [] strarrparentinfo = lookupparentnode (dssource, idepthindex, Drow [idepthindex]. tostring (). Trim (), itagindex, icontentindex );
String strtagvalue = Drow [itagindex]. tostring (). Trim ();
String strcontentvalue = Drow [icontentindex]. tostring ();

// If no parent node exists, add it directly
If (strarrparentinfo = NULL)
{
// The current node is not in hashtable
If (objarrnode [strtagvalue] = NULL)
{
// Add the current node
Tempnode = new treenode (strcontentvalue );
Tempnode. Tag = strtagvalue;
Objarrnode. Add (strtagvalue, tempnode );
}
}
Else // has a parent node. Check whether the parent node is already in hashtable.
{
String strpartagvalue = strarrparentinfo [0]. Trim ();
String strparcontentvalue = strarrparentinfo [1]. Trim ();

// The parent node is already in hashtable.
If (objarrnode [strpartagvalue]! = NULL)
{
// The current node is not in hashtable
If (objarrnode [strtagvalue] = NULL)
{
Tempnode = new treenode (strcontentvalue );
Tempnode. Tag = strtagvalue;
}
Else
{
// Retrieve and remove the node, and insert the parent node
Tempnode = new treenode ();
Tempnode = (treenode) objarrnode [strtagvalue];
Objarrnode. Remove (strtagvalue );
}

// Insert to parent node
Treenode tempparnode = new treenode ();
Tempparnode = (treenode) objarrnode [strpartagvalue];
Tempparnode. nodes. Add (tempnode );
Objarrnode [strpartagvalue] = tempparnode;
}
Else // The 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
{
// Retrieve and remove the node, and insert the parent node
Tempnode = new treenode ();
Tempnode = (treenode) objarrnode [strtagvalue];
Objarrnode. Remove (strtagvalue );
}

// Create a 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

// There are also untraversed Layers
If (icurdeplen> imindeplen)
{
Collectnodes (dssource, itagindex, icontentindex, idepthindex, iCurDepLen-1, imindeplen, ref objarrnode );
}

}

/// <Summary>
/// Find Father's Day
/// </Summary>
/// <Param name = "dssource"> </param>
/// <Param name = "idepthindex"> </param>
/// <Param name = "strsubdepth"> </param>
/// <Param name = "itagindex"> </param>
/// <Param name = "icontentindex"> </param>
/// <Returns> Returns a string array consisting of tag value, content value, and depth value. Otherwise, null is returned. </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;
Do
{
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 );

If (DV. Count <= 0)
{
Return NULL;
}
Else
{
String [] strarr = {DV [0] [itagindex]. tostring (), DV [0] [icontentindex]. tostring (), DV [0] [idepthindex]. tostring ()};
Return strarr;
}
}

/// <Summary>
/// Obtain the maximum depth (depth length)
/// </Summary>
/// <Param name = "dssource"> dataset </param>
/// <Param name = "idepthindex"> deep index (column number) </param>
/// <Returns> maximum depth </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>
/// Obtain the minimum depth value (depth length)
/// </Summary>
/// <Param name = "dssource"> dataset </param>
/// <Param name = "idepthindex"> deep 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;
}

}
}

Contact Us

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.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.