我已经找到了一种方法,可以在不影响TreeView.DataContext的情况下完成这个操作。绑定很容易。Codebehind也很容易,但有一个小问题。
如果将XmlDataProvider绑定或分配给ItemsSource,则什么都得不到。它不是IEnumerable(虽然它是INotifyPropertyChanged),也没有隐式转换。你想要的是XmlDataProvider.Data,它被声明为Object,但我看到的是运行时类型为XmlDataCollection(它继承自ReadOnlyObservableCollection<XmlNode>)。
MVVM
绑定Data很容易。我不知道将XmlDataProvider放在您的视图模型中是否是纯粹的MVVM,也许不是。
视图模型:
public XmlDataProvider ViewModelXMLDataProp { ... }
XAML
<TreeView
ItemsSource="{Binding ViewModelXMLDataProp.Data}"
...
/>
完成了 - 除非您需要使用Binding
的XPath
属性。如果是这样,您必须使用DataContext
的技巧。您不能在同一个绑定上同时设置Path
和XPath
。
XmlDataProvider
的XPath
属性执行相同的操作。如果您可以使用它,那就很好。
您会认为Binding.Source
会起作用,因为当您的XmlDataProvider
是静态资源时,它可以工作。当Binding.Source
是DataSourceProvider
并且未指定Path
时,Path
默认为Data
:
<TreeView
ItemsSource="{Binding Source={StaticResource MyXmlDataProviderResource}}"
...
/>
但是这只适用于您提供静态资源的情况。下面的代码实际上会绑定到字符串 "ViewModelXMLDataProp"
,而不是查找 DataContext 中是否存在该属性。这样做是不好的。
<TreeView
ItemsSource="{Binding Source=ViewModelXMLDataProp}"
...
/>
也许你可以编写一个MarkupExtension
来实现这个功能,但没有必要。
代码后台
你应该学习和使用MVVM,但事情总有很多原因,你不是来听布道的。
代码后台有点棘手。 TreeView.ItemsSource
只需要你提供的对象实现System.Collections.IEnumerable
接口,所以将provider.Data
强制转换为System.Collections.IEnumerable
,不用担心确切的运行时类型。
现在问题来了:XmlDataProvider.Data
是异步填充的。
protected void LoadXML(String path)
{
var provider =
new XmlDataProvider()
{
Source = new Uri(path, UriKind.Absolute),
XPath = "./*"
};
treeView.ItemsSource = (IEnumerable)provider.Data;
}
我发现即使我创建了一个
XmlDocument
,调用
XmlDocument.Load()
并将文档分配给
XmlDataProvider.Document
时,这仍然是一个问题。当
Data
属性最终被设置时,
Binding
仍然会挂起,并更新
ItemsSource
。但是在您的代码后台文件中对
ItemsSource
进行赋值并不会做任何事情。
与Stack Overflow上普遍流传的民间信仰相反,在以下代码中没有发生任何绑定,也没有以任何方式进行绑定:
// NOT A BINDING
treeView.ItemsSource = someRandomCollectionOfStuff;
如果没有人创建
System.Windows.Data.Binding
或
x:Bind
的实例,那么它就不是绑定。这种区别很重要:“使用
x
的当前值”不是“无限期地更新
y.x
的未来值,每当
y
引发
PropertyChanged
时”。
您可以以编程方式创建
Binding
甚至处理
PropertyChanged
,但他们提供了一个更简单的选项。只需处理
XmlDataProvider.DataChanged
事件即可。
protected void LoadXML(String path)
{
var provider =
new XmlDataProvider()
{
Source = new Uri(path, UriKind.Absolute),
XPath = "./*"
};
provider.DataChanged += (s,e)
=> treeView.ItemsSource = (IEnumerable)provider.Data;
}
这就完成了。你甚至可以保留该提供程序,加载新的XML,并使“DataChanged”事件保持树视图的最新状态。不过,这似乎是一种浪费精力的做法。