Working with XML Data Using LINQ, a TreeView, and a ListView :: Displaying DataBy Miroslav Kadera
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:
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:
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
treeSource). Next, create a
TreeNodeBinding for the root
PhoneBook element and
Department children elements. A
TreeNodeBinding instructs the TreeView as to
which XML elements to display and how to display them.
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
Employee elements. This is because there is no
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:
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
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:
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
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,
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
method that we can call to read in the contents of an XML document:
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
to remove the first n characters from the XPath string, where n is the length of the root element's XPath expression.
Linq to XML provides the
XElement with three extension methods:
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;
XPathSelectElements returns a collection of elements. We will use the second method,
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
XElement class contains a
Descendants(elementName) method to assist with deep loading. We can therefore select all
parentElement, regardless of how deep in the hierarchy they are, using:
(To browse employees non-recursively - that is, to get just those employees in the selected branch or department - use the
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:
All that remains is to return the data to the LinqDataSource from the
Selecting event. This is accomplished by assigning the query results
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.
The ListView controls works on the templates principle. We need to define a template to display the whole ListView (the
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.
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.
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
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
Sortand the name of the field to sort the data by, respectively. For example, to add a LinkButton to sort the results by the
Nameproperty, use the following LinkButton:
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.
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.