0

EDIT AFTER CLOSE

Thanks for that partial answer. It was a valid suggestion. Although I don't believe my question had to be closed for that. I added a getter and setter to the immutable field, and now the treeview loads the root node, and ONE level of subnodes (public List<AdOu> Items { get; set; } = new List<AdOu>();) Still... as was the original intent of my question, I believe the HierarchicalDataTemplate doesn't work for Items on level 3 and below.

ORIGINAL QUESTION

I'm trying to recreate the Active Directory (Organizational Units) in a TreeView control. The following is one of the things I tried already.

    <Window.Resources>
        <DataTemplate x:Key="treeviewou">
            <TextBlock Text="{Binding Name}" />
        </DataTemplate>
        <HierarchicalDataTemplate 
            x:Key="treeviewitems" 
            DataType="{x:Type admodels:AdOu}" 
            ItemsSource="{Binding Items}"
            ItemTemplate="{StaticResource treeviewou}">
            <StackPanel>
                <TextBlock Text="{Binding Name}" />
            </StackPanel>
        </HierarchicalDataTemplate>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="83*"/>
            <ColumnDefinition Width="317*"/>
        </Grid.ColumnDefinitions>
        <DataGrid x:Name="grdUsers" Grid.Column="1" d:ItemsSource="{d:SampleData ItemCount=5}"/>
        <TreeView x:Name="tvOus"
                  SelectedItemChanged="tvOus_SelectedItemChanged"
                  ItemTemplate="{StaticResource treeviewitems}"
                  >
            
        </TreeView>
    </Grid>

DataType="{x:Type admodels:AdOu}" looks like this:

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.DirectoryServices;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    namespace ADUtils.Models
    {
        public class AdOu : ViewModelBase
        {
            private SearchResult? _searchResult;
            private DirectoryEntry? _directoryEntry;
            private string? _name;
            public List<AdOu> Items { get; set; } = new List<AdOu>();

            public AdOu(SearchResult sr)
            {
                searchResult = sr;
                //Text = sr.Properties["name"][0].ToString();
            }
            public AdOu(DirectoryEntry de) {
                directoryEntry = de;
                //Text = de.Properties["name"][0].ToString();
            }
            public SearchResult? searchResult {
                get {
                    if (_searchResult == null && directoryEntry != null) {
                        DirectoryEntry startingPoint = new DirectoryEntry(directoryEntry.Path);

                        DirectorySearcher searcher = new DirectorySearcher(startingPoint);
                        searcher.SearchScope = SearchScope.Base;
                        //searcher.Filter = "(objectCategory=organizationalUnit)";

                        _searchResult = searcher.FindOne();
                    }
                    return _searchResult;
                }
                set {
                    _searchResult = value;
                    OnPropertyChanged(nameof(Name));
                }
            }
            public DirectoryEntry? directoryEntry {
                get {
                    if (_directoryEntry == null && searchResult != null) {
                        _directoryEntry = searchResult.GetDirectoryEntry();
                    }
                    return _directoryEntry;
                     
                }
                set {
                    _directoryEntry = value;
                    OnPropertyChanged(nameof(Name));
                }
            }
            public string? Name {
                get {
                    if (_name == null && searchResult != null && searchResult.Properties["Name"].Count > 0) {
                        _name = searchResult.Properties["Name"][0].ToString();
                    }
                    return _name;
                }
            }
        }
    }

PS. ViewModelBase holds my OnPropertyChanged logic

Next, I create the OU hierarchy and bind it to the treeview

    public void InitTreeView() {
        ListOus = adOUs.GetHierarchical();
        //ListTreeViewItems = new ObservableCollection<ADTreeViewItem>();
        TreeViewList = new ObservableCollection<AdOu>(ListOus);
        tvOus.ItemsSource = TreeViewList;
        //foreach (AdOu ou in ListOus) {
        //    ListTreeViewItems.Add(OusToTreeView(ou));
        //}
        //tvOus.ItemsSource = ListTreeViewItems;
    }
    private ADTreeViewItem OusToTreeView(AdOu? ou = null, ADTreeViewItem? parent = null) {
        ADTreeViewItem tviRoot = new ADTreeViewItem(ou);
            
        foreach (AdOu child in ou?.Items ?? ListOus ?? new List<AdOu>()) {
            OusToTreeView(child, tviRoot);
        }
        if (parent != null) {
            parent.Items.Add(tviRoot);
        }
        return tviRoot;
    }

Sidenote The commented out lines in the InitTreeView function is what I had tried before. The treeview loaded those nodes, but didn't display the text. Since you shouldn't override the TreeViewItem class according to 'the internet', I gave up on that approach.

Current approach I know this example is wrong, this example only loads the first node (top-root node), without loading the child nodes. I think it's because It doesn't recognize the child's HierarchicalDataTemplate it has to use.

I really don't know how to solve this. The entire hierarchy tree exists out of AdOu objects with it's respective List<AdOu> items. This question's answer may help, but I don't understand how to implement it, and if it would work.

DerpyNerd
  • 4,743
  • 7
  • 41
  • 92
  • `public List Items = new List();` - for starters, you need at least getter to make it a property: `public List Items { get; } = new List();` – ASh Jul 19 '22 at 18:10
  • @ASh Nice, I had to give it a getter and setter. Now I have all nodes up to one level down. But there are no nodes for my third level and down – DerpyNerd Jul 19 '22 at 19:34
  • 1
    "*the HierarchicalDataTemplate doesn't work for Items on level 3 and below*" - that makes no sense. If it works for one level, then it does for all. Try to remove the ItemTemplate assignment from the HierarchicalDataTemplate. It is also unclear why there is a StackPanel in the HierarchicalDataTemplate, where a TextBlock would apparently be sufficient. – Clemens Jul 19 '22 at 20:24
  • @Clemens thanks, you are absolutely right. I removed the datatemplate and moved the `HierarchicalDataTemplate` into the `Treeview.Resources` section and it all started loading perfectly. Apparently the root problem was indeed as suggested by the "duplicate answer" – DerpyNerd Jul 20 '22 at 14:45

0 Answers0