Привязка XML-данных к WPF TreeView Control

Я потратил много времени, пытаясь выяснить, как связать данные в моем XML-файле с элементом управления TreeView, но я не знаю, с чего начать. Я даже пытался выполнить двустороннее связывание данных Xml с WPF TreeView и пример кода Джоша Смита для проекта кода, но все еще не могу понять, с чего начать!!!

У меня есть XML в файле "C:\SPDependencies.xml" (я могу изменить формат, если требуется)!!!:

  <node type="SPDependencies" Name="SPDependencies">
        <node type="StoredProc" Name="SP1">
                <node type="OperationType" Name="Type1">
                        <node type="TableName" Name="Table1"/>
                        <node type="TableName" Name="Table2"/>
                </node>
                <node type="OperationType" Name="Type2">
                         <node type="TableName" Name="Table1"/>
                        <node type="TableName" Name="Table2"/>
                </node>
                 .....
        </node>
        <node type="StoredProc" Name="SP2">
              <node type="OperationType" Name="Type1">
              ...
              ...
        </node>
</node>

Мне нужно отобразить это в элементе управления Treeview в следующем формате:

<SP1>
   <Type1>
      <Table1>
      <Table2>
      <Table3>
   <Type2>
      <Table1>
      <Table2>
      <Table3>
<SP2>
    <Type1>
........

Спасибо, Абхи.

3 ответа

Учитывая следующий XML-файл:

<node type="SPDependencies" Name="SPDependencies">
  <node type="StoredProc" Name="SP1">
    <node type="OperationType" Name="Type1">
      <node type="TableName" Name="Table1"/>
    </node>
    <node type="OperationType" Name="Type2">
      <node type="TableName" Name="Table1"/>
    </node>
  </node>
  <node type="StoredProc" Name="SP2">
    <node type="OperationType" Name="Type1">
    </node>
  </node>
</node>

Посмотреть:

<Window x:Class="Tree.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Tree"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:ViewModel />
    </Window.DataContext>
    <Window.Resources>
        <HierarchicalDataTemplate x:Key="template">
            <TextBlock Text="{Binding XPath=@Name}" />
            <HierarchicalDataTemplate.ItemsSource>
                <Binding XPath="node" />
            </HierarchicalDataTemplate.ItemsSource>
        </HierarchicalDataTemplate>
    </Window.Resources>
    <Grid DataContext="{Binding Path=XmlData}">
        <TreeView ItemsSource="{Binding}" ItemTemplate="{StaticResource template}">
        </TreeView>
    </Grid>
</Window>

Посмотреть модель:

public class ViewModel
{
    public XmlDataProvider XmlData { get; set; }

    public ViewModel()
    {
        XmlData = new XmlDataProvider();
        XmlData.Source = new Uri(@"C:\input.xml");
        XmlData.XPath = "node";
    }
}

Выход:

вывод в виде дерева

Если вы хотите показать только узлы ниже корня, просто измените XPath на:

XmlData.XPath = "/node/node";

Вот дерево:

<Window.Resources>
    <HierarchicalDataTemplate DataType="node"
                              ItemsSource="{Binding XPath=node}">
        <TextBox Width="Auto"
                 Text="{Binding XPath=@Name, UpdateSourceTrigger=PropertyChanged}" />
    </HierarchicalDataTemplate>

    <XmlDataProvider
        x:Key="xmlDataProvider"
        XPath="node" Source="C:\Data.XML">
    </XmlDataProvider>
</Window.Resources>
<Grid>
    <StackPanel>
        <Button Click="Button_Click">Save</Button>
            <TreeView
                Width="Auto"
                Height="Auto"
                Name="treeview"
                ItemsSource="{Binding Source={StaticResource xmlDataProvider}, XPath=.}"/>
     </StackPanel>
</Grid>

Я добавил простую кнопку, чтобы сохранить изменения. Итак, для вашего метода Button_Click в коде:

XmlDataProvider dataProvider = this.FindResource("xmlDataProvider") as XmlDataProvider;
dataProvider.Document.Save(dataProvider.Source.LocalPath);

Смотрите здесь статью о привязке данных и WPF.

Я понял, как это сделать, не наступая на 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 ляп. Вы не можете установить оба 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 в вашем коде файла нет такой вещи.

Вопреки вездесущему народному убеждению в 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 событие держать дерево в курсе. Похоже, трата усилий, хотя.

Другие вопросы по тегам