开发者

Implement WPF treeview with different Parent Nodes a well as different child nodes?

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


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>


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>


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜