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.