Я понял, как это сделать, не наступая на TreeView.DataContext
. Связывание легко. Codebehind почти так же легко, но с одной маленькой ошибкой.
Вы ничего не получите, если связываете или назначаете XmlDataProvider
на ItemsSource
. Это не IEnumerable
(хотя это INotifyPropertyChanged
), и нет неявного преобразования. То, что вам нужно, это XmlDataProvider.Data
, который объявлен как Object
, но я вижу тип времени выполнения XmlDataCollection
(который наследуется от ReadOnlyObservableCollection<XmlNode>
).
MVVM
Связывание Data
легко. Я не знаю, если это чисто MVVM, чтобы поместить XmlDataProvider
в вашу модель представления, возможно, нет.
ViewModel:
public XmlDataProvider ViewModelXMLDataProp { ... }
XAML
<TreeView
ItemsSource="{Binding ViewModelXMLDataProp.Data}"
...
/>
Готово - то есть, если вам не нужно использовать XPath
свойство Binding
. Если вы это сделаете, вы должны использовать DataContext
Kludge. Вы не можете установить Path
и XPath
на одну и ту же привязку.
Свойство XPath XmlDataProvider
делает то же самое. Если вы можете работать с этим, у вас все хорошо.
Вы могли бы подумать, что Binding.Source
будет работать, потому что это работает, когда ваш XmlDataProvider
является статическим ресурсом. Когда Binding.Source
является DataSourceProvider
и Path
не указан, Path
по умолчанию Data
:
<TreeView
ItemsSource="{Binding Source={StaticResource MyXmlDataProviderResource}}"
...
/>
... но это работает, потому что вы предоставляете ему статический ресурс. Следующее фактически связывается со строкой "ViewModelXMLDataProp"
, а не ищет DataContext
для свойства с таким именем. Это не хорошо.
<TreeView
ItemsSource="{Binding Source=ViewModelXMLDataProp}"
...
/>
Может быть, вы могли бы написать MarkupExtension
, который бы сработал, но в этом нет необходимости.
Codebehind
Вы должны учиться и использовать MVVM, но вещи случаются по многим причинам, и вы пришли сюда не для проповеди.
Codebehind немного сложнее. 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 = "./*"
};
// FAIL: provider.Data is still null
treeView.ItemsSource = (IEnumerable)provider.Data;
}
Я обнаружил, что это проблема, даже когда я создаю XmlDocument
, вызываю XmlDocument.Load()
и назначаю документ XmlDataProvider.Document
. Binding
все еще будет зависать, когда свойство Data
будет окончательно установлено, и тогда оно обновит ItemsSource
. Но присвоение ItemsSource
в вашем коде позади файла не делает такой вещи.
Вопреки вездесущему народному поверью в переполнении стека, в следующем коде не происходит связывания, и ни в каком смысле ничего не было связано:
// 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
, поддерживающее текущее древовидное представление. Похоже, трата усилий, хотя.