Using binding to XML in Silverlight
I'm currently having trouble binding XML to a GUI in a Silverlight application. Especially with TwoWay-Binding.
As we know it is really easy to do in a Windows client application using WPF. There you can just do something like:
XML:
<person>
<firstname>Test</firstname>
<surname>Test</surname>
<email>testc@testc.com</email>
</person>
and a XAML page viewing a Grid to edit (Binding using either XLinq or XPath):
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="221*" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="First name:" />
<TextBox Grid.Column="1" Grid开发者_开发问答.Row="0" Text="{Binding Path=Element[firstname].Value, Mode=TwoWay}" />
<TextBlock Grid.Column="0" Grid.Row="1" Text="Surname:" />
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding Path=Element[surname].Value, Mode=TwoWay}" />
<TextBlock Grid.Column="0" Grid.Row="2" Text="EMail:" />
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding Path=Element[email].Value, Mode=TwoWay}" />
</Grid>
Because of the TwoWay-Mode the user writes directly into the XML.
However, in Silverlight there is no option to bind like in the example above. But Microsoft added the XPathEvaluate-Method() in Silverlight 4.
So I was trying to bind the complete XDocument to every TextBox and use a converter along with the ConverterParameter to pass a XPath expression and evaluate it.
<Grid x:Name="LayoutRoot">
<TextBlock Text="{Binding Path=Data, Converter={StaticResource TestKonverter}, ConverterParameter=//firstname, Mode=TwoWay}" FontSize="20" />
</Grid>
and...
public class XMLConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var __doc = (XDocument)value;
var __xpath = (IEnumerable)__doc.XPathEvaluate(parameter.ToString());
return (__xpath.Cast<XElement>().FirstOrDefault());
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//getting the attached XDocument again as __doc ?!
var __xpath = (IEnumerable)__doc.XPathEvaluate(parameter.ToString());
(__xpath.Cast<XElement>().FirstOrDefault()).Value = value.ToString();
return value;
}
}
To get some kind of TwoWay-Binding, I thought of using the XPath-Expression to get the correct Node and write the new value in it. The problem is, in the ConvertBack-Method() I don't see a way how to get the XDocument. Is there any way to get the XDocument through the given parameters in ConvertBack, without making it somewhere as static?
Thanks for your answer.
About two weeks ago I solved this problem, by just creating a new class called XTextBox. I simple inherit from TextBox and create two DependencyProperties with the types XElement and string in the new class.
In my XAML I can use binding as i did before and hand over the complete XElement and the XPath to find the right node to my control.
<custom:XTextBox Data="{Binding Path=XValues, Mode=TwoWay}" XPath="//Measurements/Headline/Fontsize" />
Aside from the two DependencyProperties I use a normal TextChanged Event to update the Value in the XElement using the XPath again.
This completley solves TwoWay Binding, because I never have to create a new XElement and copy anything around. I also implemented XComboBox, XRadioButton and XCheckBox, which is enough in my case.
quote from a silverlight forum question -
"You can create a class to hold your data and implement INotifyPropertyChanged and then read or write your xml in code.
If the format of your xml is not already defined, you can use 'DataContractSerializer'.
write xml example :
YourDataClassType data = new YourDataClassType();
MemoryStream ms = new MemoryStream();
DataContractSerializer ds = new DataContractSerializer(typeof(YourDataClassType));
ds.WriteObject(ms, data);
ms.Flush();
ms.Seek(0, SeekOrigin.Begin);
StreamReader sr = new StreamReader(ms);
string xml = sr.ReadToEnd();
sr.Close();
ms.Close();
read xml example :
MemoryStream ms = new MemoryStream();
StreamWriter sw = new StreamWriter(ms);
sw.Write(xml);
sw.Flush();
ms.Seek(0, SeekOrigin.Begin);
DataContractSerializer ds = new DataContractSerializer(typeof(YourDataClassType));
YourDataClassType data = ds.ReadObject(ms) as YourDataClassType;
sw.Close();
ms.Close();
where YourDataClassType is your class that reflects the data. This way you don't have to parse the xml yourself."
精彩评论