Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Create Editable XML Documents Using XPath and the TreeView Control : Page 3

Adding drag and drop support to your TreeView control needn't be taxing. With XML and Xpath, you can get most of that support for free.

Filtering XML Data
Let's take a step back. If this were a real contacts application, users wouldn't expect to see "email," "city," or "country" in the hierarchical view. Rather, they would expect some top-level identification of who the contact is—Alex, Rebekah, or Justin, in this case—and the set of details associated with that contact (email, city) in an adjoining, editable pane. Similarly, users are likely to want to drag contacts up and down the tree to rearrange them; however, dragging email addresses or cities between individual contacts, which the use of a TreeView control implies would also be possible, makes no sense. That's because people normally expect a hierarchical view to summarize data, not just categorize it. In other words, there are valid reasons to change contact order or groupings, but "city" and "country" elements are clearly associated with specific contacts, and you'd no more expect to find them in isolation than to find pairs of them together.

One solution is to hide the sub-fields when you display the contact tree. For example, you could add a special attribute (such as view="hide") to sub-elements such as email and address that you don't want to display. Then, you could ignore any elements (and their children) containing that special attribute in the populateTreeControl method when populating the TreeView control. Although that would work, changing the data source to suit the data consumer isn't a responsible design decision.

A better approach is for the data consumer to define what structures the hierarchical view can or cannot manipulate within a given document. You can achieve this by modifying the populateTreeControl() method so it supports XPath queries. For example:

[C#] private void populateTreeControl(System.Xml.XmlNode document, System.Windows.Forms.TreeNodeCollection nodes) { foreach (System.Xml.XmlNode node in document.ChildNodes) { System.Xml.XmlNode expr = node.SelectSingleNode(xpath_filter); if (expr != null) { TreeNode new_child = new TreeNode(expr.Value); nodes.Add(new_child); populateTreeControl(node, new_child.Nodes); } } }

[VB] Private Sub populateTreeControl( _ ByVal document As System.Xml.XmlNode, _ ByVal nodes As System.Windows.Forms.TreeNodeCollection) Dim node As System.Xml.XmlNode For Each node In document.ChildNodes Dim expr As System.Xml.XmlNode = _ node.SelectSingleNode(xpath_filter) If Not (expr Is Nothing) Then Dim new_child As New TreeNode(expr.Value) nodes.Add(new_child) populateTreeControl(node, new_child.Nodes) End If Next End Sub

At class-level scope add the following line:

[C#] private string xpath_filter = "@id[parent::contacts or parent::contact]";

[VB] Private xpath_filter As String = _ "@id[parent::contacts or parent::contact]"

You use the result of the XPath query to determine whether to recurse into its children. In this case, the query constitutes an inclusion rule, reading "Select the id attribute of any 'contacts' or 'contact' element." But you might equally well use an exclusion rule to identify the data you explicitly want to reject.

attribute::id[not(parent::email or parent::city or parent::country)]

This isn't a generic solution, but using a filter based—like this one—on parent-child relationships, rather than on unqualified node or attribute pattern matches, is an effective way to express the fundamental structures of an XML document when users have substantial editorial rights with respect to its hierarchy. In this case, a simple query was sufficient, but there's no reason you couldn't go for something more complicated.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date