Introduction
ASP.NET includes a variety of tools for displaying and editing XML documents. A previous article, Working
with XML Data Using LINQ, a TreeView, and a ListView :: Displaying Data, showed how with a TreeView control, a ListView control, an XmlDataSource control,
a LinqDataSource control, and about 50 lines of code we could create a web page that displayed the contents of a hierarchical XML file. Specifically,
the page displayed the contents from a fictional employee phonebook, which allowed for an arbitrary number of employees nested within branches and
departments. The TreeView and XmlDataSource controls displayed the various branches and departments, while the ListView and LinqDataSource controls
displayed the employees belonging to the selected branch or department (or one of its subdepartments).
In addition to displaying data, the ListView and LinqDataSource controls can be augmented to edit, insert, and delete data. This article examines how
to update the ListView and code to enable the visitor to add, edit, and delete employee phone records. By the conclusion of this article we will have
constructed a web page that displays XML data and enables an end user to modify the data from a simple, easy-to-use web page interface. Read on to
learn more!
First Things First: Read the Displaying Data Article Before This One
Configuring the ListView to Support Editing
The ListView control, new to ASP.NET version 3.5, displays a series of records from a data source using templates. In addition to displaying data,
the ListView control can be used to insert, edit, and delete data. Like the GridView control, the ListView control allows for in-line editing. That is,
a user can select a particular record to edit from the list of displayed records, make her changes, and then update the data. The ListView also offers
inserting functionality.
The principles of editing records in a ListView are very simple. We have to create a special template for the item being edited; this template, EditItemTemplate,
is used to render the item being edited by the user. Additionally, we need to add a Button, LinkButton, or ImageButton control (usually in the ItemTemplate)
that has its CommandName property set to "Edit". When this button is clicked, a postback occurs and the ListView is rebound to its data source
with this particular item rendered using its EditItemTemplate.
The ItemTemplate is straightforward enough: just add a LinkButton (or Button or ImageButton) with its CommandName property set to "Edit":
Before we create the ListView's EditItemTemplate, however, there is a complication in our specific situation that we need to first address.
Recall that the records displayed in the ListView may be located on different "levels" in our XML file (i.e. in various branches/departments). For example,
when select a particular branch or department from the TreeView on the left, the ListView displays not only the employees in that selected branch or department,
but all employees in descendent branches and departments as well. We need to be able to identify the correct branch or department an employee belongs to
when saving the edited values back to the XML file.
We can solve this problem by remembering the XPath address of the Employee XML element when loading it in the ListView. To accomplish this we
need to add a property to our anonymous type returned by the LINQ query. The text in red shows the new anonymous type
member, XPathAddress:
var query = from employeeElement in parentElement.Descendants("Employee")
select new
{
Name = employeeElement.Attribute("name").Value,
Telephone = employeeElement.Attribute("telephone").Value,
XPathAddress = getXPathAddress(employeeElement)
};
We will later set this value as the CommandArgument property of the "Update" LinkButton in the ListView's EditItemTemplate.
As you can see from the LINQ query above, the XPathAddress member assigned the value returned by the getXPathAddress method, which
we need to create within the ASP.NET page's code-behind class. The method will iteratively traverse the passed-in employeeElement structure
to the root element, building the XPathAddress value as it walks up the tree:
// We will iteratively go through the structure to the root element
// The loop will stop at the root element (we don't include the root
// element in the address !!)
while (cElement.Parent != null)
{
xPath = String.Format("/*[@id='{0}']",
cElement.Attribute("id").Value)
+ xPath;
cElement = cElement.Parent;
}
return xPath;
}
Now that we know the XPath expression for each employee object, we can create the EditItemTemplate. As you can see, the Update LinkButton's
CommandArgument property is assigned the XPathAddress value returned by the LinqDataSource. We will need to use this value
in code to update the apporpriate employee record. Also note that instead of displaying the employee's name and phone number values in a Label, we use
TextBox Web controls instead.
You may be wondering why we used the CommandName value of "XUpdate" instead of "Update". If we set the CommandName to "Update",
the ListView will try to update the changes automatically by calling the data source control's Update method. We have mapped the data
ourselves (in our LinqDataSource control's Selecting event handler), so our LinqDataSource can't serve as the updating source
in this case. We therefore have to write a method ourselves to save changes.
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
Saving the Editing Record Back to the XML File
We need to execute code when the "XUpdate" LinkButton has been clicked. The ListView's ItemCommand event is raised whenever a button
with a CommandName property is clicked. Therefore, we need to create an event handler for this event. Keep in mind that this event handler
will execute when any command button in the ListView is clicked; this includes the sorting and paging interface buttons. Consequently, it is imperative
that we check the passed in e.CommandName value equals "XUpdate" before proceeding with our updating logic.
When the "XUpdate" button has been clicked, we need to save the changes and return the ListView to its pre-editing state. The following code illustrates
this functionality. Note that the data is saved by the saveChanges method; we will create this method in a moment.
protected void lvwEmployees_ItemCommand(object sender, ListViewCommandEventArgs e)
{
switch (e.CommandName)
{
// Update the XML file after editation
case "XUpdate":
saveChanges((string)e.CommandArgument, e.Item);
Our final task for editing records is to create the saveChanges method, which saves the changes back to the XML file. As the above code
shows, the saveChanges method is passed two input parameters:
The XPath address of the element being changed (recall that we assigned this XPathAddress value to the Update button's CommandArgument property)
The ListViewItem being edited. Our method will search TextBoxes with new values in this ListViewItem
The saveChanges method loads the Employees XML element from the source file as an XElement object. The values
of the XElement object are then modified to correspond with the user's entries. Finally, the updated XElement object is
saved back to the XML file.
That's all there is to it! At this point a user can edit any existing employee phone record, changing their name, telephone number, or both.
Deleting Employee Phone Records
Deleting employee phone records works on a similar principle. We need to remember the XPathAddress for the employee and pass this to
the event handler responsible for deleting the record. As with editing, we need to add a Delete LinkButton (or Button or ImageButton) to the ListView's
ItemTemplate. Set its CommandName property to "XDelete" and its CommandArgument property to the XPathAddress
value of the element to delete.
In the ListView's ItemCommand event handler we need to handle the "XDelete" command. Like with the "XUpdate" command, we will call a
helper method (deleteItem), passing it the XPath expression, and then return the ListView to its pre-editing state.
case "XDelete":
deleteItem((string)e.CommandArgument);
The deleteItem method loads an XElement object based on the supplied XPath expression, deletes it, and then saves the
contents of the XML file back to disk.
Adding New Employee Phone Records
Our final task is to enable the end user to add new employee phone records to the selected branch or department. Inserting an item from the ListView
control is similar to editing an item in that both interfaces are defined through templates. To define the interface used for inserting, use the
InsertItemTemplate. This template can be shown as the first item in the ListView or as the last item.
The InsertItemTemplate (shown below) looks similar to the EditItemTemplate in that it contains a TextBox Web control for the employee's name and
telephone number. Instead of an Update button, we have a Save button (whose CommandName property is set to "XSave").
Like with the "XUpdate" and "XDelete" commands, we need to handle the "XSave" command in the ListView's ItemCommand event handler. After
saving the item (via a call to saveNewItem), we need to rebind the data to the ListView so that the just-added item appears in the list.
case "XSave":
saveNewItem(e.Item);
lvwEmployees.DataBind();
break;
The saveNewItem method is a bit more complicated than the saveChanges or deleteItem methods.
Part of the challenge stems from the fact that each element in the XML file has an id attribute with a unique identifier for that
"level." So in adding a new element we need to choose an appropriate id value.
The saveNewItem method performs the following tasks:
Load the root element (like in other methods)
Get the XPathAddress of the selected element in the TreeView and find the element in the XML file
Get the maximum ID value from all elements in the parent element
Create a new XElement object
Add the new element to its parent and save the file
int maxId = parentElement.Elements().Max(c => int.Parse(c.Attribute("id").Value));
XElement newElement = new XElement("Employee",
new XAttribute("id", (maxId + 1).ToString()),
new XAttribute("name", ((TextBox)lvwItem.FindControl("txtInsertName")).Text),
new XAttribute("telephone", ((TextBox)lvwItem.FindControl("txtInsertTelephone")).Text));
parentElement.Add(newElement);
rootElement.Save(fileName);
}
That's all, folks! With relatively small effort and a few lines of code we've created a fully-functional ASP.NET application for editing structured
XML data. The main idea, on which the application is based, is the connection of TreeView and ListView, which enabled us to edit tabular data stored
in more hierarchical XML structures. The bulk of the work is done for us by the TreeView, ListView, DataPager and LinqDataSource controls. All we had
to do was write the code dealing with the location of record in various levels of the XML structure.
All newsletters are sent from the domain "internet.com." Please use this domain name (not the entire "from" address, which varies) when configuring e-mail or spam filter rules, if you use them.
You are subscribed to the 4GuysFromRolla newsletter as kallyorama@gmail.com. To *** from the 4GuysFromRolla newsletter, please click here.
To *** via postal mail, please contact us at:
Jupitermedia Corp.
Attn: Newsletter Subscription Dept.
475 Park Avenue South
New York, NY 10016
Please include the email address which you have been contacted with.