使用不同的父节点和不同的子节点实现 WPF 树视图?

发布于 2024-11-08 16:40:22 字数 1112 浏览 0 评论 0原文

我想实现一个具有以下结构的树视图......

[RootNode] <---- 树的根
--[ParentNode P1] <---- ModelClass P1 的对象
----[ChildNode C1] <----- ModelClass C1 的对象(也有不同类型的子节点)
----[ChildNode C2] <----- ModelClass C2 的对象(也有不同类型的子节点)
----[ChildNode C3] <----- ModelClass C3 的对象(也有不同类型的子节点)
--[ParentNode Q1] <---- ModelClass Q1 的对象
----[ChildNode B1] <----- ModelClass B1 的对象(也有不同类型的子节点)
----[ChildNode B2] <----- ModelClass B2 的对象(也有不同类型的子节点)
----[ChildNode B3] <----- ModelClass B3 的对象(也有不同类型的子节点)
--[ParentNode R1] <---- ModelClass R1 的对象
----[ChildNode A1] <----- ModelClass A1 的对象(也有不同类型的子节点)
----[ChildNode A2] <----- ModelClass A2 的对象(也有不同类型的子节点)
----[ChildNode A3] <----- ModelClass A3 的对象(也有不同类型的子节点)

我已经查看了此站点以及网络上提出的许多解决方案...... .但就是不知道该怎么做......

这是我对 Wpf 的第一次尝试,这是一个至关重要的要求......

还发现很难为上述不同的类创建对象模型...... ..

上面显示的所有类都具有其他属性,包括它们的子节点... 我不想只显示子节点的所有属性

完全困惑...看到不同的解决方案

如果我能在这方面得到一些帮助那就太好了...

谢谢

I want to implememt a tree view with has the following structure.....

[RootNode] <---- Root of tree
--[ParentNode P1] <---- Object of ModelClass P1
----[ChildNode C1] <----- Object of ModelClass C1 (have children of different type as well)
----[ChildNode C2] <----- Object of ModelClass C2 (have children of different type as well)
----[ChildNode C3] <----- Object of ModelClass C3 (have children of different type as well)
--[ParentNode Q1] <---- Object of ModelClass Q1
----[ChildNode B1] <----- Object of ModelClass B1 (have children of different type as well)
----[ChildNode B2] <----- Object of ModelClass B2 (have children of different type as well)
----[ChildNode B3] <----- Object of ModelClass B3 (have children of different type as well)
--[ParentNode R1] <---- Object of ModelClass R1
----[ChildNode A1] <----- Object of ModelClass A1 (have children of different type as well)
----[ChildNode A2] <----- Object of ModelClass A2 (have children of different type as well)
----[ChildNode A3] <----- Object of ModelClass A3 (have children of different type as well)

I have looked at many of the solution proposed on this site as well as on the web.....but just cant figure out how to do it.....

This is my first attempt on Wpf and this is a crucial requirement ......

Also finding hard to make the Object Model for the above different classes .....

All the classes displayed above have Other properties as well including their child nodes...
i dont want to display all properties only the child nodes

Totally puzzled by ... seeing different solution

It would be really great if i could recieve some help in this regard...

Thanks

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

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

发布评论

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

评论(3

时光沙漏 2024-11-15 16:40:22

如果您使用自定义集合,层次结构数据模板就可以工作......
我的类是这样的:

public class EntityBase :ObservableCollection<object>
{

}

public class Parent : EntityBase
{

}  

public class ChildA : EntityBase // Dont make it a collection if it has noe childern to be displayed so dont inherit for EntityBase
{
   //Child Properties
}


public class ChildB : EntityBase
{
//Child Properties
}  

现在,当您最终将数据绑定到 TreeView 时,您将使 ChildAChildB 项作为 Parent 的子项> object ie

    public ObservableCollection<object> GetData()
    {
         var temp = new ObservableCollection<object>();
         Parent parent = new Parent(); // Root Node
         temp.Add(parent);
         parent.Add(new ChildA()); // ChildA as Child1 of Parent

         parent.Add(new ChildA()); // ChildA as Child2 of Parent

         parent.Add(new ChildB()); // ChildB as Child3 of Parent

         parent.Add(new ChildB()); // ChildB as Child4 of Parent

         return temp;

    }  

最后,层次结构数据模板将如下所示..

<TreeView Name="test" Grid.Row="0" ItemsSource="{Binding Path=TreeData,Source={StaticResource ResourceKey=DataSource}}">
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type EntityLayer:Parent}" ItemsSource="{Binding}">
                <StackPanel>
                    <TextBlock>Parent</TextBlock>
                </StackPanel>
            </HierarchicalDataTemplate>
            <HierarchicalDataTemplate DataType="{x:Type EntityLayer:ChildA}" ItemsSource="{Binding}">
                <StackPanel>
                    <TextBlock Text="{Binding Path = Name}"></TextBlock>
                </StackPanel>
            </HierarchicalDataTemplate>
            <HierarchicalDataTemplate DataType="{x:Type EntityLayer:ChildB}" ItemsSource="{Binding}">
                <StackPanel>
                    <TextBlock Text="{Binding Path = Name}"></TextBlock>
                </StackPanel>
            </HierarchicalDataTemplate>
        </TreeView.Resources>
    </TreeView>

Hierarchial Data Templates work if you use custom collections....
I made my classes like this :

public class EntityBase :ObservableCollection<object>
{

}

public class Parent : EntityBase
{

}  

public class ChildA : EntityBase // Dont make it a collection if it has noe childern to be displayed so dont inherit for EntityBase
{
   //Child Properties
}


public class ChildB : EntityBase
{
//Child Properties
}  

Now when you finally bind the data to you TreeView you'll make ChildA and ChildB items as Child items of Parent object i.e

    public ObservableCollection<object> GetData()
    {
         var temp = new ObservableCollection<object>();
         Parent parent = new Parent(); // Root Node
         temp.Add(parent);
         parent.Add(new ChildA()); // ChildA as Child1 of Parent

         parent.Add(new ChildA()); // ChildA as Child2 of Parent

         parent.Add(new ChildB()); // ChildB as Child3 of Parent

         parent.Add(new ChildB()); // ChildB as Child4 of Parent

         return temp;

    }  

Finally The Hierarchial data templates will look like..

<TreeView Name="test" Grid.Row="0" ItemsSource="{Binding Path=TreeData,Source={StaticResource ResourceKey=DataSource}}">
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type EntityLayer:Parent}" ItemsSource="{Binding}">
                <StackPanel>
                    <TextBlock>Parent</TextBlock>
                </StackPanel>
            </HierarchicalDataTemplate>
            <HierarchicalDataTemplate DataType="{x:Type EntityLayer:ChildA}" ItemsSource="{Binding}">
                <StackPanel>
                    <TextBlock Text="{Binding Path = Name}"></TextBlock>
                </StackPanel>
            </HierarchicalDataTemplate>
            <HierarchicalDataTemplate DataType="{x:Type EntityLayer:ChildB}" ItemsSource="{Binding}">
                <StackPanel>
                    <TextBlock Text="{Binding Path = Name}"></TextBlock>
                </StackPanel>
            </HierarchicalDataTemplate>
        </TreeView.Resources>
    </TreeView>
九公里浅绿 2024-11-15 16:40:22

如果这些类中的任何一个具有共同的基类,那么您会更容易,例如,您可以为多个类使用一个 DataTemplate

但是,如果每个模型确实不同并且没有足够的共同点,则您需要使用 DataTemplateSelector,尽管内置机制可能就足够了。

设置

以下是我为了重现类似情况而制作的一些基础。每个不同的类都继承自 List,因此它具有包含任何类型的子级的内置方法,但我不依赖于这种通用性来选择数据模板。

public class P1 : List<object> {
    public P1() {}
    public P1( IEnumerable<object> collection ) : base( collection ) {}
}

此外,我的根数据源是 List类型,因此它可以包含任何类型的对象。

Window 的构造函数:

public MainWindow() {
    InitializeComponent();
    this.DataContext = MyDataSource.GetData(); // in which I construct the tree of parents and children
}

解决方案

首先为每种类型创建HierarchicalDataTemplate。如果任何类型不包含子项,您当然会为它们创建 DataTemplate

<HierarchicalDataTemplate DataType="{x:Type loc:P1}"
                          ItemsSource="{Binding}">
    <TextBlock>a P1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:C1}"
                          ItemsSource="{Binding}">
    <TextBlock>a C1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:C2}"
                          ItemsSource="{Binding}">
    <TextBlock>a C2 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:Q1}"
                          ItemsSource="{Binding}">
    <TextBlock>a Q1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:B1}"
                          ItemsSource="{Binding}">
    <TextBlock>a B1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:B2}"
                          ItemsSource="{Binding}">
    <TextBlock>a B2 object</TextBlock>
</HierarchicalDataTemplate>

由于每个类都源自 List,因此对象本身是子项的来源,而不是属性的集合,因此我使用 ItemsSource="{Binding}"。如果子项位于名为 Children 的属性下的集合中,那么它当然会是 ItemsSource="{Binding Children}"。这仍然允许每个对象将其子对象放在不同的位置。

在此示例中实现 DataTemplateSelector 的最简单方法是不执行任何操作。因为我只在数据模板上指定了 DataType 而不是 x:Key,即使集合很模糊 (List) WPF 仍将检查基础类型以确定它是否是 P1/Q1/etc。并找到要使用的正确的 HierarchicalDataTemplate。我的 TreeView 只需看起来像这样:

<TreeView ItemsSource"{Binding}" />

但是,假设为了向您展示一个 DataTemplateSelector,您不能依赖它通过 Type 隐式匹配。您可以将 x:Key 放在数据模板上,如下所示:

<HierarchicalDataTemplate DataType="{x:Type loc:P1}" x:Key="myKeyforP1"
                          ItemsSource="{Binding}">
    <TextBlock>a P1 object</TextBlock>
</HierarchicalDataTemplate>

然后您的选择器可能会在内部使用 Dictionary 来确定给定 使用的资源键>Type (请记住,这是一个幼稚的实现):

public class CustomDataTemplateSelector : DataTemplateSelector {
    static Dictionary<Type, object> typeToKey = new Dictionary<Type, object>();
    static CustomDataTemplateSelector() {
        typeToKey[ typeof( P1 ) ] = "myKeyforP1";
    }

    public override DataTemplate SelectTemplate( object item, DependencyObject container ) {
        var element = container as FrameworkElement;
        if ( element != null && item != null ) {
            var itemtype = item.GetType();
            object keyObject;
            if ( typeToKey.TryGetValue( itemtype, out keyObject ) ) {
                var template = element.TryFindResource( keyObject ) as DataTemplate;
                if ( template != null ) {
                    return template;
                }
            }
        }
        return base.SelectTemplate( item, container );
    }
}

然后您将选择器添加到资源字典中,并且您的 TreeView 将需要分配另一个属性:

<Grid.Resources>
    <loc:CustomDataTemplateSelector x:Key="mySelector" />
</Grid.Resources>
<TreeView ItemsSource="{Binding}"
          ItemTemplateSelector="{StaticResource mySelector}"></TreeView>

并且因为 base.SelectTemplate() 将尝试使用项目的 Type 作为资源键,您可以为模型使用标准的 HierarchicalDataTemplate 和自定义版本仅当您的 TreeView 使用自定义 DataTemplateSelector 时才使用:

<HierarchicalDataTemplate DataType="{x:Type loc:P1}"
                          ItemsSource="{Binding}">
    <TextBlock>a P1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:P1}" x:Key="myKeyforP1"
                          ItemsSource="{Binding}">
    <TextBlock>a P1 that looks much different</TextBlock>
</HierarchicalDataTemplate>

It will be easier on you if any of these classes have base classes in common so that, for example, you can use one DataTemplate for several classes.

However, if each of these models really is different and don't have enough in common, you'll need to use a DataTemplateSelector, though built-in mechanisms may suffice.

Setup

Here's some of the foundation I made for the sake of recreating a similar situation. Each of the different classes inherits from List<object> so that it has a built-in way to contain children of any type, but I don't depend on that commonality for choosing datatemplates.

public class P1 : List<object> {
    public P1() {}
    public P1( IEnumerable<object> collection ) : base( collection ) {}
}

In addition, my root datasource is of type List<object> so that it can contain any types of objects.

The Window's constructor:

public MainWindow() {
    InitializeComponent();
    this.DataContext = MyDataSource.GetData(); // in which I construct the tree of parents and children
}

Solution

Start by making HierarchicalDataTemplates for each type. If any of the types do not contain children, you'll of course make DataTemplates instead for them:

<HierarchicalDataTemplate DataType="{x:Type loc:P1}"
                          ItemsSource="{Binding}">
    <TextBlock>a P1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:C1}"
                          ItemsSource="{Binding}">
    <TextBlock>a C1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:C2}"
                          ItemsSource="{Binding}">
    <TextBlock>a C2 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:Q1}"
                          ItemsSource="{Binding}">
    <TextBlock>a Q1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:B1}"
                          ItemsSource="{Binding}">
    <TextBlock>a B1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:B2}"
                          ItemsSource="{Binding}">
    <TextBlock>a B2 object</TextBlock>
</HierarchicalDataTemplate>

Since each class descends from List<object>, the object itself is the source of children items, rather than a collection on a property, so I use ItemsSource="{Binding}". If the child items are in a collection under a property called Children, it would instead be ItemsSource="{Binding Children}", of course. This still allows each object to have its children in a different place.

The simplest way to implement your DataTemplateSelector in this example is to do nothing. Because I only specified the DataType and not an x:Key on the data templates, even though the collections are vague (List<object>) WPF will still examine the underlying type to determine if it is P1/Q1/etc. and find the correct HierarchicalDataTemplate to use. My TreeView only has to look like this:

<TreeView ItemsSource"{Binding}" />

However, let's say for the sake of showing you a DataTemplateSelector that you can't rely on it implicitly matching by Type. You'd put x:Keys on your datatemplates, such as this:

<HierarchicalDataTemplate DataType="{x:Type loc:P1}" x:Key="myKeyforP1"
                          ItemsSource="{Binding}">
    <TextBlock>a P1 object</TextBlock>
</HierarchicalDataTemplate>

Then your selector might internally use a Dictionary to determine what resource key to use for a given Type (keep in mind that this is a naive implementation):

public class CustomDataTemplateSelector : DataTemplateSelector {
    static Dictionary<Type, object> typeToKey = new Dictionary<Type, object>();
    static CustomDataTemplateSelector() {
        typeToKey[ typeof( P1 ) ] = "myKeyforP1";
    }

    public override DataTemplate SelectTemplate( object item, DependencyObject container ) {
        var element = container as FrameworkElement;
        if ( element != null && item != null ) {
            var itemtype = item.GetType();
            object keyObject;
            if ( typeToKey.TryGetValue( itemtype, out keyObject ) ) {
                var template = element.TryFindResource( keyObject ) as DataTemplate;
                if ( template != null ) {
                    return template;
                }
            }
        }
        return base.SelectTemplate( item, container );
    }
}

Then you'd add the selector to a resource dictionary and your TreeView would need another property assigned:

<Grid.Resources>
    <loc:CustomDataTemplateSelector x:Key="mySelector" />
</Grid.Resources>
<TreeView ItemsSource="{Binding}"
          ItemTemplateSelector="{StaticResource mySelector}"></TreeView>

And because base.SelectTemplate() will try to use the item's Type as a resource key, you can have a standard HierarchicalDataTemplate for the model and a customized version that will only be used if your TreeView is using the custom DataTemplateSelector:

<HierarchicalDataTemplate DataType="{x:Type loc:P1}"
                          ItemsSource="{Binding}">
    <TextBlock>a P1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:P1}" x:Key="myKeyforP1"
                          ItemsSource="{Binding}">
    <TextBlock>a P1 that looks much different</TextBlock>
</HierarchicalDataTemplate>
澜川若宁 2024-11-15 16:40:22

您是否可以发布您的解决方案的更多详细信息。我正在尝试实现同样的目标,我添加了多个分层数据模板,但有兴趣查看对象模型。

就我而言,我有一个具有以下属性的父级,

Public string Name { get; set; }
Public ObservableCollection<ChildA> ChildrenA { get; set; }
Public ObservableCollection<ChildB> ChildrenB { get; set; }

我想在树视图中显示这些属性。我有兴趣了解如何构建对象模型,因为我需要在树中显示单个 ChildA 和 ChildB 集合之上级别的内容。

Is it possible you can post further details of your solution. I'm trying to achieve the same thing, I've added multiple hierarchicaldatatemplates but would be interested in seeing the object model.

In my case I've got a Parent with the following properties

Public string Name { get; set; }
Public ObservableCollection<ChildA> ChildrenA { get; set; }
Public ObservableCollection<ChildB> ChildrenB { get; set; }

I want to show these in my treeview. Would be interested in knowing how I might structure the object model as I need something to display in the tree at the level above the individual ChildA and ChildB collections.

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