Как я могу использовать HierarchicalDataTemplate для отображения XML-элементов и атрибутов? - PullRequest
6 голосов
/ 03 октября 2011

Я хотел бы отобразить произвольный XML в TreeView с разворачивающимися и сворачивающимися узлами, показывая как имя элемента, так и набор атрибутов и их значения.Я думаю, что я могу сделать это с HierarchicalDataTemplate.

Я видел подсказки для использования HierarchicalDataTemplate для отображения произвольных элементов XML и текстовых узлов, например:

  <Window.Resources>
    <HierarchicalDataTemplate x:Key="NodeTemplate">
      <TextBlock x:Name="tbName" Text="?" />
      <HierarchicalDataTemplate.ItemsSource>
        <Binding XPath="child::node()" />
      </HierarchicalDataTemplate.ItemsSource>
      <HierarchicalDataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=NodeType}" Value="Text">
          <Setter TargetName="tbName" Property="Text" Value="{Binding Path=Value}"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=NodeType}" Value="Element">
          <Setter TargetName="tbName" Property="Text" Value="{Binding Path=Name}"/>
        </DataTrigger>
      </HierarchicalDataTemplate.Triggers>
    </HierarchicalDataTemplate>
    <XmlDataProvider x:Key="xmlDataProvider">
    </XmlDataProvider>
  </Window.Resources>
  ....

  <TreeView Name="treeView1"
          ItemsSource="{Binding Source={StaticResource xmlDataProvider}, XPath=*}"
          ItemTemplate= "{StaticResource NodeTemplate}"/>

Что прекрасно работает.Он отображает имена элементов и текст для каждого элемента.Но мой XML использует атрибуты для переноса информации.Схема сложна, и у меня нет ее формального определения, поэтому сейчас я отношусь к ней как к произвольному XML.

Самый простой документ выглядит так:

<c4soap name="GetVersionInfo" seq="" result="1">
  <versions>
    <version name="Director" 
             version="2.1.0.126418" 
             buildtype="" 
             builddate="Jun  1 2011" buildtime="14:52:43" />
    <version name="MediaManager" 
             version="2.1.0.126418" 
             buildtype="" 
             builddate="Jun  1 2011" 
             buildtime="14:36:17" />
  </versions>
</c4soap>

Используя приведенное выше определение HierarchicalDataTemplate, я получаю это для отображения:

enter image description here

Не совсем то, что я хочу.Для каждого узла я хочу отобразить как имя элемента , так и набор атрибутов и их значения.

Я пробовал это:

  <Window.Resources>
    <HierarchicalDataTemplate x:Key="NodeTemplate">
      <WrapPanel
          Focusable="False">
        <TextBlock x:Name="tbName" Text="?" />
        <TextBlock x:Name="tbAttrs" Text="?" />
      </WrapPanel>
      <HierarchicalDataTemplate.ItemsSource>
        <Binding XPath="child::node()" />
      </HierarchicalDataTemplate.ItemsSource>
      <HierarchicalDataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=NodeType}" Value="Text">
          <Setter TargetName="tbName" Property="Text" Value="{Binding Path=Value}"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=NodeType}" Value="Element">
          <Setter TargetName="tbName" Property="Text" Value="{Binding Path=Name}"/>
          <Setter TargetName="tbAttrs" Property="Text" Value="{Binding Path=Attributes}"/>
        </DataTrigger>
      </HierarchicalDataTemplate.Triggers>
    </HierarchicalDataTemplate>
    <XmlDataProvider x:Key="xmlDataProvider">
    </XmlDataProvider>
  </Window.Resources>

... который получаетЯ вроде как близок, но Value="{Binding Path=Attributes}" приводит к отображению «(Collection)» в TreeView.

enter image description here

Как можно просто отобразить все фактические имена и значения атрибутов, в дополнение к имени элемента?

Ответы [ 2 ]

9 голосов
/ 03 октября 2011

Я добавил ItemsControl в шаблон, например:

<Window.Resources>
  <SolidColorBrush x:Key="xmlValueBrush" Color="Blue" />
  <SolidColorBrush x:Key="xmAttributeBrush" Color="Red" />
  <SolidColorBrush x:Key="xmlTagBrush" Color="DarkMagenta" />
  <SolidColorBrush x:Key="xmlMarkBrush" Color="Blue" />
  <DataTemplate x:Key="attributeTemplate">
    <StackPanel Orientation="Horizontal"
                Margin="3,0,0,0"
                HorizontalAlignment="Center">
      <TextBlock Text="{Binding Path=Name}"
                 Foreground="{StaticResource xmAttributeBrush}"/>
      <TextBlock Text="=&quot;"
                 Foreground="{StaticResource xmlMarkBrush}"/>
      <TextBlock Text="{Binding Path=Value}"
                 Foreground="{StaticResource xmlValueBrush}"/>
      <TextBlock Text="&quot;"
                 Foreground="{StaticResource xmlMarkBrush}"/>
    </StackPanel>
  </DataTemplate>

  <HierarchicalDataTemplate x:Key="nodeTemplate">
    <StackPanel Orientation="Horizontal"
        Focusable="False">
      <TextBlock x:Name="tbName" Text="?" />
      <ItemsControl
          ItemTemplate="{StaticResource attributeTemplate}"
          ItemsSource="{Binding Path=Attributes}"
          HorizontalAlignment="Center">
        <ItemsControl.ItemsPanel>
          <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal"/>
          </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
      </ItemsControl>
    </StackPanel>
    <HierarchicalDataTemplate.ItemsSource>
      <Binding XPath="child::node()" />
    </HierarchicalDataTemplate.ItemsSource>
    <HierarchicalDataTemplate.Triggers>
      <DataTrigger Binding="{Binding Path=NodeType}" Value="Text">
        <Setter TargetName="tbName" Property="Text" Value="{Binding Path=Value}"/>
      </DataTrigger>
      <DataTrigger Binding="{Binding Path=NodeType}" Value="Element">
        <Setter TargetName="tbName" Property="Text" Value="{Binding Path=Name}"/>
      </DataTrigger>
    </HierarchicalDataTemplate.Triggers>
  </HierarchicalDataTemplate>
  <XmlDataProvider x:Key="xmlDataProvider">
  </XmlDataProvider>
</Window.Resources>

Теперь отображаются имена элементов и набор атрибутов и их значений, например:

enter image description here

4 голосов
/ 07 октября 2011

Вы также можете использовать селектор шаблонов для разных типов узлов и использовать узел XPath () | @ * для циклического перебора всех типов узлов:

<TreeView
    x:Name="TreeView"
    ItemsSource="{Binding}"
    ItemTemplateSelector="{DynamicResource ResourceKey=NodeTemplateSelector}">
    <TreeView.Resources>
        <HierarchicalDataTemplate x:Key="TextTemplate">
            <Grid>
                <uixml:TextInputControl DataContext="{Binding}" />
            </Grid>
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate x:Key="AttributeTemplate">
            <Grid>
                <uixml:AttributeInputControl DataContext="{Binding}" />
            </Grid>
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate x:Key="NodeTemplate" >
            <TextBlock Text="{Binding Path=Name}" />
            <HierarchicalDataTemplate.ItemsSource>
                <Binding XPath="child::node()|@*" />
            </HierarchicalDataTemplate.ItemsSource>
        </HierarchicalDataTemplate>
        <ui:XmlTemplateSelector
            x:Key="NodeTemplateSelector"
            NodeTemplate="{StaticResource NodeTemplate}"
            TextTemplate="{StaticResource TextTemplate}"
            AttributeTemplate="{StaticResource AttributeTemplate}" />
    </TreeView.Resources>
</TreeView>

и:

public class XmlTemplateSelector:DataTemplateSelector{
    public DataTemplate NodeTemplate { get; set; }
    public DataTemplate TextTemplate { get; set; }
    public DataTemplate AttributeTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container) {
        XmlNode node = (XmlNode)item;
        switch (node.NodeType) {
            case XmlNodeType.Attribute:
                return AttributeTemplate;
            case XmlNodeType.Element:
                return NodeTemplate;
            case XmlNodeType.Text:
                return TextTemplate;
        }
        throw new NotImplementedException(String.Format("not implemented for type {0}", node.NodeType));
    }
}
...