C# Xml 数据到 WPF TreeView 的双向绑定
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/188001/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Two-way binding of Xml data to the WPF TreeView
提问by Timothy Lee Russell
I am attempting to rewrite my ForestPadapplication utilizing WPF for the presentation layer. In WinForms, I am populating each node programmatically but I would like to take advantage of the databinding capabilities of WPF, if possible.
我正在尝试使用 WPF 为表示层重写我的ForestPad应用程序。在 WinForms 中,我以编程方式填充每个节点,但如果可能,我想利用 WPF 的数据绑定功能。
In general, what is the best way to two-way databind the WPF TreeView to an Xml document?
一般来说,将 WPF TreeView 双向数据绑定到 Xml 文档的最佳方法是什么?
A generic solution is fine but for reference, the structure of the Xml document that I am trying to bind to looks like this:
通用解决方案很好,但作为参考,我尝试绑定的 Xml 文档的结构如下所示:
<?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>
采纳答案by Joel B Fant
Well, it would be easier if your element hierarchy was more like...
好吧,如果您的元素层次结构更像......
<node type="forest">
<node type="tree">
...
...rather than your current schema.
...而不是您当前的架构。
As-is, you'll need 4 HierarchicalDataTemplate
s, one for each hierarchical element including the root, and one DataTemplate
for leaf
elements:
按原样,您将需要 4 个HierarchicalDataTemplate
s,一个用于包括根在内的每个分层元素,一个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>
You can instead set the Source
of the XmlDataProvider
programmatically:
您可以改为Source
以XmlDataProvider
编程方式设置 的:
dp = this.FindResource( "dataxml" ) as XmlDataProvider;
dp.Source = new Uri( @"D:\fp.xml" );
Also, re-saving your edits is as easy as this:
此外,重新保存您的编辑就像这样简单:
dp.Document.Save( dp.Source.LocalPath );
The TreeView
itself needs a Name
and an ItemsSource
bonded to the XmlDataProvider
:
将TreeView
自身需要Name
和ItemsSource
结合到XmlDataProvider
:
<TreeView
Name="treeview"
ItemsSource="{Binding Source={StaticResource dataxml}, XPath=.}">
I this example, I did TwoWay
binding with TextBox
es on each node, but when it comes to editing just one node at a time in a separate, single TextBox
or other control, you would be binding it to the currently selected item of the TreeView
. You would also change the above TextBox
es to TextBlock
s, as clicking in the TextBox
does not actually select the corresponding TreeViewItem
.
在这个例子中,我在每个节点上TwoWay
使用TextBox
es 进行了绑定,但是当在单独的、单个TextBox
或其他控件中一次只编辑一个节点时,您会将其绑定到TreeView
. 您还可以将上面的TextBox
es更改为TextBlock
s,因为单击TextBox
并实际上并未选择相应的TreeViewItem
.
<TextBox
DataContext="{Binding ElementName=treeview, Path=SelectedItem}"
Text="{Binding XPath=data, UpdateSourceTrigger=PropertyChanged}"/>
The reason you must use two Binding
s is that you cannot use Path
and XPath
together.
必须使用两个Binding
s的原因是不能将Path
andXPath
一起使用。
Edit:
编辑:
Timothy Lee Russell asked about saving CDATA to the data elements. First, a little on InnerXml
and InnerText
.
Timothy Lee Russell 询问了将 CDATA 保存到数据元素的问题。首先,有点关于InnerXml
和InnerText
。
Behind the scenes, XmlDataProvider
is using an XmlDocument
, with it's tree of XmlNodes
. When a string such as "stuff" is assigned to the InnerXml
property of an XmlNode
, then those tags are really tags. No escaping is done when getting or setting InnerXml
, and it is parsed as XML.
在幕后,XmlDataProvider
使用的是XmlDocument
,它是XmlNodes
. 当将诸如“stuff”之类的字符串分配给InnerXml
an的属性时XmlNode
,这些标签就是真正的标签。获取或设置时不进行转义InnerXml
,并解析为 XML。
However, if it is instead assigned to the InnerText
property, the angle brackets will be escaped with entities < and >. The reverse happens when the value is retreived. Entities (like <) are resolved back into characters (like <).
但是,如果它改为分配给InnerText
属性,则尖括号将被转义为实体 < 和 >。取回该值时会发生相反的情况。实体(如 <)被解析回字符(如 <)。
Therefore, if the strings we store in the data elements contain XML, entities have been escaped, and we need to undo that simply by retrieving InnerText
before adding a CDATA section as the node's child...
因此,如果我们存储在数据元素中的字符串包含 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 );
If the node already has a CDATA section and the value has not been changed in any way, then it still has a CDATA section and we essentially replace it with the same. However, through our binding, if we change the value of the data elements contents, it replaces the CDATA in favor of an escaped string. Then we have to fix them.
如果节点已经有一个 CDATA 部分并且值没有以任何方式改变,那么它仍然有一个 CDATA 部分,我们基本上用相同的部分替换它。但是,通过我们的绑定,如果我们更改数据元素内容的值,它会替换 CDATA 以支持转义字符串。然后我们必须修复它们。
回答by Shaun Bowe
We had a similar issue. You may find reading this articlehelpful. We used the ViewModel pattern described and it simplified everything.
我们有一个类似的问题。您可能会发现阅读这篇文章很有帮助。我们使用了所描述的 ViewModel 模式,它简化了一切。