locked
Problem with TreeView RRS feed

  • Question

  • Hi,

    I'm running into another problem with XAML and I hope someone can help me out. I'm trying to create a TreeView off of ONE POCO class using ObservableCollection property. Here's the scenario:

    The Parent node has the name of the vendor and the vendor's part list, and that seems to be working fine though I want to change it's display layout at a later stage; but on the child leaves, I wanted it to be hyperlinks that the user can click on it and open a browser, but it doesn't seem to be working. ALL of this information is from one POCO class called PartsList. Here's the XAML:

            <HierarchicalDataTemplate x:Key="PartListTemplate"
    				  ItemsSource="{Binding CurrentPartsList}">
                <StackPanel Orientation="Horizontal">
                    <TextBox Text="{Binding Path=VendorName}"/>
                    <TextBox Text="{Binding Path=VendorPartNo}"></TextBox>
                </StackPanel>
                <HierarchicalDataTemplate.ItemTemplate>
                    <HierarchicalDataTemplate ItemsSource="{Binding}">
                        <StackPanel Orientation="Vertical">
                            <TextBox Text="{Binding Path = VendorPartNoLink}" />
                            <TextBox Text="{Binding Path = AlternativeLink1}" />
                            <TextBox Text="{Binding Path = AlternativeLink2}" />
                            <TextBox Text="{Binding Path = AlternativeLink3}" />
                        </StackPanel>
                    </HierarchicalDataTemplate>
                </HierarchicalDataTemplate.ItemTemplate>
            </HierarchicalDataTemplate>

    As stated, the Vendor Name and Vendor Part Number is showing up fine, but it's the ItemTemplate that's giving me a headache. So where did I go wrong?


    • Edited by sb68 Saturday, October 24, 2015 10:49 PM Error in the XAML code
    Saturday, October 24, 2015 10:47 PM

Answers

  • >>I think the second one would work, but now that you seen more of the code, I do appreciate your thoughts and opinions on how I should solve the problem with the child leaves.

    The PartsList class doesn't seem to contain any property that contains other PartsList objects so it is not hierarchical...You need to add a collection property to the PartsList class that returns the child parts of that particular PartsList object. I have already provided you with such an example: https://social.msdn.microsoft.com/Forums/vstudio/en-US/fdebbcf9-6e3d-4be6-b110-728154305d1c/wpf-treeview-get-child-and-parent?forum=wpf. The Discipline class has a Tests property that returns a collection of Test objects. In this case you set the ItemsSource property of the HierarchicalDataTemplate to "Tests". You need to add a corresponding collection property to your class if you intend to display hierarchical items in a TreeView:

    public class PartsList
    {
            public long PartsListID { get; set;}
            public long? PartID { get; set;}
            public long? VendorID { get; set;}
            public string VendorName { get; set;}
            public string VendorPartNo { get; set;}
            public string VendorPartNoLink { get; set;}
            public double? Price { get; set;}
            public string Alternative { get; set;}
            public string AlternativeLink1 { get; set;}
            public string AlternativeLink2 { get; set;}
            public string AlternativeLink3 { get; set;}
            public string Description { get; set;}
            public virtual StockControl StockControl { get; set;}
    
           public ICollection<PartsList> Children {get;set;}
     public bool IsParent {get { return this.Children != null && this.Children.Any(); }}
    
    }
    

    Then you set the ItemsSource of the HierarchicalDataTemplate to the Children collection, put all TextBlocks in the same HierarchicalDataTemplate and use a DataTrigger that binds to some property (IsParent) of a PartsList that determines whether it is a leaf or parent node and then change the Visibility property of the TextBlocks to be shown/hidden as I explained in my previous reply.

     <HierarchicalDataTemplate DataType="{x:Type local:ParentPart}" ItemsSource="{Binding Children}">
                        ...
                    </HierarchicalDataTemplate>

    You won't be able to display hierarchical data unless the PartsList class has any collection property.

    And just taking some auto generated Entity Framework class and plug it in into a solution as-is is rarely going to work in a real time scenario. You will have to modify or wrap the POCO class to make it adopt to your WPF requirements. In this case you have to add a collection property to it and populate it with the child objects for each PartsList.

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    Sunday, October 25, 2015 10:11 PM

All replies

  • Please show us how you build your collection and what the class looks like.

    PartsList seems an odd name for a class but whatever contains your VendorPartNoLink etc.

    One thing I'm particularly interested to see is whether it has a collection within it or you're just kind of expecting it to work out what the children are.
    Because it won't.

    You need something like a converter on your binding.

    Like here:

    http://stackoverflow.com/questions/14161963/how-to-bind-self-referencing-table-to-wpf-treeview

    <TreeView Name="treeview1" ItemsSource="{Binding Converter={StaticResource HierarchyConverter}}" ItemTemplate="{StaticResource ItemTemplate}" >
          <TreeView.Resources>
                <local:HierarchyConverter x:Key="HierarchyConverter" />
                <HierarchicalDataTemplate x:Key="ItemTemplate" ItemsSource="{Binding Converter={StaticResource HierarchyConverter}}">
                      <TextBlock Text="{Binding element_name}" />
                </HierarchicalDataTemplate>
          </TreeView.Resources>
    </TreeView>

    And that converter gets the children:

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var item = value as MyTable;
            return  item.MyTable1.Where(i => i.parent_id== item.id); //return children
        }

    Personally, I prefer to translate into an object containing a child collection.

    One of these presents units. A unit can have subunits each subunit contains a unit... and so on.

    My itemtemplate presents a unit and the hierarchicaldatatemplate looks:

                        <HierarchicalDataTemplate
                           ItemsSource="{Binding Path=Unit.SubUnits}"
                           DataType="{x:Type model:SubUnit}"

    In Unit:

            private ObservableCollection<SubUnit> _SubUnits;
            public ObservableCollection<SubUnit> SubUnits
            {
                get
                {
                    if (_SubUnits == null)
                        _SubUnits = new ObservableCollection<SubUnit>();
                    return _SubUnits;
                }
                set { _SubUnits = value; }
            }

    SubUnit has a few properties like a GUID and

            public Unit Unit
            {
                get
                {
                    return _Unit;
                }
                set
                {
                    _Unit = value;
                    OnPropertyChanged("Unit");
                }
            }



    Hope that helps.

    Technet articles: WPF: MVVM Step 1; All my Technet Articles

    Sunday, October 25, 2015 10:33 AM
  • Instead of defining an ItemsTemplate for the TreeView or the HierarchicalDataTemplate you could add one or two DataTemplates to the <Resources> section of the TreeView itself. Replace the x:Key with a DataType. Please refer to the following thread for an example: https://social.msdn.microsoft.com/Forums/vstudio/en-US/fdebbcf9-6e3d-4be6-b110-728154305d1c/wpf-treeview-get-child-and-parent?forum=wpf

    I don't know how your Part class or whatever you call it is defined since you haven't posted the code for it but if you want to display a different template for leaf nodes, you could use two different types for parent nodes and leaf nodes (ParentPart and LeafType in the sample markup below):

    <TreeView x:Name="tv" xmlns:local="clr-namespace:WpfApplication1">
    <TreeView.Resources>
    <HierarchicalDataTemplate DataType="{x:Type local:ParentPart}" ItemsSource="{Binding CurrentPartsList}">
                <StackPanel Orientation="Horizontal">
                    <TextBox Text="{Binding Path=VendorName}"/>
                    <TextBox Text="{Binding Path=VendorPartNo}"></TextBox>
                </StackPanel>
    </HierarchicalDataTemplate>
    <DataTemplate DataType="{x:Type local:LeafType}">
    <StackPanel Orientation="Vertical">
                            <TextBox Text="{Binding Path = VendorPartNoLink}" />
                            <TextBox Text="{Binding Path = AlternativeLink1}" />
                            <TextBox Text="{Binding Path = AlternativeLink2}" />
                            <TextBox Text="{Binding Path = AlternativeLink3}" />
                        </StackPanel>
    </DataTemplate>
    </TreeView.Resources>
    </TreeView> 

    If you are using the same type for parent and leaf nodes, you will have to modify the template based on the state of the node. You could for example do this using DataTriggers. Add an "IsParent" property or something to your class and then show/hide controls in the template based on the value of this one. Something like this:

                    <HierarchicalDataTemplate DataType="{x:Type local:ParentPart}" ItemsSource="{Binding CurrentPartsList}">
                        <StackPanel x:Name="sp" Orientation="Horizontal">
                            <TextBox x:Name="a" Text="{Binding Path=VendorName}"/>
                            <TextBox x:Name="b" Text="{Binding Path=VendorPartNo}"></TextBox>
    
                            <TextBox x:Name="c" Text="{Binding Path = VendorPartNoLink}" />
                            <!-- +...-->
                        </StackPanel>
                        <HierarchicalDataTemplate.Triggers>
                            <!-- Add an IsParent property to your node class -->
                            <DataTrigger Binding="{Binding IsParent}" Value="False">
                                <Setter TargetName="sp" Property="Orientation" Value="Vertical"/>
                                <Setter TargetName="a" Property="Visibility" Value="Collapsed"/>
                                <Setter TargetName="b" Property="Visibility" Value="Collapsed"/>
                                <Setter TargetName="c" Property="Visibility" Value="Visible"/>
                            </DataTrigger>
                        </HierarchicalDataTemplate.Triggers>
                    </HierarchicalDataTemplate>
    

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    Sunday, October 25, 2015 4:49 PM
  • My apologies for not posting the rest of the code. I thought that just the XAML would suffice.

    In any case, here's the class I am using for the TreeView:

    public class PartsList
    {
            public long PartsListID { get; set;}
            public long? PartID { get; set;}
            public long? VendorID { get; set;}
            public string VendorName { get; set;}
            public string VendorPartNo { get; set;}
            public string VendorPartNoLink { get; set;}
            public double? Price { get; set;}
            public string Alternative { get; set;}
            public string AlternativeLink1 { get; set;}
            public string AlternativeLink2 { get; set;}
            public string AlternativeLink3 { get; set;}
            public string Description { get; set;}
            public virtual StockControl StockControl { get; set;}
    
    }

    In the ViewModel:

    public class StockItemsViewModel : BindableBase { private ObservableCollection<PartsList> _currentPartList; private DBContext _context = new DBContext(); public ObservableCollection<PartsList> CurrentPartsList { get {return _currentPartList;} set { SetProperty(ref _currentPartList, value); } } // code snipped public StockItemsViewModel() { if (!DesignerProperties.GetIsInDesignMode(new System.Windows.DependencyObject())) { // code snipped CurrentPartsList = new ObservableCollection<PartsList>(CurrentItem.PartsLists.ToList()); } } // code snipped private void ChangeList(long ID) { _currentPartList.Clear(); CurrentPartsList.AddRange(_context.PartsLists.Where(p => p.PartID == ID).ToList()); } }

    }

    As you can see, it's just a simple POCO class that Entity Framework created which is the actual table in SQL Server 2008.  And as you can see, I'm just using ONE class on the TreeView.  BindableBase is a class basically implements the INPC where the SetProperty uses the [CallMemberName] to fire the PropertyChanged event.

    I think the second one would work, but now that you seen more of the code, I do appreciate your thoughts and opinions on how I should solve the problem with the child leaves.



    • Edited by sb68 Sunday, October 25, 2015 8:52 PM
    Sunday, October 25, 2015 8:46 PM
  • >>I think the second one would work, but now that you seen more of the code, I do appreciate your thoughts and opinions on how I should solve the problem with the child leaves.

    The PartsList class doesn't seem to contain any property that contains other PartsList objects so it is not hierarchical...You need to add a collection property to the PartsList class that returns the child parts of that particular PartsList object. I have already provided you with such an example: https://social.msdn.microsoft.com/Forums/vstudio/en-US/fdebbcf9-6e3d-4be6-b110-728154305d1c/wpf-treeview-get-child-and-parent?forum=wpf. The Discipline class has a Tests property that returns a collection of Test objects. In this case you set the ItemsSource property of the HierarchicalDataTemplate to "Tests". You need to add a corresponding collection property to your class if you intend to display hierarchical items in a TreeView:

    public class PartsList
    {
            public long PartsListID { get; set;}
            public long? PartID { get; set;}
            public long? VendorID { get; set;}
            public string VendorName { get; set;}
            public string VendorPartNo { get; set;}
            public string VendorPartNoLink { get; set;}
            public double? Price { get; set;}
            public string Alternative { get; set;}
            public string AlternativeLink1 { get; set;}
            public string AlternativeLink2 { get; set;}
            public string AlternativeLink3 { get; set;}
            public string Description { get; set;}
            public virtual StockControl StockControl { get; set;}
    
           public ICollection<PartsList> Children {get;set;}
     public bool IsParent {get { return this.Children != null && this.Children.Any(); }}
    
    }
    

    Then you set the ItemsSource of the HierarchicalDataTemplate to the Children collection, put all TextBlocks in the same HierarchicalDataTemplate and use a DataTrigger that binds to some property (IsParent) of a PartsList that determines whether it is a leaf or parent node and then change the Visibility property of the TextBlocks to be shown/hidden as I explained in my previous reply.

     <HierarchicalDataTemplate DataType="{x:Type local:ParentPart}" ItemsSource="{Binding Children}">
                        ...
                    </HierarchicalDataTemplate>

    You won't be able to display hierarchical data unless the PartsList class has any collection property.

    And just taking some auto generated Entity Framework class and plug it in into a solution as-is is rarely going to work in a real time scenario. You will have to modify or wrap the POCO class to make it adopt to your WPF requirements. In this case you have to add a collection property to it and populate it with the child objects for each PartsList.

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    Sunday, October 25, 2015 10:11 PM