0
votes

I am trying to create a TreeView that can display items in a tree hirearchy. I want to be able to use code (C#) to expand and collapse TreeViewItems in the TreeView, through the properties bound to an ObservableCollection.

I have bound a property of my class to IsExpanded, and it seems to work if I set it BEFORE setting the tree's ItemSource - the newly created hierarchy will arrive pre-expanded.

But if I click a button that sets IsExpanded for an item in the collection, it does not expand or collapse the tree items in the GUI.

Here is an ugly screenshot of the program so far. The folders were manually created in an Initialize procedure.

Really ugly screenshot, so you get an idea of what we're dealing with.

Here is the TreeView xaml in the main window:

<TreeView x:Name="TheProjectTree" Margin="5" BorderBrush="{x:Null}" ItemsSource="{Binding DataSet}">
    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded" Value="{Binding Path=IsExpanded, Mode=TwoWay}" />
            <!--<EventSetter Event="Expanded" Handler="TheProjectTreeItem_Expanded" />-->
        </Style>
    </TreeView.ItemContainerStyle>
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding nodes}">
            <StackPanel Orientation="Horizontal">
                <Image Source="{Binding Path=Icon}" Height="16"/>
                <TextBlock Text=" " />
                <TextBlock Text="{Binding Path=Name}" />
                <TextBlock Text=" (" />
                <TextBlock Text="{Binding Path=Type}" />
                <TextBlock Text=")" />
            </StackPanel>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

Here is a MyProject class that has the data structures:

using System.Collections.ObjectModel;

namespace Project_X
{
    public class MyProject
    {
        public ObservableCollection<MyNode> nodes;

        public MyProject()
        {

        }

        public void Initialize()
        {
            nodes = new ObservableCollection<MyNode>();
            nodes.Add(new MyNode("Untitled Project", "Project"));
            AddFolder("0. Initialize");
            AddFolder("1. Reset");
            AddFolder("2. Migrate");
        }

        public void AddFolder(string folderName)
        {
            nodes[0].nodes.Add(new MyProject.MyNode(folderName, "Folder"));
        }

        public class MyNode
        {
            public string Name { get; set; }
            public string Type { get; set; }
            public bool IsExpanded { get; set; }
            public ObservableCollection<MyNode> nodes { get; set; }
            public MyNode(string theName, string theType)
            {
                Name = theName;
                Type = theType;
                nodes = new ObservableCollection<MyNode>();
            }
            public string Icon
            {
                get
                {
                    if (Type == "Project")
                        return "./graphics/icon_projectTree_small.png";
                    else if (Type == "Folder")
                        return "./graphics/icon_projectTree_small.png";
                    else if (Type == "Object")
                        return "./graphics/icon_projectTree_small.png";
                    else if (Type == "SQL")
                        return "./graphics/icon_projectTree_small.png";
                    else if (Type == "Text")
                        return "./graphics/icon_projectTree_small.png";
                    return "./graphics/icon_projectTree_small.png";
                }
            }
        }
    }
}

And finally, here is a little test procedure that I can call from a testing button.

private void NewProject()
{
    Project = new MyProject();                      // fire up the main project variable!
    Project.Initialize();                           // give it some dummy data!
    Project.nodes[0].IsExpanded = true;             // pre-expand the top-level project node
    TheProjectTree.ItemsSource = Project.nodes;     // assign the data set to the tree in the main window



    Project.AddFolder("test");                      // this works! adding new folders to the collection will show up in the GUI
    Project.nodes[0].IsExpanded = true;             // this does NOT work! it should collapse the top-levl project node in the tree, but it doesn't
}

I would greatly appreciate it if you could brow-beat some knowledge into me. I usually work in SQL, C# and .NET are not my strong suit. I spent the whole evening trying to wrap my head around MVVM and goodness I now feel like a really crummy programmer!

2

2 Answers

2
votes

Implement INotifyPropertyChanged interface to your MyNode Class. Which notifies that the property value has changed.

public class MyNode : INotifyPropertyChanged
{
    private bool isExpanded;

    public string Name { get; set; }
    public string Type { get; set; }
    public bool IsExpanded
    {
        get => isExpanded;
        set
        {
            isExpanded = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsExpanded)));
        }
    }
    public ObservableCollection<MyNode> nodes { get; set; }
    public MyNode(string theName, string theType)
    {
        Name = theName;
        Type = theType;
        nodes = new ObservableCollection<MyNode>();
    }

    public event PropertyChangedEventHandler PropertyChanged;
}
1
votes

Your MyNode class need to implement INotifyPropertyChanged to let the Gui know that the property has changed. Then in the setter of the IsExpanded property you will have to call NotifyPropertyChanged has explained in the given link.