Как показать иерархию классов в WPF TreeView? - PullRequest
3 голосов
/ 05 июля 2011

У меня есть простая иерархия в моем проекте WPF.

У меня есть базовый класс, который вызывает Product. Затем я наследую два класса от этого базового класса. Затем я наследую еще пару классов от них обоих.

Итак, у меня есть класс Product, затем классы Book и Disk, которые наследуются от класса Product, и класс RecipeBook, класс ProgramingBook и класс AdventureBook, которые наследуются от класса Book.

Затем я создаю коллекцию и добавляю туда какую-нибудь книгу, например collection.Add (new RecipeBook () {bla bla bla});

Наконец, у меня есть коллекция с разными классами.

Так что все, что мне нужно, это показать все это через WPF TreeView.

Это будет что-то вроде этого:

Product
    Book
       RecipeBook
          Book#1
          Book#2
        ProgramingBook
          Book#1
        ....
    Disk
      Disk#1
      Disk#2
      .....

Так как я могу это сделать?

<TreeView x:Name="treeView" Height="224" HorizontalAlignment="Left" Margin="10,10,0,0"  VerticalAlignment="Top" Width="337" >
      <TreeViewItem Header="Products" >
          <TreeViewItem Header="Books">
              <TreeViewItem Header="Recipe"></TreeViewItem>
              <TreeViewItem Header="Programing"></TreeViewItem>
              <TreeViewItem Header="Adventure"></TreeViewItem>
          </TreeViewItem>
          <TreeViewItem Header="CDs">             
          </TreeViewItem>
      </TreeViewItem>
</TreeView>

Ответы [ 3 ]

2 голосов
/ 05 июля 2011

У меня это по большей части (то есть группы работают, но все группы должны иметь один и тот же уровень).

Product
    Book
       RecipeBook
          Book#1
          Book#2
        ProgramingBook
          Book#1
        ....
    Disk
        Disk
          Disk#1
          Disk#2
      .....

Я использую ваши данные в качестве входных данных, поэтому моя структура классовследующим образом:

class Product
{
    public string Name { get; set; }
}

class Book : Product
{
    public string BookName { get; set; }
}

class RecipeBook : Book
{
    public int NumRecipes { get; set; }
}

class ProgrammingBook : Book
{
    public string LanguageCovered { get; set; }
}

class Disk : Product
{
    public int Size { get; set; }
}

заполняется следующим образом:

ObservableCollection<Product> products = new ObservableCollection<Product>();
        products.Add(new ProgrammingBook() { BookName = "P1", LanguageCovered = "C#" });
        products.Add(new ProgrammingBook() { BookName = "P2", LanguageCovered = "F#" });

        products.Add(new RecipeBook() { BookName = "P3", NumRecipes = 4 });
        products.Add(new RecipeBook() { BookName = "P4", NumRecipes = 6 });

        products.Add(new Disk() { Size = 512 });
        products.Add(new Disk() { Size = 1024 });

        this.DataContext = products;

Нам нужно группирование по типу, чтобы мы могли использовать конвертер в CollectionViewSource PropertyGroupDescription.

Это мойконвертер (я передаю разные значения для разных уровней иерархии для типа:

public class GroupByType : IValueConverter
{
    public string type { get; set; }
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (type == "root")
        {
            if (value is Product)
            {
                return "Product";
            }

            return null;
        }

        if (type == "subs")
        {
            if (value is Book)
            {
                return "Book";
            }
            if (value is Disk)
            {
                return "Disk";
            }
        }

        if (type == "main")
        {
            if (value is ProgrammingBook)
            {
                return "PBook";
            }
            if (value is RecipeBook)
            {
                return "RBook";
            }
            if (value is Disk)
            {
                return "Disk";
            }
        }
        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

, как только у нас есть данные, нам нужны соответствующие шаблоны (мои простые и показаны в конце xaml), нонам нужен какой-то способ их выбора, поэтому я использую следующий селектор шаблонов:

public class Selector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        string templateKey;
        if (item is CollectionViewGroup)
        {
            if ((item as CollectionViewGroup).Name == null)
            {
                return null;
            }
            templateKey = "GroupTemplate";
        }
        else if (item is ProgrammingBook)
        {
            templateKey = "pTemplate";
        }
        else if (item is RecipeBook)
        {
            templateKey = "rTemplate";
        }
        else if (item is Disk)
        {
            templateKey = "dTemplate";
        }
        else
        {
            return null;
        }
        return (DataTemplate)((FrameworkElement)container).FindResource(templateKey);
    }
}

Мой XAML выглядит так:

<Window.Resources>
    <HierarchicalDataTemplate x:Key="GroupTemplate" ItemsSource="{Binding Path=Items}">
        <TextBlock Text="{Binding Path=Name}" FontWeight="Bold"/>
    </HierarchicalDataTemplate>

    <DataTemplate x:Key="pTemplate">
        <TextBlock Text="{Binding Path=LanguageCovered }"/>
    </DataTemplate>

    <DataTemplate x:Key="rTemplate">
        <TextBlock Text="{Binding Path=NumRecipes}"/>
    </DataTemplate>
    <DataTemplate x:Key="dTemplate">
        <TextBlock Text="{Binding Path=Size}"/>
    </DataTemplate>
    <c:GroupByType x:Key="gt" />
    <c:Selector x:Key="s"/>
    <CollectionViewSource Source="{Binding}" x:Key="cvs">
                    <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription>
                <PropertyGroupDescription.Converter>
                    <c:GroupByType type="root"/>
                </PropertyGroupDescription.Converter>
            </PropertyGroupDescription>
            <PropertyGroupDescription>
                <PropertyGroupDescription.Converter>
                    <c:GroupByType type="subs"/>
                </PropertyGroupDescription.Converter>
            </PropertyGroupDescription>
            <PropertyGroupDescription>
                <PropertyGroupDescription.Converter>
                    <c:GroupByType type="main"/>
                </PropertyGroupDescription.Converter>
            </PropertyGroupDescription>
        </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>
</Window.Resources>
<Grid>
    <TreeView ItemsSource="{Binding Source={StaticResource cvs}, Path=Groups}" ItemTemplateSelector="{StaticResource s}"/>
</Grid>

Идея в том, чтодля каждого типа в вашей иерархии мы создаем новую группу с фактическими объектами, являющимися листовыми узлами дерева. Вы можете добавить столько уровней в группу, сколько вам нужно, добавив новые разделы в конвертер.n также переименуйте группы, изменив возвращаемое значение преобразователя.

Если у вас есть какие-либо вопросы, пожалуйста, задавайте, так как это довольно длинный ответ:).

1 голос
/ 05 июля 2011
0 голосов
/ 05 июля 2011

Вы также должны прочитать статью Джоша Смита об использовании TreeView с MVVM

...