C
C#3mo ago
stigzler

WPF Treeview HierarchicalDataTemplate on Type with sub-collection

Hi Folks. I have the following two models (abridged for brevity) and mainViewModel:
internal class Gist
{
public ObservableCollection<GistFile> GistFiles { get; set; } = new();

public override string ToString()
{
return GistFiles.OrderBy(gf => gf.Filename).First().Filename;
}
}

internal class GistFile
{
public string Filename { get; set; }

public override string ToString()
{
return Filename;
}
}

internal class MainWindowViewModel : ViewModelBase
{
private ObservableCollection<Models.Gist> gists = new ObservableCollection<Models.Gist>();
public ObservableCollection<Models.Gist> Gists { get => gists; set => SetProperty(ref gists, value); }
}
internal class Gist
{
public ObservableCollection<GistFile> GistFiles { get; set; } = new();

public override string ToString()
{
return GistFiles.OrderBy(gf => gf.Filename).First().Filename;
}
}

internal class GistFile
{
public string Filename { get; set; }

public override string ToString()
{
return Filename;
}
}

internal class MainWindowViewModel : ViewModelBase
{
private ObservableCollection<Models.Gist> gists = new ObservableCollection<Models.Gist>();
public ObservableCollection<Models.Gist> Gists { get => gists; set => SetProperty(ref gists, value); }
}
and my xaml:
<TreeView x:Name="GistsTV" ItemsSource="{Binding Gists}" Width="200">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Gists.Files}">
<TextBlock Text="{Binding}"></TextBlock>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<TreeView x:Name="GistsTV" ItemsSource="{Binding Gists}" Width="200">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Gists.Files}">
<TextBlock Text="{Binding}"></TextBlock>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
I'm only getting the Gist displayed and not the GistFiles on a sub-level. I know I'm getting this wrapped around my head conceptually - what am I doing wrong?
1 Reply
stigzler
stigzler3mo ago
For clarity - I'm wanting Gists to display at the root level and them GistFiles to display at the child level Bingo:
<DockPanel>
<DockPanel.Resources>
<HierarchicalDataTemplate DataType="{x:Type models:Gist}" ItemsSource="{Binding Path=GistFiles}">
<TextBlock Text="{Binding Id}"></TextBlock>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type models:GistFile}">
<TextBlock Text="{Binding Filename}"></TextBlock>
</DataTemplate>
</DockPanel.Resources>
<TreeView x:Name="GistsTV" ItemsSource="{Binding Gists}" Width="200">
</TreeView>
</DockPanel>
<DockPanel>
<DockPanel.Resources>
<HierarchicalDataTemplate DataType="{x:Type models:Gist}" ItemsSource="{Binding Path=GistFiles}">
<TextBlock Text="{Binding Id}"></TextBlock>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type models:GistFile}">
<TextBlock Text="{Binding Filename}"></TextBlock>
</DataTemplate>
</DockPanel.Resources>
<TreeView x:Name="GistsTV" ItemsSource="{Binding Gists}" Width="200">
</TreeView>
</DockPanel>
I guess WPF hooks hem all up in the background For completness:
internal class GistFilesToFirstFilenameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{

ObservableCollection<Models.GistFile> gistFiles = (ObservableCollection<Models.GistFile>) value;
if (gistFiles.Count > 0)
{
return gistFiles.First().Filename;
}

return value;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
internal class GistFilesToFirstFilenameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{

ObservableCollection<Models.GistFile> gistFiles = (ObservableCollection<Models.GistFile>) value;
if (gistFiles.Count > 0)
{
return gistFiles.First().Filename;
}

return value;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
<converters:GistFilesToFirstFilenameConverter x:Key="GistFilesToFirstFilenameConverter"/>

<DockPanel Width="Auto">
<DockPanel.Resources>
<HierarchicalDataTemplate DataType="{x:Type models:Gist}" ItemsSource="{Binding Path=GistFiles}">
<TextBlock Text="{Binding GistFiles, Converter={StaticResource GistFilesToFirstFilenameConverter}}"></TextBlock>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type models:GistFile}">
<TextBlock Text="{Binding Filename}"></TextBlock>
</DataTemplate>
</DockPanel.Resources>

<TreeView x:Name="GistsTV" ItemsSource="{Binding Gists}">
</TreeView>

</DockPanel>
<converters:GistFilesToFirstFilenameConverter x:Key="GistFilesToFirstFilenameConverter"/>

<DockPanel Width="Auto">
<DockPanel.Resources>
<HierarchicalDataTemplate DataType="{x:Type models:Gist}" ItemsSource="{Binding Path=GistFiles}">
<TextBlock Text="{Binding GistFiles, Converter={StaticResource GistFilesToFirstFilenameConverter}}"></TextBlock>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type models:GistFile}">
<TextBlock Text="{Binding Filename}"></TextBlock>
</DataTemplate>
</DockPanel.Resources>

<TreeView x:Name="GistsTV" ItemsSource="{Binding Gists}">
</TreeView>

</DockPanel>