January 08, 2006
|
Inserting into an XML file (and deleting)
|
8476 hit(s)
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.
|