Bilateral Xml data binding to WPF TreeView

I am trying to rewrite my ForestPad application using WPF for presentation level. In WinForms, I populate each node programmatically, but I would like to take advantage of WPF data binding capabilities, if possible.

In general, what is the best way to two-way bind WPF TreeView data to an Xml document?

The general solution is fine, but for reference, the structure of the Xml document I'm trying to link looks like this:

<?xml version="1.0" encoding="utf-8"?> <forestPad guid="6c9325de-dfbe-4878-9d91-1a9f1a7696b0" created="5/14/2004 1:05:10 AM" updated="5/14/2004 1:07:41 AM"> <forest name="A forest node" guid="b441a196-7468-47c8-a010-7ff83429a37b" created="01/01/2003 1:00:00 AM" updated="5/14/2004 1:06:15 AM"> <data> <![CDATA[A forest node This is the text of the forest node.]]> </data> <tree name="A tree node" guid="768eae66-e9df-4999-b950-01fa9be1a5cf" created="5/14/2004 1:05:38 AM" updated="5/14/2004 1:06:11 AM"> <data> <![CDATA[A tree node This is the text of the tree node.]]> </data> <branch name="A branch node" guid="be4b0993-d4e4-4249-8aa5-fa9c940ae2be" created="5/14/2004 1:06:00 AM" updated="5/14/2004 1:06:24 AM"> <data> <![CDATA[A branch node This is the text of the branch node.]]></data> <leaf name="A leaf node" guid="9c76ff4e-3ae2-450e-b1d2-232b687214aa" created="5/14/2004 1:06:26 AM" updated="5/14/2004 1:06:38 AM"> <data> <![CDATA[A leaf node This is the text of the leaf node.]]> </data> </leaf> </branch> </tree> </forest> </forestPad> 
+4
c # xml data-binding wpf treeview
source share
2 answers

Well, it would be easier if your hierarchy of elements was more similar ...

 <node type="forest"> <node type="tree"> ... 

... not your current schema.

As-is, you will need 4 HierarchicalDataTemplate s, one for each hierarchical element, including the root, and one DataTemplate for leaf :

 <Window.Resources> <HierarchicalDataTemplate DataType="forestPad" ItemsSource="{Binding XPath=forest}"> <TextBlock Text="a forestpad" /> </HierarchicalDataTemplate> <HierarchicalDataTemplate DataType="forest" ItemsSource="{Binding XPath=tree}"> <TextBox Text="{Binding XPath=data}" /> </HierarchicalDataTemplate> <HierarchicalDataTemplate DataType="tree" ItemsSource="{Binding XPath=branch}"> <TextBox Text="{Binding XPath=data}" /> </HierarchicalDataTemplate> <HierarchicalDataTemplate DataType="branch" ItemsSource="{Binding XPath=leaf}"> <TextBox Text="{Binding XPath=data}" /> </HierarchicalDataTemplate> <DataTemplate DataType="leaf"> <TextBox Text="{Binding XPath=data}" /> </DataTemplate> <XmlDataProvider x:Key="dataxml" XPath="forestPad" Source="D:\fp.xml"> </XmlDataProvider> </Window.Resources> 

You can programmatically configure Source XmlDataProvider :

 dp = this.FindResource( "dataxml" ) as XmlDataProvider; dp.Source = new Uri( @"D:\fp.xml" ); 

Also, re-saving your changes is just as easy:

 dp.Document.Save( dp.Source.LocalPath ); 

TreeView itself requires Name and ItemsSource associated with the XmlDataProvider :

 <TreeView Name="treeview" ItemsSource="{Binding Source={StaticResource dataxml}, XPath=.}"> 

In this example, I TwoWay to TextBox es on each node, but when it comes to editing only one node at a time in a separate single TextBox or other control, you bind it to the currently selected TreeView . You can also change the above TextBox es to TextBlock s, since clicking in the TextBox does not actually display the corresponding TreeViewItem .

 <TextBox DataContext="{Binding ElementName=treeview, Path=SelectedItem}" Text="{Binding XPath=data, UpdateSourceTrigger=PropertyChanged}"/> 

The reason you should use two Binding is because you cannot use Path and XPath together.

Edit:

Timothy Lee Russell asked about storing CDATA in data items. First a bit on InnerXml and InnerText .

Behind the scenes of the XmlDataProvider , an XmlDocument used, with it the XmlNodes tree. When a string, such as “stuff,” is assigned to the InnerXml property of the XmlNode , then these tags are really tags. InnerXml not performed when InnerXml retrieved or configured, and it is parsed as XML.

However, if the InnerText property is InnerText , the angle brackets will be escaped with entities <lt; lt; and ?. The converse happens when a value is returned. Entities (e.g. & lt;) are allowed back to characters (e.g. <).

Therefore, if the rows stored in the data elements contain XML, the objects have been escaped, and we need to undo this by simply restoring the InnerText before adding the CDATA section as a child of the node ...

 XmlDocument doc = dp.Document; XmlNodeList nodes = doc.SelectNodes( "//data" ); foreach ( XmlNode node in nodes ) { string data = node.InnerText; node.InnerText = ""; XmlCDataSection cdata = doc.CreateCDataSection( data ); node.AppendChild( cdata ); } doc.Save( dp.Source.LocalPath ); 

If node already has a CDATA partition, and the value has not been changed in any way, it still has a CDATA partition, and we essentially replace it with the same. However, thanks to our binding, if we change the value of the contents of the data elements, it replaces CDATA in favor of the escaped string. Then we have to fix them.

+7
source share

We had a similar problem. You may find this article helpful. We used the described ViewModel template and simplified everything.

+2
source share

All Articles