2008-10-09 2 views
4

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

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

ответ

7

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

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

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

As-, вы будете нуждаться в 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», назначается свойству InnerXmlXmlNode, эти теги являются действительно тегами. Никакое экранирование не выполняется при получении или настройке InnerXml, и оно анализируется как XML.

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

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

+0

Спасибо, Джоэл, что работает. Один вопрос. Я окружаю контент в элементе данных секцией CDATA, чтобы можно было хранить Xml. Есть ли способ контролировать, как XmlDataProvider выписывает элемент данных? – 2008-10-09 22:09:52

2

У нас была аналогичная проблема. Вы можете найти полезное сообщение this article. Мы использовали описанный шаблон ViewModel и упростили все.

Смежные вопросы