Last time I played around with updating an XML file using an XmlDataSource control, a GridView control, and a DetailsView control. Inserting into an XML file is similar, in that guess what, you get to do all the work. One decision I made was to have a button on the page that would explictly put the DetailsView control into insert mode. My use of the DetailsView control is therefore maybe not exactly as it might have been designed:- Before any row in the GridView control is selected, the DetailsView control is invisible.
- When you select a row in the GridView control, I make the DetailsView control visible, but also put it into edit mode. When you update the selected row, I disappear the DetailsView control again.
- To insert a new record, I click a button, which displays the DetailsView control in insert mode. When I click Save, the DetailsView control disappears again.
Make sense? IOW, I'm programmatically controlling the mode in which the DetailsView control appears, rather than using any built-in functionality for that. And I'm really only displaying it in two modes, edit and insert.
Here's the code to put the DetailsView control into insert mode:Protected Sub buttonInsert_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
XmlDataSource2.Data = "" ' Clear old data from control
DetailsView1.DataBind()
DetailsView1.Visible = True
DetailsView1.ChangeMode(DetailsViewMode.Insert)
End Sub
If I don't set the Data property (or do something like that), the DetailsView control shows up with whatever record was last edited or inserted.
When you click the Update button, here's what happens:Protected Sub DetailsView1_ItemInserting(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.DetailsViewInsertEventArgs)
Dim newQuote As String = e.Values(0)
Dim newAuthor As String = e.Values(1)
Dim quotesXmlDoc As New System.Xml.XmlDocument
Dim xmlFileName As String = XmlDataSource2.DataFile
xmlFileName = xmlFileName.Replace("~", Server.MapPath("~"))
xmlFileName = xmlFileName.Replace("/", "\")
quotesXmlDoc.Load(xmlFileName)
If quotesXmlDoc Is Nothing Then
Response.Write("Xml doc is nothing")
Exit Sub
End If
' Count #/existing nodes to set new key
Dim lastElement As XmlElement = quotesXmlDoc.DocumentElement.LastChild
Dim newQuoteID As Integer = CInt(lastElement("quoteId").InnerText) + 1
' Create new Quotation element and add children elements to it
Dim newQuoteElement As XmlElement = quotesXmlDoc.CreateElement("Quotation")
Dim quoteIdElement As XmlElement = quotesXmlDoc.CreateElement("quoteId")
quoteIdElement.InnerText = newQuoteID.ToString()
newQuoteElement.AppendChild(quoteIdElement)
Dim quoteElement As XmlElement = quotesXmlDoc.CreateElement("quote")
quoteElement.InnerText = newQuote
newQuoteElement.AppendChild(quoteElement)
Dim authorElement As XmlElement = quotesXmlDoc.CreateElement("author")
authorElement.InnerText = newAuthor
newQuoteElement.AppendChild(authorElement)
quotesXmlDoc.DocumentElement.AppendChild(newQuoteElement)
' Write out updated file
quotesXmlDoc.Save(xmlFileName)
e.Cancel = True
DetailsView1.ChangeMode(DetailsViewMode.ReadOnly)
DetailsView1.Visible = False
End Sub
It's similar to updating, with a couple of small differences. (Other than the obvious difference that I'm adding a new element rather than updating an existing one.) One difference is that I have to come up with a new quoteId value -- that is, a unique key for the new quotation. I use the somewhat cheesy approach of reading the ID value of the last quote in the file and then incrementing that. The assumption, of course, is that the quotes are all in numeric order. That happens to be true in this file, but is hardly a robust algorithm for general use.
This time I got the path of the XML file from the XmlDataSource control and fixed it up by subbing Server.MapPath("~")
for the ~
returned as part of the property string. Again, this is because the various System.Xml methods don't know what ~
means.
After writing out the XML file, I cancel the insert method, else the XmlDataSource control throws a not-supported exception, because it doesn't support any update methods.
To complete the triptych of update operations, I added code to be able to delete a quotation. (This has proved handy primarily for deleting the various test quotes I put in there.) I don't need the DetailsView control to delete; I can do that directly from the GridView control by adding a CommandField to the GridView control like this:<asp:CommandField ShowDeleteButton="True" />
To actually delete the quotation, I used this code:Protected Sub GridView1_RowDeleting(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.GridViewDeleteEventArgs)
XmlDataSource2.Data = "" ' Clear old data from control
DetailsView1.DataBind()
DetailsView1.Visible = False
Dim quoteId As String = e.Keys(0).ToString
Dim quotesXmlDoc As New System.Xml.XmlDocument
Dim xmlFileName As String = XmlDataSource2.DataFile
xmlFileName = xmlFileName.Replace("~", Server.MapPath("~"))
xmlFileName = xmlFileName.Replace("/", "\")
quotesXmlDoc.Load(xmlFileName)
Dim quoteNode As XmlElement
quoteNode = quotesXmlDoc.DocumentElement.SelectSingleNode( _
String.Format("//Quotation[quoteId='{0}']", quoteId))
quotesXmlDoc.DocumentElement.RemoveChild(quoteNode)
quotesXmlDoc.Save(xmlFileName)
GridView1.DataBind()
e.Cancel = True
End Sub
Much like updating -- get key, read XML file, locate node using an XPath expression. This time, call RemoveChild, and poof, it's gone.
I am having one odd problem that I haven't quite resolved. When I update a quote or insert a new one, I call DataBind to refresh the display in the GridView control, and that works great. However, when I do the same thing after deleting a record, the deleted record stubbornly stays visible. If I page away from that page and page back, all is well. So there's something I'm not understanding about re-display after a delete in the GridView contol. When I have that one sussed out, I'll note something here.
One more thing. When I insert a new record, it's always added to the end of the XML file. I decided early on that it would be handy therefore to have a link in the pager that would take me directly to the last page, no matter where I was otherwise. Wow, this turned out to be harder to figure out than I thought it would be. I'll make a separate entry for that, since this is long enough.