Двустороннее связывание данных XML с WPF TreeView - PullRequest
4 голосов
/ 09 октября 2008

Я пытаюсь переписать мое приложение ForestPad , использующее WPF для уровня представления. В WinForms я заполняю каждый узел программно, но я хотел бы использовать возможности WPF для привязки данных, если это возможно.

В общем, каков наилучший способ двусторонней передачи данных для связывания WPF TreeView с документом XML?

Универсальное решение хорошо, но для справки, структура документа 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>

Ответы [ 2 ]

7 голосов
/ 09 октября 2008

Ну, было бы проще, если бы ваша иерархия элементов была больше похожа на ...

<node type="forest">
    <node type="tree">
        ...

... а не ваша текущая схема.

Как есть, вам потребуется 4 HierarchicalDataTemplate с, по одному на каждый иерархический элемент, включая корневой, и один 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>

Вместо этого можно программно установить Source из XmlDataProvider:

dp = this.FindResource( "dataxml" ) as XmlDataProvider;
dp.Source = new Uri( @"D:\fp.xml" );

Кроме того, восстановить ваши изменения так же просто, как это:

dp.Document.Save( dp.Source.LocalPath );

Самому TreeView требуется Name и ItemsSource, связанные с XmlDataProvider:

<TreeView
    Name="treeview"
    ItemsSource="{Binding Source={StaticResource dataxml}, XPath=.}">

В этом примере я сделал TwoWay привязку с TextBox es на каждом узле, но когда дело доходит до редактирования только одного узла за раз в отдельном, единственном TextBox или другом элементе управления, вы будете связывать это к текущему выбранному элементу TreeView. Вы также изменили бы TextBox es на TextBlock s, так как нажатие на TextBox фактически не выбирает соответствующий TreeViewItem.

<TextBox
    DataContext="{Binding ElementName=treeview, Path=SelectedItem}"
    Text="{Binding XPath=data, UpdateSourceTrigger=PropertyChanged}"/>

Причина, по которой вы должны использовать два Binding с, заключается в том, что вы не можете использовать Path и XPath вместе.

Edit:

Тимоти Ли Рассел спросил о сохранении CDATA в элементах данных. Сначала немного по InnerXml и InnerText.

За кулисами XmlDataProvider использует XmlDocument с деревом XmlNodes. Когда строка, такая как "stuff", присваивается свойству InnerXml XmlNode, тогда эти теги действительно являются тегами. При получении или установке InnerXml экранирование не выполняется, и оно анализируется как XML.

Однако если вместо этого присваивать свойству InnerText, угловые скобки будут экранированы с помощью сущностей & lt; и & gt ;. Обратное происходит при получении значения. Объекты (например, & lt;) преобразуются обратно в символы (например, <). </p>

Поэтому, если строки, которые мы храним в элементах данных, содержат 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 );

Если узел уже имеет раздел CDATA и значение не было изменено каким-либо образом, то он все еще имеет раздел CDATA, и мы по существу заменим его тем же. Однако, через нашу привязку, если мы изменим значение содержимого элементов данных, он заменит CDATA в пользу экранированной строки. Тогда мы должны исправить их.

2 голосов
/ 09 октября 2008

У нас была похожая проблема. Вы можете прочитать эту статью полезной. Мы использовали описанный паттерн ViewModel, и он все упростил.

...