I spent several hours sifting through article after article about how to build tree navigations dynamically. They assumed that the order of the data coming in was 'orderly' and that you were usually using the IE Web Control toolkit which is a .Net 1.0 package. In our environment 2.0+, that would not work for us and we needed to ensure browser compatibility.
Thanks to a colleague's suggestion of recursion, I came up with the below code. It's length is due to all the detailed commenting. Feel free to comment on it...any positives or negatives? It seems to work well, and we have yet to find a way to break it.
The code assumes you have a page called 'default.aspx' and that you have added one treeview to it labeled TreeView1. You can substitute your sql server info as necessary in the code behind:
using System;
using System.Data;
using System.Configuration;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using DW_SQL_ReportingService_TST; //Substitute your web reference to your SQL Server Reporting Services
public partial class _Default : System.Web.UI.Page
{
ReportingService objRepSrvc;
CatalogItem[] objCatalogItems;
protected static char pathSeparator = '/';
protected static char[] pathSeparatorArray = { pathSeparator };
protected static string pathSeparatorString = new string(pathSeparator, 1);
protected void Page_Load(object sender, EventArgs e)
{
GetReportStructure();
}
/// <summary>
/// Instantiate a new ReportingService object with specific credentials. Then provide the
/// report path to the ListChildren method. This will return a collection of CatalogItems containing
/// your report links.
/// </summary>
private void GetReportStructure()
{
objRepSrvc = new ReportingService();
objRepSrvc.Credentials = new System.Net.NetworkCredential("userid", "password", "domainname");
/* We subtract one from the ReportPath variable here since it ends with a forward slash. Our path
* statement must be /dirA/DirB - without the ending slash
* In addition, we also set the recursive flag to True indicating we want all subdirectories
*/
objCatalogItems = objRepSrvc.ListChildren(ReportPath.Remove(ReportPath.Length - 1), true);
BuildTree(objCatalogItems);
}
private void GetReportStructure(string path)
{
objRepSrvc = new ReportingService();
}
/// <summary>
/// This method begins the process of putting our report paths into a usable form.
/// </summary>
/// <param name="objCatalogItems"></param>
private void BuildTree(CatalogItem[] objCatalogItems)
{
// Iterate through each CatalogItem in your collection
foreach (CatalogItem item in objCatalogItems)
{
// Since we want to use only Report Items we specify that as the Type.
// In addition we also only want to use reports that are not hidden. A
// hidden report is often a drilldown or child report that needs parameters
// from a prior report; therefore; only show reports not hidden.
if (item.Type == ItemTypeEnum.Report && (!item.Hidden))
{
// Pull the current items complete path and strip off the beginning
// portion which includes the ReportPath. Then assign it to a string variable
string path = item.Path.Remove(0, ReportPath.Length);
// Split the path string by our previously assigned PathSeparatorArray and put
// the values into a string array
string[] tokens = path.Split(PathSeparatorArray);
// Pass in our new string array and the collection of nodes from our Treeview on
// our aspx page.
BuildNodesFromTokens(tokens, TreeView1.Nodes);
}
}
}
/// <summary>
/// This method is the first step in actually building our tree node collection.
/// </summary>
/// <param name="tokens">A string array containing our report and pathing parts</param>
/// <param name="nodes">A collection of nodes from our treeview</param>
private void BuildNodesFromTokens(string[] tokens, TreeNodeCollection nodes)
{
// Create a new string array that can hold our original string array values - 1. The
// reason for this is to begin shrinking our string array one by one.
string[] newtoks = new string[tokens.Length - 1];
// Check for any current nodes on the page
if (nodes.Count > 0)
{
// Since nodes currently exists, we need to check to see if any of the current
// nodes match the next value of tokens which we'll use to create a node.
// Instantiate an integer variable to act as a counter
int matchFound = 0;
// Loop through each node already on the page
foreach (System.Web.UI.WebControls.TreeNode tr in nodes)
{
// Compare the current node text with the current value of index 0 in our
// string array. If there is a match then assign all but the first value of
// our original string array to our new string array - newtoks.
if (tokens[0].Equals(tr.Text))
{
for (int i = 0; i < newtoks.Length; i++)
{
newtoks[i] = tokens[i + 1];
}
// Increment counter
matchFound++;
// Pass the new string array, which is one less than original, and the current
// node to AddNode method.
AddNode(newtoks, tr);
}
}
// If no match was found in the foreach loop
if (matchFound == 0)
{
// Add a new node to the page and use the index 0 value of the tokens (original)
// string array.
nodes.Add(new System.Web.UI.WebControls.TreeNode(tokens[0]));
// Check to see if we have more than one value (indicating additional levels or depth)
if (tokens.Length - 1 > 0)
{
// Set the SelectAction of our last node, which is the new one we just created, to
// be None. This prevents the folders from being links with a URL action. We
// simply want it to be a + or - symbol indicating if we can expand or collapse the
// folder.
nodes[nodes.Count - 1].SelectAction = TreeNodeSelectAction.None;
// Assign all but the first value of tokens, our original string array to the newly
// created newtoks array.
for (int i = 0; i < newtoks.Length; i++)
{
newtoks[i] = tokens[i + 1];
}
// Since we just created a new 'parent' node, we'll pass any
// existing string array values to the AddChild method to build. We can
// do this since we know that the parent node had not previously existed. Therefore
// no new checks for existence are needed.
AddChild(newtoks, nodes[nodes.Count - 1]);
}
else
AeonFlux MaevskyPosted Feb 4, 2014, 5:37 PM
Hello! Many thanks for the post. I need some help! returns JSON as the TreeView1?