Atomic Blography

XPath Variable/Dynamic Parameters in WPF Binding February 14, 2007

This blog is no longer actively maintained. The content is well over 5 years old – which is like 50 coder years. Use at your own risk!

The XML binding support in WPF is excellent and just about anything you’ve ever wanted to know about it’s binding support can be found on Beatriz Costa’s blog. However, as raised by Grae Foster in a recent WPF Forumposting the Binding support is not without limitations such as the lack of support for parameterising an XPath expression. Let’s just confirm what it can do..

<Binding XPath="root/reports/report[key='5']"/>

If you want to parameterize your XPath expression with literal values, in this case ‘5’, then you can do it with little bother. This would be useful if you had a button which filtered your xml source based on specific well known values, say enum values, but it’s no good if the value comes from somewhere else in the app. Unfortunately this second scenario is very common.

Another, unsupported scenario that causes problems is if you want to allow the user to make a selection from one part of the xml, and use it to select a separate section from the xml. Sounds a bit specialist? It’s not, and better explained by example..

In the xml above, the categories would be bound to a ComboBox so that the user can select one. Below this would be a ListBox, which would display the name of the reports in that category. The finished app is shown below.

The difficulty in getting this (apparently simple) master-detail app to work is that the ‘master’ xml is in a separate part of the document to the ‘detail’ xml. This post explains how you can resolve this issue.

First wrap the xml data island up in an XmlDataProvider, and key it as xmlData. Then bind the category elements to a ComboBox. Finally define a template so that the category name is displayed for the user. All straight forward..

Now we need to bind the ListBox. We know that the contents of the ListBox has to depend on the user selection in the ComboBox, so represent this by binding the DataContext of the ListBox to the SelectedItem of the ComboBox. The ListBox.DataContext will now be a category element (or null). Then create bindings on the ListBox ItemTemplate which will pull out the name and description of the reports that correspond to the selected category. All sounds fine, but there’s no XPath statement defined.

From the perspective of the ListBox, our DataContext will be a category element but we need a set of report elements. Thankfully the Binding class has a Converter parameter, which can be used to convert the input values of a binding. Bingo – that’s exactly what we want to do. I’ve defined an XmlConverter class that can be used to perform that conversion, which would be used as follows..

The XPath property of the ListBox Binding is used to select the specific @key values that uniquely identify the report elements that we want from the other part of the xml. So the input to the conversion is now a collection of XmlAttributes. Ok, but let’s imagine for a second that we had literal values, instead of XmlAttributes. What would the XPath look like?

/root/reportdefinitions/reportdef[@defkey=’rep1‘ or @defkey=’rep2‘]

We don’t have literal values, so this needs to be broken down into 3 parts. An XPath template, an XPath condition and the XPath condition values. The XPath template (shown in red) is the bit that identifies what should be bound to the control, in our case we want the reportdef elements. The XPath condition (shown in blue) will be evaluated to determine which of the elements identified by the XPath template will be included, in this case the ones for the chosen category. In our case above the condition appears twice because there’s two condition values (shown in green) in the current category. The XPath template and XPath condition are declared in the xaml as shown above.

So that’s all there is to it, in terms of xaml. Just need to explain what the XmlConverter and XmlConverterParameter classes actually do. The XmlConverterParameter class is just a DependencyObject with 2 DependencyProperties declared. (Bear in mind this means that these values can also be data bound.)

And the XmlConverter? Well that’s pretty simple too, it just implements IValueConverter.Convert. This method checks the input, it then builds up the condition using the given XmlNode array (in our case the @key values) and then executes the XPath statement against the xml to find the resultant xml (in our case the reportdef elements).

The XmlConverter class clearly won’t support every scenario. If you have another common scenario, you could create a different XmlConverterParameter object which allows the XPath query to be built up in a different way. But what if you want a complete solution? Well then you need to address the fundamental problem… you need a powerful expressive way of selecting one set of nodes given another set of nodes. Three methods spring to mind…

First you could use XSL. You could implement an XslConverter, to replace the XmlConverter class which accepts an XSL fragment to perform the select. However XSL uses XPath and so you’re likely to run into the same problems, specifically, finding a way to merge the source multiple input XmlNodes with the ‘select’ statement. An XSL solution also presents problem of producing a new XmlDocument as the output, which would probably have to be related back to the original xml data. Second thoughts, this isn’t a solution at all.

Second, you could use XQuery. This is a much better tool for the job, although it’s been cooking for so long that the world has moved on. In addition, Microsoft dropped support for XQuery along time ago (Microsoft.Xml.XQuery). You can still get their implementation of it from 4guysfromrolla, and it seems to work fairly well, but obviously this is a non-supported approach. Microsoft did include an implementation in Sql Server 2005, so you could (in theory) off-load the processing to it, but there’s far more drawbacks than benefits to this approach. XQuery support may re-appear but i believe the architect on this project, Michael Brundage, is now working on the XBox – so I wouldn’t hold your breath.

Thirdly, you could use XLinq, and you can get an overview here. It provides all the expressiveness of XQuery, along with countless other advantages. This is the best solution, and will kill the parameterized XPath problem dead, by allowing you to perform a join on the xml. I suspect that’s why the XPath parameterization problem has been overlooked for now. It simple wasn’t worth producing a half way house solution. You can download the May 2006 CTP to create your own Binding Converter that uses XLinq to do the work. If you can wait though, the necessary functionality will probably appear natively, e.g. in the Binding class, or probably something much better that I never would have thought of.

[…] This post by Karl Hulme almost did what I wanted but not quite. it still wasn’t flexible enough to perform the type of binding I wanted to solve this particular problem, which was, to basically externalise rich tool tip data that was held in an external file, so the client could easily update the data to be shown based on control names on a form. The tool tip had to show not only text, but other rich content that was configured from an XML data source. […]

It is a nice article about related controls binding. But is it relevant now with WPF 4.0? Also I need to do this kind of operation in a DataGrid Row with two different ComboBoxes? Is this approach will work in multiple Rows?

Believing that you are actually starving, it releases chemicals that
actually make it harder to lose weight in an effort to conserve energy.
Vitamin B3 or also called as Niacin is important in weight loss because
it is responsible for the regulation of thyroid hormones and
also in sugar levels in the body. Hence, broccoli is a must-add ingredient in your vegetarian weight loss diet.