如何在 WPF TreeView 中显示类层次结构?

发布于 2024-11-18 18:06:55 字数 1218 浏览 2 评论 0原文

我的 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>

I have some simple hierarchy in my WPF project.

I have base class, which calls Product. Then I inherit two classes from this base class. Then I inherit couple more classes from both of them.

So I have Product class, then Book and Disk classes, which inherit from Product class, and RecipeBook class, ProgramingBook class and AdventureBook class, which inherit from Book class.

Then I create collection and add some book there like collection.Add(new RecipeBook() {bla bla bla});

Finaly I have collection with diferent classes.

So all I need is to show all this stuff via WPF TreeView.

It's gonna be smth like that:

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

So how can I do that?

<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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

栩栩如生 2024-11-25 18:06:55

我在那里的大部分方式都是这样的(即分组有效,但所有组必须具有相同的级别。)

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>

这里的想法是,对于层次结构中的每种类型,我们创建一个新组,其中实际对象是树的叶节点。您可以通过向转换器添加新部分来根据需要向分组添加任意多个级别。您还可以通过更改转换器的返回值来重命名组。

如果您有任何疑问,请询问,因为这是一个很长的答案:)。

I have this most of the way there (i.e. the groupings work but all groups must have the same level.)

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

I am using your sample data as my input so my class structure is as follows:

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; }
}

populated as follows:

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;

We need a groupoing by type so we can use a converter on the CollectionViewSource PropertyGroupDescription.

This is my converter (I pass in different values for different levels of the hierarchy for the type:

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();
    }
}

once we have the data we need appropriate templates (mine are simple and are shown in the xaml at the end) but we need some way of choosing them so I use the following template selector:

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);
    }
}

My XAML looks like this:

<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>

The idea here is that for each type in your hierarchy we create a new group with the actual objects being the leaf nodes of the tree. You can add as many levels to the grouping as you need by adding new sections to the converter. You can also rename the groups by changing the return value of the converter.

If you have any questions then please ask as this is quite a long answer :).

孤云独去闲 2024-11-25 18:06:55

检查:HierarchicalDataTemplate,有一个完整的示例供您使用开始: 显示分层数据示例

check: HierarchicalDataTemplate, there is a complete example for you to start: Displaying Hierarchical Data Sample

梦断已成空 2024-11-25 18:06:55

您还应该查看 Josh Smith 的文章,了解如何将 TreeView 与 MVVM 结合使用

You should also check out Josh Smith's article on using the TreeView with MVVM

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文