|
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 we will 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
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.
From the Internet.com eBook Library: Navigating Your IT Career
A career in information technology usually has its share of
ups and downs. Download this Internet.com eBook to learn
where the jobs are in IT, how to negotiate a salary, and
helpful advice on job security and how to deal with a layoff.
Join Internet.com now to download!
http://www.devx.com/ebook/Link/34938
|
Interested in placing your TEXT AD HERE? Click Here
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.
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.
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.
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.
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 a future article we will see how to further augment the
ListView to allow for editing, inserting, and deleting or employee information.
Until then... 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
Attachments
Download the code used in this article
|