To read the article online, visit http://www.4GuysFromRolla.com/articles/042308-1.aspx

Working with XML Data Using LINQ, a TreeView, and a ListView :: Displaying Data

By Miroslav Kadera


Introduction


With more and more data being stored in XML, web applications today commonly need some way to view and edit the data stored in an XML file from a web page interface. If the XML data is relatively "flat" and tabular in nature, we can use data Web controls like the DataGrid, GridView, and Repeater. (See Quickly Editing XML Data for an example of editing XML data through a DataGrid control.) But what if the XML data is more hierarchical and in a less tabular format? Consider a company-wide phone book, which is recursively structured into branches and departments, subdepartments, and so forth. How can this XML data, which can have any number of nodes and any level of children, be displayed and edited through a web page?

In this article we will build a web page that displays the contents of a company-wide phone book whose information is encoded in an XML file. The page will recursively display the phone book XML data using a TreeView to list the branches and departments and a ListView to enumerate the employees that belong to the selected branch or department. In particular, the ListView will display the employees that belong to the selected branch or department as well as all the employees that belong to any subdepartments. (In a future article - Working with XML Data Using LINQ, a TreeView, and a ListView :: Editing Data - we look at how to extend the ListView to enable the user to add, edit, and delete phone book entries.)

Read on to learn more!

A Working Demo is Available for Download at the End of this Article
Throughout this article we will be examining various code snippets and screen shots. These code snippets and screen shots were taken directly from the demo application, which is available for download at the end of this article. I encourage you to first download the article, load it in Visual Studio, and run it locally to see it in action. After that, return to this article and follow along at your computer.

Please note: many of the controls and concepts discussed in this article are new to ASP.NET version 3.5; therefore, the demo will not work in Visual Studio 2005 or earlier versions. If you do not currently have Visual Studio 2008, you can always download and install the free Visual Web Developer 2008, which can be installed side-by-side with other versions of Visual Studio.

A Look at the Source XML Data


This article illustrates how to display XML data in a web page using a TreeView and ListView. Before we jump into building the page, let's first take a moment to examine the structure of the XML data we are to display. The following XML data represents a phone book of employees in a company. The company consists of branches and departments. A branch may contain departments and departments may contain other departments. Employees may be assigned to any branch or department.

The XML data may look like this:

<?xml version="1.0" encoding="utf-8"?>
<PhoneBook>
   <Branch id="1" name="Northern Branch">
      <Department id="1" name="Marketing">
         <Employee id="1" name="Miroslav" telephone="555-5555" />
         <Employee id="2" name="Scott" telephone="555-1111" />
         <Department id="2" name="Advertising">
            <Employee id="3" name="Chris" telephone="555-2222" />
            <Employee id="4" name="Bruce" telephone="555-3333" />
            <Employee id="5" name="Sam" telephone="555-4444" />
         </Department>
      </Department>
      <Employee id="6" name="Jisun" telephone="555-9999" />
   </Branch>
   <Department id="3" name="Executive Team">
      <Employee id="7" name="Davis" telephone="555-8888" />
      <Employee id="8" name="Kate" telephone="555-9900" />
   </Department>
</PhoneBook>

The above XML indicates that there is a Northern Branch with a Marketing department. The Marketing department is where employees Miroslav and Scott work. The Advertising department is a subdepartment of Marketing and is where Chris, Bruce, and Sam work. Jisun works in the Northern Branch, but is not assigned to a particular department within the branch. The Executive Team department is where Davis and Kate work, and is not tied to any branch.

Populating a TreeView Control with the XML Data


Our end goal is to build a page that lists the branches and departments in a hierarchical layout in the left portion of the page and the employees that belong to the selected branch or department in the right portion of the page. Before we can display the employees, we first need to create the user interface for listing the branches and departments. Because there is a hierarchical relationship among branches and departments, and because there can be an arbitrary number of branches, departments, and subdepartments, the TreeView control is an ideal Web control for displaying this information.

The XmlDataSource control makes displaying XML data in a TreeView a walk in the park. Start by adding an XmlDataSource onto the page. Next, set its DataFile property to point to the source XML file:

<asp:XmlDataSource ID="treeSource" runat="server" DataFile="~/PhoneBook.xml" />

The XmlDataSource, by itself, doesn't display anything. It just queries XML data. To display the data we need to add a TreeView control and bind it to the XmlDataSource. Drag a TreeView control from the Toolbox onto the page and set its DataSourceID property to the ID of the XmlDataSource (treeSource). Next, create a TreeNodeBinding for the root PhoneBook element and TreeNodeBindings for the Branch and Department children elements. A TreeNodeBinding instructs the TreeView as to which XML elements to display and how to display them.

<asp:TreeView ID="tvwPhoneBook" runat="server" DataSourceID="treeSource"
   AutoGenerateDataBindings="False">
   <DataBindings>
       <asp:TreeNodeBinding DataMember="PhoneBook" Text="Phone Book" />
            <asp:TreeNodeBinding DataMember="Branch"
                   FormatString="Branch "{0}"" TextField="name" />
            <asp:TreeNodeBinding DataMember="Department"
                   FormatString="Dpt. "{0}"" TextField="name" />
   </DataBindings>
</asp:TreeView>

Running the page now we see a TreeView with nodes corresponding to the XML structure of branches and departments. Note that the TreeView does not include Employee elements. This is because there is no Employees TreeNodeBinding.

The branches and departments are listed in the TreeView.

Loading Employee Records for the Selected Branch or Department


With the TreeView correctly displaying branches and departments, our next task is to display the employees for the selected department or branch to the right of the TreeView. We will use a LinqDataSource to load the employee information from the phone book XML data. Start by adding a LinqDataSource control to your page:

<asp:LinqDataSource ID="listSource" runat="server" />

Whenever data is requested from the LinqDataSource, its Selecting event is raised. When this event fires we need to programmatically grab the appropriate set of employee records given the selected branch or department, so create an event handler for the LinqDataSource's Selecting event. To create this event handler, simply double-click the LinqDataSource control in the page designer. This will create an event handler in your page's code-behind class named listSource_Selecting with the following signature:

protected void listSource_Selecting(object sender, LinqDataSourceSelectEventArgs e)
{

}

Within this method we aim to select the data (from the source XML file) of employees that correspond to the selected node in the TreeView. The selected node in the TreeView can be retrieved using the TreeView's SelectedNode property, which returns a TreeNode instance corresponding to the selected node. This returned TreeNode includes a DataPath property that spells out the path of the element represented by the TreeNode in such a form that it can be directly used as an XPath query. (XPath is a language for querying nodes within an XML document.)

For example, the TreeNode representing the first department of the first branch (the Marketing department of the Northern Branch) has a DataPath property value of /*[position()=1]/*[position()=1]/*[position()=1]. The first part, /*[position()=1], represents the root element. In English, it says, "Give me the first node (position()=1) that starts at the root." The second part returns the first branch because, like the first part, it retrieves the first node (position()=1), but this time it's the first node of the root element (since that was what was returned by the first part of the XPath expression). Finally, the last part represents the first department of the first branch. As you can see, the XPath indexing used by position() starts with 1, not 0. (For more information on XPath, visit the XPath Tutorials at W3 Schools.)

To retrieve the appropriate set of XML elements in the LinqDataSource's Selecting element we will use LINQ to XML. LINQ to XML is a series of classes new to the .NET Framework version 3.5 that facilitate working with XML data. See Hooked on LINQ's LINQ to XML - 5 Minute Overview for a good summary of this technology.

One of the core classes in LINQ to XML is XElement, which represents an XML element. XElement also includes a Load method that we can call to read in the contents of an XML document:

XElement rootElement = XElement.Load(MapPath("PhoneBook.xml"));

Notice that the LINQ to XML Load method returns the root element. If you have worked with XML data in previous versions of the .NET Framework you likely used the XmlDocument class, whose Load method reads in the whole document such that the root element is the first ChildNode. We have to keep this in mind when querying with XPath because we need to cut out the first part (the XPath address to the root element) from the XPath string.

The following code illustrates this. It starts by reading in the XPath query for the TreeView's SelectedNode and then uses the Substring method to remove the first n characters from the XPath string, where n is the length of the root element's XPath expression.

// Get the XPath address of the selected branch/department
string xPathQuery = tvwPhoneBook.SelectedNode.DataPath;

// Cut the root element path from the XPath query
string rootElementXPath = tvwPhoneBook.Nodes[0].DataPath;
xPathQuery = xPathQuery.Substring(rootElementXPath.Length);

Linq to XML provides the XElement with three extension methods: XPathEvaluate, XPathSelectElement, and XPathSelectElements. All three methods accept an XPath expression as input. As we can guess from their names, the XPathEvaluate is used to evaluate an XPath expression, returning a scalar value, such as the value of attribute or the text content of an XML element; XPathSelectElement returns the XElement specified by the XPath expression passed into the method; and XPathSelectElements returns a collection of elements. We will use the second method, XPathSelectElement.

XElement parentElement = rootElement.XPathSelectElement(xPathQuery);

The main complication is that we need to select the employee records recursively. For example if we have a branch selected in the TreeView, we want to display employees in all departments and subdepartments in this branch, as well as employees in the branch that do not belong to any particular department. The XElement class contains a Descendants(elementName) method to assist with deep loading. We can therefore select all employees from parentElement, regardless of how deep in the hierarchy they are, using:

parentElement.Descendants("Employee")

(To browse employees non-recursively - that is, to get just those employees in the selected branch or department - use the Elements(elementName) method, instead.)

We are only interested in the name and the telephone numbers for the employees. The easiest (and most elegant) way to represent these data is to use an anonymous data type. Our LINQ query will therefore look as follows:

var query = from employeeElement in parentElement.Descendants("Employee")
            select new
            {
                Name = employeeElement.Attribute("name").Value,
                Telephone = employeeElement.Attribute("telephone").Value
            };

All that remains is to return the data to the LinqDataSource from the Selecting event. This is accomplished by assigning the query results to the e.Results parameter.

e.Result = query;

Displaying the Selected Emloyees in the ListView Control


Now that we have crafted the LINQ query to retrieve those employees that belong to the selected branch or department, all that remains is to display the selected employees in a ListView control. Add a ListView control to the page and bind it to the LinqDataSource.

<asp:ListView ID="lvwEmployees" runat="server" DataSourceID="listSource" />

The ListView controls works on the templates principle. We need to define a template to display the whole ListView (the LayoutTemplate) and then a template for rendering individual items (the ItemTemplate). (For a more in-depth look at the ListView control, be sure to read Using ASP.NET 3.5's ListView and DataPager Controls.) Add the following template definition to your ListView control.

<LayoutTemplate>
   <table>
      <tr>
         <td style="font-weight: bold;">Name</td>
         <td style="font-weight: bold;">Phone number</td>
      </tr>
      <asp:PlaceHolder ID="itemPlaceHolder" runat="server" />
   </table>
</LayoutTemplate>

<ItemTemplate>
   <tr>
      <td><%# Eval("Name") %></td>
      <td><%# Eval("Telephone") %></td>
   </tr>
</ItemTemplate>

The above template markup displays the results in a two-column HTML <table>. To get the name and telephone number for each employee, use the data-binding function Eval("propertyName"). The net result is that the page now shows the set of employees in a ListView to the right of the TreeView control.

The employees are listed in a ListView control.

We need to make sure that the ListView's contents are rebound to it each time the user selects a new branch or department from the TreeView. This can be accomplished by calling the ListView.DataBind() method after each change of the TreeView's SelectedNode:

protected void tvwPhoneBook_SelectedNodeChanged(object sender, EventArgs e)
{
   lvwEmployees.DataBind();
}

Adding Sorting and Paging


The ListView has the ability to allow the end user to sort its contents. The ListView handles all of the sorting logic; we just have to place a LinkButton control (or a Button or ImageButton control) within the ListView and to set its CommandName and CommandArgument properties to Sort and the name of the field to sort the data by, respectively. For example, to add a LinkButton to sort the results by the Name property, use the following LinkButton:

<asp:LinkButton ID="lnkSortName" runat="server" Text="Name" CommandName="Sort" CommandArgument="Name" />

For more information see Sorting Data with the ListView Control.

Another useful enhancement that is easy to implement is paging. ASP.NET 3.5 includes the DataPager control, which can be used in tandem with the ListView control to provide a pageable interface. After adding the DataPager control onto the page, set its PageSize property to the number of records you want to display per page. Next, set its PagedControlID property to the ID of the ListView control whose data is to be paged through. The final step is to define the paging fields to display (whether to show Next/Previous links, whether to use numeric paging links, and so on); everything else is done handled by the DataPager and ListView controls! For more information see Paging Through Data with the ListView and DataPager Controls.

The screenshot below shows the ListView after it has been configured to support sorting and paging.

The ListView control now implements sorting and paging.

Conclusion


With just a couple of Web controls and a few lines of code, we have a fully-functional web application for viewing XML data structures that have a hierarchical structure. We used a TreeView control and XmlDataSource for displaying the hierarchical portions of the XML file and a ListView control and LinqDataSource for showing the tabular employee records for the selected branch or department. In Working with XML Data Using LINQ, a TreeView, and a ListView :: Editing Data we see how to further augment the ListView to allow for editing, inserting, and deleting or employee information.

Happy Programming!

  • By Miroslav Kadera


    Further Reading


  • Quickly Editing an XML File
  • LINQ to XML - 5 Minute Overview
  • Using ASP.NET 3.5's ListView and DataPager Controls
  • Working with XML Data Using LINQ, a TreeView, and a ListView :: Editing Data
  • Attachments


  • Download the code used in this article

  • Article Information
    Article Title: ASP.NET.Working with XML Data Using LINQ, a TreeView, and a ListView :: Displaying Data
    Article Author: Miroslav Kadera
    Published Date: April 23, 2008
    Article URL: http://www.4GuysFromRolla.com/articles/042308-1.aspx


    Copyright 2014 QuinStreet Inc. All Rights Reserved.
    Legal Notices, Licensing, Permissions, Privacy Policy.
    Advertise | Newsletters | E-mail Offers