views:

467

answers:

2

I have an XML file similar in structure to the following:

<Parent>
<Child>5</Child>
<Child>3</Child>
<Child>5</Child>
<Child>1</Child>
</Parent>

In my XAML, I have a ListView bound to the XML file and have set the DataTemplate of a ListViewItem to be bound as follows:

<TextBlock Text="{Binding XPath=Parent/Child}"/>

Obviously, I'm expecting 4 results for this XPath query, but I can't seem to find a way to convert the results to a comma-delimited string, and right now, the TextBlock is just displaying the first value.

If I use the same XPath query for setting the ItemsSource of a ListBox, I get all the results in the ListBox, so I think I should be able to get all the values passed to a Converter class...

A: 

If you want to set the XML data as source to your ListView you can do like this:

MyListView.ItemsSource = XElement.Load(@"XMLFile1.xml").Elements("Child");

and you bind to the Value property in the TextBlock:

<TextBlock Text="{Binding Path=Value}" />

If you need to modify the query you can extract the content of your XML file to a var that you can use as ItemsSource on your ListView.

XElement xmlData = XElement.Load(@"XMLFile1.xml");
var query = from x in xmlData.Elements("Child")
      select x;
xamlgeek
Perhaps, I'm not following you very well, but I'm afraid my ListView is a little bit more complicated than just the single ListViewItem.I was trying to do this without much code-behind since it "seems" like there should be a way to convert XPath queries which return multiple results.
Carl
A: 

There's no way to have an XPath query that returns multiple nodes, such as yours, aggregate them into a single value for you. What's happening is that a nodeset is being returned and, since you're binding to a single string property, the infrastructure is simply coercing that nodeset by grabbing the first node from the set and then grabbing its @text node.

Honestly I have not tried this myself and don't have time at the moment, but the only way I'd expect this to ever work is if you wrote a custom IValueConverter. I assume that it will hand an XmlNodeList as the value to be converted and then you can enumerate those nodes and concatenate a comma separated string yourself.

Update

Since the IValueConverter suggestion did not work due to the XPath engine doing a pre-coercion, here's what I suggest you do: instead of binding to a single TextBlock, bind to an ItemsControl instead and define the ItemTemplate for the ItemsControl to be as follows:

<DataTemplate>
    <TextBlock Text="{Binding}"/>,
</DataTemplate>

Note: in all honesty I'm taking the lazy approach in the DataTemplate and you will end up with a comma even after the last item right now. That said you should be able to define a DataTemplate with a trigger that determines that it's the last item and doesn't show the comma.

Finally, depending on how you want the data to layout also set the ItemsControl's ItemsPanel. I'm assuming you want horizontal flow with wrapping here:

<ItemsPanelTemplate>
    <WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
Drew Marsh
Hmm, I was afraid that was the case. In the IValueConverter I wrote, it seemed I was getting passed a single string, but since it's passed as type object, I was hoping the debugger was doing some simple casting or something.As I said, this works fine when binding <ListBox ItemsSource="{Binding XPath=Parent/Child}"/>, so my guess was that there was coercing before the call to the IValueConverter, I just couldn't confirm it.
Carl
Can't say I'm surprised. If they're using SelectNodes under the covers (which I'm sure they are) the standard XPath conversion for a node-set to a single value is to take the first node from the set. I'll edit my answer with a second suggestion for how you can accomplish this.
Drew Marsh
Thanks, Drew. I appreciate the followup as it probably saved me a bit of time as I was templating a ListBox (ha) instead of going straight to the source- the ItemsControl.
Carl