Click here to Skip to main content
15,890,527 members
Articles / Web Development / ASP.NET

Simple Way to Share Tree Organized Info on the Intranet

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
22 Aug 2012CPOL5 min read 18.2K   100   4   1
A simple way to share tree organized information on the Intranet

Sample Image

Introduction

Wouldn't it be nice to write what you want and have it be available right away on the intranet for your pals? There are solutions, yes, but what about just two ASPX pages and a few tricks on writing your information in XML which to the end is just text, right?

What I did:

  • I looked at XML blocks as nodes reflecting hierarchies, node attributes having node related information
  • I found simple recursive code to translate XML nodes into tree nodes
  • I created the XmlReadOnlyView.aspx page which is Explorer like with a tree control on the left, table on the right
  • For each XML node, the tree will capture the "display" attribute and the rest of the attributes for the selected node will be displayed in the table on the right
  • Established the following rules (if you think of XML as nodes expressing hierarchies and attributes qualifying the parent node, the rules are natural):
    • Supported XML-s will have a MainTree node, child of the root node
    • Each XML node child of MainTree will have a mandatory display attribute
    • Remaining attributes for the selected tree node will be displayed name, value in the table on the right of the screen
    • Added root child IgnoreAttributeList to be able to filter out attributes you simply want to ignore in the table on the right (one being "display")

The simplest way to try the sample is to unzip the source, use File -> Open -> Web Site... menu path in Visual Studio 2010 and after loading, run Debug. Inspect, alter the XML, look at the code... To deploy, you need to add an application with IIS Manager, either you know how to or you can Google...

The code in this presentation is deliberately stripped down to the backbone. It was not intended to be production quality, the scope was to keep focus on what needs to happen. These being said, all you have to do for sharing information is to edit some XML files to create nodes and attributes at your will, just respecting the conventions above. Myself, I went deeper. In my elaborated solution, the XMLs are created from different applications - not only logs - and I can later update the XMLs through both application and web (adding/removing nodes and attributes without breaking the application). I added an Updates node in the XML file root children collection where I keep track of each intranet user update on the XML file, either through application or the browser. And sure, I have more than one deployment for the browser access, each dedicated (one for logs, many for different config files for my employer's applications). Think of this as an initial information portal created by somebody deep into the problem then, through web, many consumers able to do punctual updates. But that will be part two if people will find this article worth of 'To be continued ...'

Code

Default.aspx.cs has nothing fancy, enumerates the XML files in the current directory and loads the table, one row per file. The button handlers just redirect the request to the page xmlReadOnlyViewer.aspx:

C#
public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        List<string> files = Directory.EnumerateFiles(
             Path.GetDirectoryName(Request.PhysicalPath), "*.xml", 
             SearchOption.TopDirectoryOnly).ToList();
        foreach (string f in files)
        {
           try
           {
                XDocument xDoc = XDocument.Load(f);
                XElement xmain = xDoc.Root.Element("MainTree");
                if (xmain != null)
                {
                    TableRow tr = new TableRow();
                    TableCell tcb = new TableCell();
                    tcb.BorderWidth = 1;
                    Button btn = new Button();
                    btn.CommandArgument = Path.GetFileName(f);
                    btn.Text = "Open";
                    btn.Click += new EventHandler(btn_Click);
                    tcb.Controls.Add(btn);
                    TableCell tcDesc = new TableCell();
                    tcDesc.Text = xmain.Attribute("display").Value;
                    tcDesc.BorderWidth = 1;
                    tcDesc.Wrap = true;
                    TableCell tcName = new TableCell();
                    tcName.Text = f;
                    tcName.BorderWidth = 1;
                    TableFiles.Rows.Add(tr);
                    tr.Cells.Add(tcb);
                    tr.Cells.Add(tcDesc);
                    tr.Cells.Add(tcName);
                }
           }
           catch (Exception ex)
           {
               Trace.Write(ex.Message);
           }
        }
        if (TableFiles.Rows.Count == 0)
        {
            TableRow tr = new TableRow();
            TableCell tc = new TableCell();
            tc.Text = "No suitable xml found.";
            tc.ColumnSpan = 3;
            tc.HorizontalAlign = HorizontalAlign.Center;
            tc.VerticalAlign = VerticalAlign.Middle;
            TableFiles.Rows.Add(tr);
            return;
        }
    }
 
    void btn_Click(object sender, EventArgs e)
    {
        Response.Redirect("XmlViewer.aspx?file="+((Button)sender).CommandArgument);
    }
}

XmlReadOnlyViewer.aspx.cs - The selected XML file is displayed in the tree control on the left. The attributes for the selected node in the tree are displayed in the table on the right.

Page_load Event

C#
protected void Page_Load(object sender, EventArgs e)
{
    string selNodeStr = "";
    if (Session["selected_tree_node"] != null)
        selNodeStr = Session["selected_tree_node"].ToString();
    loadTree(selNodeStr);
    updateRightPannel();
}

loadTree method - note the call to the recursive method building the tree node and the recursive meted to highlight the selected node.

C#
private void loadTree(string selectedNode)
{
    string file = Request["file"];
    if (file == null || file.Length == 0)
        return;
    string fileFullPath = Path.GetDirectoryName(Request.PhysicalPath) + "\\" + file;
    XDocument xDoc = XDocument.Load(fileFullPath);
    if (Session["selected_tree_node"] == null)
        Session["selected_tree_node"] = xDoc.Root.Element("MainTree").Attribute("display").Value;
    TreeNode root = new TreeNode(xDoc.Root.Element("MainTree").Attribute("display").Value);
    XElement xMainTree = xDoc.Root.Element("MainTree");
    buildNodes(root, xMainTree);
    treeViewlayout.Nodes.Clear();
    treeViewlayout.Nodes.Add(root);
    treeViewlayout.ExpandAll();
    if (selectedNode.Length > 0)
    {
        selectNode(root, selectedNode);
    }
    else
    {
        root.Select();
    }
}

buildNodes method - Builds the tree view recursively using the display attribute value for node text.

C#
private void buildNodes(TreeNode treeNode, XElement element)
{
    foreach (XElement child in element.Elements())
    {
        // Reserve the name Link 
        if (child.Name.LocalName == "Link")
            continue;
        TreeNode childTreeNode = new TreeNode(child.Attribute("display").Value);
        childTreeNode.SelectAction = TreeNodeSelectAction.SelectExpand;
        treeNode.ChildNodes.Add(childTreeNode);
        buildNodes(childTreeNode, child);
    }
}

selectNode method - Recursive, highlights the node with focus.

C#
private bool selectNode(TreeNode treeNode, string nodeText)
{
    if (treeNode.Text == nodeText)
    {
        treeNode.Select();
        return true;
    }
    foreach (TreeNode child in treeNode.ChildNodes)
    {
        if (selectNode(child, nodeText))
            return true;
    }
    return false;
}

updateRightPannel method - Displays the selected XML node attributes in the table on the right side of the page.

C#
private void updateRightPannel()
{
    try
    {
        if (treeViewlayout.SelectedNode == null)
            return;
        // the node is identified by the display attribute which
        // may not be unique. If so, you may want to use two session variables
        // with node name and display string and just know what kind of XML you are building.
        string selectedNodeDisplayString = (Session["selected_tree_node"] != null) ? 
                             Session["selected_tree_node"].ToString() : "";
 
        // the parent page is called after selecting the file to view 
        string file = Request["file"];
        if (file == null || file.Length == 0)
            return;
        // the xml files are in the same directory with the pages, 
        // you may want to use a subdirectoy, just change the path
        string fileFullPath = Path.GetDirectoryName(Request.PhysicalPath) + "\\" + file;
        if (selectedNodeDisplayString != null && selectedNodeDisplayString.Length > 0)
        {
            XDocument xDoc = XDocument.Load(fileFullPath);
            XElement xSelNode = xDoc.Root.Element("MainTree"); ;
            string display = selectedNodeDisplayString;
            var xNodes = from xNode in xDoc.Root.Element("MainTree").Descendants()
                             where xNode.Attribute("display").Value == display
                             select xNode;
            if (xNodes.Count() > 0)
            {
                xSelNode = xNodes.First();
            }
 
	        var ignoreAttributeList = from xAttNode in xDoc.Root.Element
                                          ("IgnoreAttributeList").Elements("Attribute")
	                                          select xAttNode.Attribute("name").Value;
	        var attrs = from xAtr in xSelNode.Attributes()
	                            where !ignoreAttributeList.Contains(xAtr.Name.LocalName)
	                            select xAtr;
	        tableAttributes.Rows.Clear();
	        if (attrs.Count() > 0)
	        {
	            foreach (XAttribute xAttr in attrs)
	            {
	                TableRow tr = new TableRow();
	                TableCell tcc = new TableCell();
	                tcc.Text = xAttr.Name.LocalName;
	                tcc.BorderWidth = 1;
	                tr.Cells.Add(tcc);
	                TableCell tcv = new TableCell();
	                tcv.Text = xAttr.Value;
	                tcv.BorderWidth = 1;
	                tr.Cells.Add(tcv);
	                tableAttributes.Rows.Add(tr);
	            }
	        }
	    }
	}
	catch (Exception ex)
	{
	    Trace.Write(ex.Message);
	}
}

treeViewlayout_SelectedNodeChanged method - the new selection is passed through the Session variable.

C#
protected void treeViewlayout_SelectedNodeChanged(object sender, EventArgs e)
{
    if (treeViewlayout.SelectedNode != null)
    {
       Session["selected_tree_node"] = treeViewlayout.SelectedNode.Text;
       updateRightPannel();
    }
}

XmlLog.cs - Sample class that would offer an interface for building XML logs compatible with the display. Use with caution, the class was not intended to be used in a multithreaded environment, it is just a sample. Through the interface, log nodes are added to the MainTree node. However, XmlDoc is publicly accessible, meaning you can build whatever hierarchy you want. The node Captions is part of the extended functionality I mentioned. I preferred to leave it in the code to not need to change this class for the follow-up article.

C#
public static class XmlLog
{
    public static XDocument XmlDoc
    {
        get;
    }
    public static string FileName
    {
        get;
    }
 
    public static void CreateXmlDoc(string root)
    {
        XmlDoc = new XDocument();
        XElement xRoot = new XElement(root);
        DateTime currentDate = DateTime.Now;
        XElement xMain = new XElement("MainTree");
        xMain.Add(new XAttribute("display", root + " Log " + 
                  currentDate.ToShortDateString() + " " + currentDate.ToShortTimeString()));
        xRoot.Add(xMain);
        xMain.Add(new XAttribute("start", string.Format("{0}:{1}:{2}:{3}", 
                  currentDate.Hour, currentDate.Minute, currentDate.Second, currentDate.Millisecond)));
        XmlDoc.Add(xRoot);
        FileName = string.Format("{0}_{1}_{2}_{3}_{4}_{5}_{6}_{7}.xml",
                xRoot.Name,
                currentDate.Year,
                currentDate.Month,
                currentDate.Day,
                currentDate.Hour,
                currentDate.Minute,
                currentDate.Second,
                currentDate.Millisecond);
        xRoot.Add(new XElement("Captions"));
        XElement xI = new XElement("IgnoreAttributeList");
        XElement xa = new XElement("Attribute");
        xa.Add(new XAttribute("name", "display"));
        xI.Add(xa);
        xRoot.Add(xI);
    }
    public static void LogInfo(string display, string description)
    {
        XElement xL = new XElement("Log");
        xL.Add(new XAttribute("display", display));
        xL.Add(new XAttribute("description", description));
        xL.Add("date_time", string.Format("{0} {1}", 
               DateTime.Now.ToShortDateString(), DateTime.Now.ToLongTimeString()));
        XmlDoc.Root.Element("MainTree").Add(xL);
    }
    public static void Save()
    {
        using (Stream logFile = File.Create(FileName))
        {
            using (XmlTextWriter tr = new XmlTextWriter(logFile, Encoding.Unicode))
            {
                DateTime now = DateTime.Now;
                XmlLog.XmlDoc.Root.Element("MainTree").Add(new XAttribute(
                   "end", string.Format("{0}:{1}:{2}:{3}", 
                   now.Hour, now.Minute, now.Second, now.Millisecond)));
                tr.Formatting = Formatting.Indented;
                XmlDoc.WriteTo(tr);
            }
        }
    }
    public static void LogException(Exception ex, string function)
    {
        XElement xex = new XElement("Exception");
        xex.Add(new XAttribute ("display","Exception"));
        xex.Add(new XAttribute("function", function));
        xex.Add(new XAttribute("message", ex.Message));
        xex.Add(new XAttribute("callstack", ex.StackTrace));
 
        if (ex.InnerException != null)
        {
            XElement ixex = new XElement("InnerException");
            ixex.Add(new XAttribute ("display","InnerException"));
            ixex.Add("message", ex.InnerException.Message);
            xex.Add(ixex);
        }
        XmlDoc.Root.Element("MainTree").Add(xex);
    }
}

XML file sample:

XML
<?xml version="1.0" encoding="utf-8" ?>
<NightlyDataCheckLog>
  <MainTree display="Nightly data check for 08/16/2012 3:30 AM" 
   start="08/16/2012 3:30 AM" result="OK" duration="200ms">
    <Log display="data inspection start" 
    description="Inspection started at 08/12/2012 03:00:15:455"/>
    <Log display="Inspection Resultt" 
    description="All inspected points are valid, 
     end time 08/12/2012 03:00:25:777"/>
  </MainTree>
  <IgnoreAttributeList>
    <Attribute name="display"/>
  </IgnoreAttributeList>
</NightlyDataCheckLog>

Background

The article is aimed at C# programmers having collateral contact with ASP.NET, but being familiar with LINQ to the extent the syntax is not a mystery.

Using the Code

Download the code, extract into a directory of your choice, then start Visual Studio 2010 and use the 'Open Existing Web' menu path. You can run as it is or you may review the references - be sure you have .NET 4.0 which does not come with Visual Studio 2010, you need to install it. The code should work with 3.5, I did not bother to check.

Points of Interest

I went into this playing with LINQ, trying to do ASP.NET without the auto generated code (that is part two), and exploiting XML as info storage and info handling. The log came into attention because I had some data content utilities running once a week and I needed a way to expose the XML logs through intranet for our customer support web interface.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Christian Amado22-Aug-12 11:32
professionalChristian Amado22-Aug-12 11:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.