views:

2238

answers:

4

Lets say I have a simple chunck of XML:-

<root>
   <item forename="Fred" surname="Flintstone" />
   <item forename="Barney" surname="Rubble" />
</root>

Having fetched this XML in Silverlight I would like to bind it with xaml of this ilke:-

<ListBox x:Name="ItemList" Style="{StaticResource Items}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">     
                <TextBox Text="{Binding Forename}" />
                <TextBox Text="{Binding Surname}" />  
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Now I can bind simply enough with LINQ to XML and a nominal class:-

public class Person {
     public string Forename {get; set;} 
     public string Surname {get; set;}
}

So here is the question, can it be done without this class? IOW coupling between the Sliverlight code and the input XML is limited to the XAML only, other source code is agnostic to the set of attributes on the item element.

Edit: The use of XSD is suggested but ultimately it amounts the same thing. XSD->Generated class.

Edit: An anonymous class doesn't work, Silverlight can't bind them.

Edit: This needs to be two way, the user needs to be able to edit the values and these value end up in the XML. (Changed original TextBlock to TextBox in sample above).

A: 

As far as I'm aware the Silverlight Binding lacks the XPath properties found in WPF so there is no nice way to bind directly to XML. When I've encountered this problem I've used xsd.exe against a schema to generate my classes and then use Xml Serialization to populate them. It's not ideal but at least I'm not writing and maintaining the classes and mappings myself.

David Padbury
Thanks for the response. XSDs were the closest I've got too. I was hoping for something more flexible.
AnthonyWJones
+2  A: 

You can do this with an IValueConverter. Here is a simple one:

public class XAttributeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var xml = value as XElement;
        var name = parameter as string;
        return xml.Attribute(name).Value;
    }
}

Then in your Xaml you can reference the type converter and pass the attribute name as the parameter:

<ListBox x:Name="ItemList">
    <ListBox.Resources>
        <local:XAttributeConverter x:Name="xcvt" />
    </ListBox.Resources>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Converter={StaticResource xcvt}, ConverterParameter=forename}" />
                <TextBlock Text="{Binding Converter={StaticResource xcvt}, ConverterParameter=surname}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

This is when you bind to the xml loaded in an XElement:

XElement xml = XElement.Parse("<root><item forename='Fred' surname='Flintstone' /><item forename='Barney' surname='Rubble' /></root>");

ItemList.ItemsSource = xml.Descendants("item");

Not a super elegant binding syntax, but it does work and is easier than mapping to classes.

Bryant
This is closest I got. I appreciate the effort your putting in and I apologies for my poor questioning. The problem I had is making this approach two way. (My original Q contained TextBlock I meant TextBox). I need this to be editable.
AnthonyWJones
A: 

Could you do something similar to what Bryant is suggesting with a query that uses an anonymous class?

i.e.:

var data = from c in xml.Descendants("item")
                       select new  { Forename = c.Attribute("forename").Value, 
                                     Surname = c.Attribute("surname").Value };
ItemList.ItemsSource = data

I think this should work, but I'm not somewhere I could test it out. If it doesn't, somebody let me know why because now I'm interested.

MojoFilter
Ok, it occurs to me that there must me something fundamentally wrong with this or it would have been the obvious answer. So, what part of this doesn't work?
MojoFilter
The result of the linq query is not a list of objects but a {System.Linq.Enumerable.WhereSelectEnumerableIterator<System.Xml.Linq.XElement,<>f__AnonymousType0<System.Xml.Linq.XAttribute,System.Xml.Linq.XAttribute>>} which I don't think is bindable. Trying a few tweaks to see if I can make it work.
Bryant
I also tried casting it to a list using ToList(), but either way I get the exception:((System.MethodAccessException)e.ExceptionObject).Message with a message of <>f__AnonymousType0`2.get_Forename().
Bryant
You can't bind to anonymous types. Silverlight needs public classes and anonymous types are intrinsically internal.
AnthonyWJones
Ok, well I guess that makes sense. I didn't realize that Silverlight couldn't bind to anonymous types.
MojoFilter
+1  A: 

see here for info on binding to anonymous types in silverlight:

http://grahammurray.wordpress.com/2010/05/30/binding-to-anonymous-types-in-silverlight/

Graham Murray
+5 Billion point, my hat comes off to you!
AnthonyWJones