0
votes

I am new to WPF and I would like TreeView to show expand/collapse icon (the triangle beside node) at all times, regardless whether node has items in it.

To show it at all times, I add a dummy item for nodes that have no items ending up with something like below (for now, I would like to do this in code-behind):

+ Node 1
- Node 2
 - Dummy Item
+ Node 3

Further requirement is to delete Dummy Item once the node having it is expanded.
To do that, I remove the item in OnExpand:

public void OnExpand(object sender, EventArgs e)
{
    ...
    foreach (var item in tvItems){
        if (item is dummy){
            tvItems.Children.Remove(item);
        }
    }
    ...
}

The problem with this is that once node is expanded, I see empty line

+ Node 1
- Node 2
          <-- How to remove this line?
+ Node 3

How do I remove this line so that list shows like:

+ Node 1
  Node 2  // there is no empty line btw Node 2 and Node 3
+ Node 3
2

2 Answers

1
votes

try this sample

 <Window x:Class="TreeViewExample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.Resources>
        <HierarchicalDataTemplate x:Key="ChildTemplate" >
            <TextBlock FontStyle="Italic" Text="{Binding Path=Name}" />
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate x:Key="NameTemplate" ItemsSource="{Binding Path=Books}" 
                                  ItemTemplate="{StaticResource ChildTemplate}" >
            <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" />
        </HierarchicalDataTemplate>
    </Grid.Resources>
    <TreeView HorizontalAlignment="Left" Height="218" VerticalAlignment="Top" Width="175" Margin="76,37,0,0" ItemsSource="{Binding Standards}" ItemTemplate="{StaticResource NameTemplate}" x:Name="tv" TreeViewItem.Expanded="TreeViewItem_Expanded"     />

</Grid>

Here is MainWindow.cs

using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;

namespace TreeViewExample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    ObservableCollection<Author> authors = new ObservableCollection<Author>();

    public ObservableCollection<Author> Standards
    {
        get { return authors; }
        set { authors = value; }
    }
    public MainWindow()
    {
        InitializeComponent();
       this.Loaded+=MainWindow_Loaded;
       this.DataContext = this;
    }
    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        ObservableCollection<Book> Books = new ObservableCollection<Book>();
        Books.Add(new Book() { Id = 1, Name = "X" });
        Books.Add(new Book() { Id = 2, Name = "Y" });
        Books.Add(new Book() { Id = 3, Name = "Z" });

        ObservableCollection<Book> Books2 = new ObservableCollection<Book>();
        Books2.Add(new Book() { Id = 1, Name = "X" });
        Books2.Add(new Book() { Id = 2, Name = "Y" });
        Books2.Add(new Book() { Id = 3, Name = "Z" });


        Standards.Add(new Author() { Name = "I", Books = Books });
        Standards.Add(new Author() { Name = "II" });
        Standards.Add(new Author() { Name = "III", Books = Books2 });
    }

    private void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
    {
        TreeViewItem tvi = e.OriginalSource as TreeViewItem;
        Author author = tvi.Header as Author;
        author.Books.Clear();

    }
}

public class Author
{
    public string Name { get; set; }
    public ObservableCollection<Book> Books { get; set; }
}
public class Book
{
    public int Id { get; set; }
    public string Name { get; set; }
}
}

this example load sample data for the node, whenever you tried to expand the item, sub items will be cleared. let me know is this solve your problem.

1
votes

The easiest way is to override the default template to disable hiding of indicator. You can't do that with two lines of XAML code, unfortunately, but it's still easy.

First, you need to get default TreeView template and copy it to your resource dictionary. If you don't have styles dictionary, you can create new one and put everything there (brushes first, then all the styles).

Second, you need to find the trigger which hides the button and remove it (or change to IsEnabled or whatever you want):

<Trigger Property="HasItems"
         Value="false">
    <Setter TargetName="Expander"
            Property="Visibility"
            Value="Hidden" />
</Trigger>

Third, we need to give the main style our custom key, so that x:Key="{x:Type TreeView}" becomes x:Key="CustomTreeViewStyle" for example. And use it for our TreeView:

<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="MyTreeViewStyles.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Window.Resources>


<Grid>
    <TreeView Style="{StaticResource CustomTreeViewStyle}" />
</Grid>

That's it. Not the shortest solution, but an easy one and you can customize it any way you like. And you don't need to create phantom items.

You can reference this dictionary in App.xaml so every page can use it (if needed). Also, I used MergedDictionary here in case you already have some resource in the page - they will go in ResourceDictionary itself.