Bind XML Data in ASP.NET
Display dynamic Web content by using XML files as the data source and databinding to ASP.NET list controls.
by Stan Schultes
April 2003 Issue
Technology Toolbox: VB.NET, ASP.NET, XML, ASP
The ubiquitous XML is quickly becoming the Web's data language. The .NET Framework offers many ways to access and manipulate data, including XML. Reading your Web pages' source data from XML files has several advantages over loading content from a database. You can construct XML files easily and generate them from a database or other source. You can split your data into small, easily transportable pieces. Also, you can use XML without any licensing fees or restrictions. I'll take you through a sample application to show how you can drive dynamic content on your Web pages by using XML as the data source bound to the ASP.NET DataList control.
The sample app uses a partial index of my Getting Started columns as the data. The articles.aspx page displays the list of articles in a DataList control. Each entry consists of four lines. The first line displays both the article title as a hyperlink to the online article, and the Technology Toolbox. The second line contains the magazine, column type, and publication date. The third line is a short description of the article, and the final line contains a hyperlink for downloading the code (see Figure 1). The hyperlinks post back to the page with a QueryString parameter, giving you the opportunity to log page and file-download requests.
The data representing this structure lives in an XML file on the Web server in this format (abbreviated from the demoflat.xml sample file):
<articles>
<article>
<title>Create...</title>
<type>Getting Started</type>
<readurl>gs0301a</readurl>
</article>
</articles>
The <article> element repeats for each article the file lists.
You read the XML file into an ADO.NET DataSet object, which you assign to a DataList control's Data Source. You can use the .NET Framework to load an XML file in several ways, depending on your data's complexity and how you want to represent it (see the ADO.NET documentation for more information). The simplest way is to use the DataSet object's ReadXML method:
Dim ds As New DataSet
ds.ReadXml("c:\inetpub\demoflat.xml")
You can use the DataSet's ReadXmlSchema method to load an XML schema (XSD) file if you have one, but the ReadXml method infers a schema from the input file by default. Calling the ReadXml method on demoflat.xml creates a single table named article from the input data. The <article> element is topmost in the XML data structure, occurring just below the root <articles> element.
Explore XML Structure in a DataSet
You can explore the XML data's structure easily by displaying it in a DataGrid control on an ASP.NET Web form. Drag a DataGrid control from the Web Forms tab of the VS.NET toolbox onto the WebForm1 designer. Add this code to the form's Page_Load event (double-click on the WebForm1 designer to bring up its code-behind file):
Dim dsFlat As New DataSet()
Dim sXmlPath As String = _
"c:\inetpub\demoflat.xml"
dsFlat.ReadXml(sXmlPath)
With DataGrid1
.DataSource = dsFlat.Tables("article")
.DataBind()
End With
The ReadXml method loads the XML content into the dsFlat DataSet. You set the DataGrid's DataSource property to the article table in the DataSet, then call DataBind to make the connection. This is ASP.NET databinding at its simplest, but you'll see quickly that this is a powerful capability.
When you run the project, you see one column for each attribute of the article element, with a row for each article. With this structure, you can display the article title as a hyperlink, with the URL's QueryString parameter coming from the <readurl> element value. For example, the relative URL for an article might look like this:
Webform1.aspx?id=gs0301a
This is nice, but the data is a simple column-by-rows structure. You might want to specify the text for a hyperlink or have more than one link. The demohier.xml file adds a <codeurl> element within the <article> element:
<codeurl text="Code" linkid="gs0301c"/>
The <codeurl> element, with its own attributes, is represented as a second table in the DataSet, appropriately named codeurl.
Databinding this hierarchical structure takes a little more work. Modify the page to display the demohier.xml data in two additional DataGrids. When you run this page, you can see that ADO.NET creates a linking column named article_id in both the article and codeurl tables to represent the relationship between the parent <article> element and its child <codeurl> elements (see Figure 2).
Now you're ready to move on to this column's main event—building the form with a bound DataList that displays the articles.xml file's article-index data. Add another Web form to the project and name it articles.aspx. Right-click on the articles.aspx item in Solution Explorer (SE), then choose Set as Start Page from the popup menu. Drag the default styles.css item from SE onto the articles.aspx designer surface to include this stylesheet on the page.
Next, drag a DataList control from the toolbox onto the page designer, then set its ID property to dlsArticles in the Properties window. A gray rectangle on the designer represents the DataList. Right-click on the rectangle, then choose Edit Template | Item Templates from the popup menu. Drag a Hyperlink control from the toolbox into the Item Template textbox in the Item Templates designer. Set the control's ID to hypTitle, then clear its Text property.
Now, drop a Label control into the item template and set its ID to lblTech. Click to the right of lblTech in the designer, then press the Enter key to move down a line. Drop three Label controls on the new line: lblPub, lblType, and lblDate. Press Enter again to move down to the third line. Drop another Label—lblDescription—and press Enter to move to the fourth line. Drop a Hyperlink—hypDownload. Right-click on the Item Templates designer, then choose End Template Editing.
Some Assembly Required
Next, you should do some manual tweaking to improve on the default appearance of the controls on the DataList. Click on the HTML tab at the bottom left of the page-designer window to view the page's HTML source. Replace the <p> and </p> (paragraph) tags around each control with a simple <br> (break) at the end of each line to reduce the vertical spacing between controls. Format each control's style, then edit the Header, Footer, and Separator templates as you wish.
You'll return to the task of databinding the item template controls after you create some code to handle the job. Double-click on the page designer to open the code-behind file. Insert these lines at the top of articles.aspx.vb (above the Class declaration) to save some typing:
Imports System.Data.OleDb
Imports System.Xml
Imports System.Configuration _
.ConfigurationSettings
Insert these declarations just above the "Web Form Designer Generated Code" region:
Private mDS As New DataSet()
Private msXmlPath As String
Open the project's web.config file (which is an XML file) by double-clicking on it in SE, then enter these lines just below the <configuration> root element to create a setting for the XML file location:
<appSettings>
<add key="XmlPath"
value="content/articles.xml" />
</appSettings>
This relative path specifies that articles.xml is located in a content directory just below your project's Web root.
Insert code in the page_load event routine to get the XML file's location and display the page:
msXmlPath = Server.MapPath _
(AppSettings.Get("XmlPath"))
DisplayArticleData()
Next, add the DisplayArticleData routine, which populates the DataSet and binds it to the DataList's DataSource property:
Private Sub DisplayArticleData()
mDS.ReadXml(msXmlPath)
With dlsArticles
.DataSource = _
mDS.Tables("article")
.DataBind()
End With
End Sub
This makes the DataSet available to the DataList and its contained controls, but you still must connect the data within the DataSet to the individual template controls. Go back to the articles.aspx page designer and click on the HTML tab. Set the hypTitle control's declaration within the ItemTemplate to bind the title data element to the hyperlink text:
<asp:hyperlink id="hypTitle"
runat="server"><%# DataBinder.Eval
(Container.DataItem, "title") %>
</asp:hyperlink>
The string inside the <%# and %> delimiters is a databinding expression. A databinding expression has two forms: One uses the DataBinder object as in the preceding code, and the second returns data with a function (which I'll show you shortly).
The DataBinder object's Eval method in the hypTitle control gets the "title" DataItem from the DataList, which is the current container object. This DataItem comes from the DataSet you bound to the DataList in DisplayArticleData. Recall that the first XML element within an article is the title. The preceding complicated-looking expression simply sets the hyperlink control's Text property to the value of the title field for the current record. A separate row in the DataList is rendered for each row in the table, resulting in a list of the article titles.
Databinding Saves You Time
Once you get the hang of how databinding works, you'll find that it saves you from writing a large amount of code to generate tables to display your data, as you must do in classic ASP. In ASP.NET, you spend more time twiddling the page's visual appearance than writing data-access code.
You should see the list of article titles when you run the project. The other DataList's controls don't show any data, because you haven't bound them to the DataSource yet. The DataList's Label controls are straightforward, because each binds to a particular field in the source DataSet:
<asp:label id="lblDescription"
runat="server" ><%# DataBinder.Eval
(Container.DataItem,"description")%>
</asp:label>
The hypTitle Hyperlink control uses the second form of databinding expression, returning data for the navigateurl property with a function:
navigateurl='<%# GetArticleUrl
(DataBinder.Eval(Container.DataItem,
"readurl")) %>'
The GetArticleUrl function takes the readurl value from the current row of the source DataSet as a parameter and returns a relative URL with a QueryString parameter for postback to the page:
Protected Function GetArticleUrl _
(ByVal LinkID As String) As String
Return "articles.aspx?ID=" & LinkID
End Function
The hypDownload control's databinding is more interesting, because it uses data from multiple tables of the input DataSet. The GetCodeUrl and GetCodeUrlText functions return a URL and text for the hyperlink, respectively (see Listing 1):
<asp:hyperlink id="hypDownload"
runat="server" target="_blank"
navigateurl='<%# GetCodeUrl
(DataBinder.Eval(Container.DataItem,
"article_id")) %>'>
<%# GetCodeUrlText(DataBinder.Eval
(Container.DataItem,"article_id"))%>
</asp:hyperlink>
Note the use of single-quote delimiters (rather than double quotes) around the navigateurl attribute value. They're necessary because double quotes appear within the databinding expression itself. ASP.NET interprets the single-quote attribute delimiters correctly in this case.
That takes care of the databinding tasks. Back in the page_load event, you check the QueryString ID parameter and call either DisplayArticleData to display the page (if there's no parameter), or LogAndDisplayLink if an article or code link was requested:
Dim sID As String = _
Request.QueryString("ID")
If sID.Length > 0 Then
LogAndDisplayLink(sID)
Else
DisplayArticleData()
End If
The LogAndDisplayLink routine does just what its name says—logs the request if desired, looks up the URL in the XML link table by ID, and redirects the page request:
'log request here ...
mDS.ReadXml(msXmlPath)
Dim dv As DataView = _
mDS.Tables("link").DefaultView
dv.RowFilter = "id='" & LinkID & "'"
Dim sURL As String = dv.Item(0).Item(1)
Response.Redirect(sURL)
The drill-down into the link table occurs with the help of a DataView object. You set DataView's RowFilter property to find the item from the link table whose ID is passed from the page's QueryString parameter. You retrieve the result from dv.item(0).item(1), where item(0) is the row and item(1) is the column. A new page opens, and the requested content displays when the user clicks on one of the links.
Using an XML file as a data source is easy, giving you an alternative to using a database for your dynamic Web content. Coupling an XML data source with ASP.NET databinding allows you to get your data onto a Web page with a minimum of code. However, your databinding task becomes complicated as the XML data becomes more hierarchical. At a certain point, you must balance the usefulness of complex data against the impracticality of displaying it on your Web pages.
About the Author
Stan Schultes is a Sarasota, Fla.-based Web and enterprise application architect and developer, and is an MCP in VB. Stan is a contributing editor for VSM and writes regularly for the magazine. Visit Stan's Web site at www.vbnetexpert.com for online code demos, updates, and other information. E-mail him at .
|