Привязка XML-данных к WPF TreeView Control - PullRequest
2 голосов
/ 03 апреля 2009

Я потратил много времени, пытаясь выяснить, как связать данные в моем 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 ]

5 голосов
/ 22 ноября 2011

С учетом следующего 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";
    }
}

Выход:

tree view output

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

XmlData.XPath = "/node/node";
3 голосов
/ 04 апреля 2009

Вот дерево:

<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.

1 голос
/ 21 февраля 2017

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...