patrick.harkin Ответов: 1

Как отобразить значение XML-узла и дочернего узла на одном уровне WPF treeview?


I have an application which manages audio recordings. In theory, new recording of "My Series" go in a folder "\\Library\My Series" but there are two complications. Firstly, new recordings may come to me with alternate names; for example "Macbeth" might become "William Shakespeare's Macbeth". Secondly, names can contain characters not allowed in folder names - for example "Who Framed Roger Rabbit?". So I have a simple data structure which contains, for each series, the display name for use in the UI, the foldername for use in storage and a list (possibly empty) of aliases, and I have LINQ to retrieve one given another. Here's an example to show the structure of the data file:

<?xml version="1.0" encoding="utf-8"?>
<recordings>
  <series>
    <seriestitle>Series 1</seriestitle>
    <seriesfolder>Series 1</seriesfolder>
    <alias>Series One</alias>
    <alias>Series 1.0</alias>
  </series>
  <series>
    <seriestitle>Series 2:The Return</seriestitle>
    <seriesfolder>Series 2_The Return</seriesfolder>
    <alias>Series Two</alias>
  </series>
  <series>Example with title as text of Series node -> This is series three
    <seriestitle>Series 3 - Why?</seriestitle>
    <seriesfolder>Series 3 - Why_</seriesfolder>
    <alias>Series three alias 1</alias>
    <alias>Series 3.0</alias>
    <alias>Series 3.0 (Premier)</alias>
  </series>
</recordings>


Я хотел бы добавить представление всех этих данных в свой пользовательский интерфейс, поэтому я играл с WPF TreeView. Я использовал HierarchicalDataTemplate, но столкнулся с проблемой. Я показываю [NodeName][NodeValue], который работает - но дает все содержимое узла для <series>, как и должно быть. Я могу подавить поле "Серия", которое работает, но если я сверну дерево, чтобы получить больше одного экрана, вместо списка названий серий, я получу имя поля снова и снова - не очень полезно для навигации. Вот XAML и codebehind для моего главного окна.

<Window x:Class="XmlDataTree.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:local="clr-namespace:XmlDataTree"
        Title="XmlData Tree Test"
        Loaded="Window_Loaded">

    <Window.Resources>
        <!-- Expand all nodes on load for demo -->
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded" Value="{Binding ElementName=chkExpand, Path=IsChecked, TargetNullValue=false}"/>
        </Style>

        <HierarchicalDataTemplate x:Key="NodeTemplate">
            <StackPanel Orientation="Horizontal" Focusable="False">
                <TextBlock x:Name="tbName" Text="{Binding Name}" />
                <TextBlock Text=": " />
                <TextBox x:Name="tbValue" Text="{Binding Value}" />
            </StackPanel>
            <!-- Hide series data -->
            <HierarchicalDataTemplate.Triggers>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding Path=Name.LocalName}" Value="series"/>
                        <Condition Binding="{Binding ElementName=chkHide, Path=IsChecked, TargetNullValue=false}" Value="True"/>
                    </MultiDataTrigger.Conditions>
                    <Setter TargetName="tbValue" Property="Visibility" Value="Collapsed"/>
                </MultiDataTrigger>
            </HierarchicalDataTemplate.Triggers>
            <HierarchicalDataTemplate.ItemsSource>
                <Binding Path="Elements" />
            </HierarchicalDataTemplate.ItemsSource>
        </HierarchicalDataTemplate>
    </Window.Resources>

    <Grid Margin="4">
        <StackPanel Orientation="Vertical">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Expand Tree" />
                <CheckBox Name="chkExpand" IsChecked="True" />
                <TextBlock Text="Hide Series" Margin="10,0"/>
                <CheckBox Name="chkHide" IsChecked="False" />
            </StackPanel>
            <TreeView 
            Name="trview"
            ItemsSource="{Binding Path=Root.Elements}"
            ItemTemplate="{StaticResource NodeTemplate}"
            HorizontalAlignment="Stretch"
            VerticalAlignment="Stretch"
            />
        </StackPanel>
    </Grid>
</Window>



using System.Windows;
using System.Xml.Linq;

namespace XmlDataTree
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            XDocument xDoc = new XDocument();
            xDoc = XDocument.Load(System.IO.Path.GetFullPath(System.AppDomain.CurrentDomain.BaseDirectory) + "aliases.xml");
            trview.DataContext = xDoc;
        }
    }
}


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

Есть ли способ для "&ЛТ;серии&ГТ;" развал области/расширить возможности быть сохранены, но для отображения &ЛТ;seriestitle&ГТ; childnode, а не в <серии&ГТ; сам узел? Я чувствую, что должен быть в состоянии управлять чем-то, заменив

<Setter TargetName="tbValue" Property="Text" Value="{Value}"/>

с

<Setter TargetName="tbValue" Property="Text" Value="{Binding Path=node::childnode[seriestitle].text()"/>


Что я уже пробовал:

Я потратил много времени на поиск в Google и путался между Path и XPath, XDocuments и XmlDocuments и многими другими. Наверное, это не поможет, потому что я не профессионал, это просто хобби ;-)

Все предложения с благодарностью получены!

1 Ответов

Рейтинг:
1

patrick.harkin

Я написал конвертер, который принимает текущий узел и возвращает текст дочернего узла seriestitle. Но я еще не написал Конвертбек!

public class seriesTitleConverter : IValueConverter
    {
        public Object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value == null) return string.Empty;
            try
            {
                XElement xe = value as XElement;
                XElement ce = xe.Element("seriestitle");
                return (ce.Value);
            }
            catch (Exception ex)
            {
                return "No title node found";
            }
        }
        public Object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }