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.