Ну, было бы проще, если бы ваша иерархия элементов была больше похожа на ...
<node type="forest">
<node type="tree">
...
... а не ваша текущая схема.
Как есть, вам потребуется 4 HierarchicalDataTemplate
с, по одному на каждый иерархический элемент, включая корневой, и один DataTemplate
для 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>
Вместо этого можно программно установить Source
из XmlDataProvider
:
dp = this.FindResource( "dataxml" ) as XmlDataProvider;
dp.Source = new Uri( @"D:\fp.xml" );
Кроме того, восстановить ваши изменения так же просто, как это:
dp.Document.Save( dp.Source.LocalPath );
Самому TreeView
требуется Name
и ItemsSource
, связанные с XmlDataProvider
:
<TreeView
Name="treeview"
ItemsSource="{Binding Source={StaticResource dataxml}, XPath=.}">
В этом примере я сделал TwoWay
привязку с TextBox
es на каждом узле, но когда дело доходит до редактирования только одного узла за раз в отдельном, единственном TextBox
или другом элементе управления, вы будете связывать это к текущему выбранному элементу TreeView
. Вы также изменили бы TextBox
es на TextBlock
s, так как нажатие на TextBox
фактически не выбирает соответствующий TreeViewItem
.
<TextBox
DataContext="{Binding ElementName=treeview, Path=SelectedItem}"
Text="{Binding XPath=data, UpdateSourceTrigger=PropertyChanged}"/>
Причина, по которой вы должны использовать два Binding
с, заключается в том, что вы не можете использовать Path
и XPath
вместе.
Edit:
Тимоти Ли Рассел спросил о сохранении CDATA в элементах данных. Сначала немного по InnerXml
и InnerText
.
За кулисами XmlDataProvider
использует XmlDocument
с деревом XmlNodes
. Когда строка, такая как "stuff", присваивается свойству InnerXml
XmlNode
, тогда эти теги действительно являются тегами. При получении или установке InnerXml
экранирование не выполняется, и оно анализируется как XML.
Однако если вместо этого присваивать свойству InnerText
, угловые скобки будут экранированы с помощью сущностей & lt; и & gt ;. Обратное происходит при получении значения. Объекты (например, & lt;) преобразуются обратно в символы (например, <). </p>
Поэтому, если строки, которые мы храним в элементах данных, содержат XML, сущности были экранированы, и нам нужно отменить это, просто получив InnerText
перед добавлением раздела CDATA в качестве дочернего узла ...
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 );
Если узел уже имеет раздел CDATA и значение не было изменено каким-либо образом, то он все еще имеет раздел CDATA, и мы по существу заменим его тем же. Однако, через нашу привязку, если мы изменим значение содержимого элементов данных, он заменит CDATA в пользу экранированной строки. Тогда мы должны исправить их.