Search code examples
c#wpfdata-bindingtreeview

WPF TreeView structure lost after update through ICommand


I am developing a WPF project which, for now, implements a ComboBox, Button, ICommand and TreeView. The TreeView is generated through the ICommand interface that binds to the Button. While running the app and after selecting the item from the ComboBox + clicking the Button my TreeView is displayed as expected. The xaml:

<Window.Resources>
        <vm:RevitVM x:Key="viewmodel"/>
        <HierarchicalDataTemplate DataType="{x:Type vm:ParentNew}"
                                  ItemsSource="{Binding Children}">
            <TextBlock Text="{Binding Name}"/>
        </HierarchicalDataTemplate>
        <DataTemplate DataType="{x:Type vm:ChildNew}">
            <TextBlock Text="{Binding Name}"/>
        </DataTemplate>
        
    </Window.Resources>

    <Grid DataContext="{StaticResource viewmodel}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="2*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="6*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="1">
            <ComboBox ItemsSource="{Binding UniqueAreaTypes}"
                      SelectedItem="{Binding SelectedAreaType}">
                <ComboBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock Text="{Binding AreaType}"/>
                        </StackPanel>
                    </DataTemplate>
                </ComboBox.ItemTemplate>
            </ComboBox>
        </StackPanel>
        <Button Content="Search" 
            Command="{Binding SearchCommand}"
            Grid.Row="5"
            Grid.Column="0"
            Margin="4"/>
        <TreeView
            Grid.Row="1"
            Grid.Column="1">
            <TreeViewItem ItemsSource="{Binding FamilyList}" Header="Test"
                         IsExpanded="False"/>
        </TreeView>
    </Grid>

The problem arises when the TreeView is expanded followed by using the Button for the second time (regardless of the ComboBox selected item). My TreeView only displays the updated Parents and not the Children. It looks like the binding is somehow lost.

        public RevitVM()
        {
            SearchCommand = new SearchCommand(this);
            FamilyList = new ObservableCollection<ParentNew>();
        }



        public void SetTree()
        {
            FamilyList.Clear();
            foreach (var a in AreasToShow)
            {
                ParentNew parent = new ParentNew(); 
                parent.Name = a.Name;
                FamilyList.Add(parent); 
                ObservableCollection<ChildNew> children = new ObservableCollection<ChildNew>();
                foreach (var r in RoomsToShow)
                {
                    if (r.Position == a.Position) 
                    {
                        ChildNew child = new ChildNew();
                        child.Name = r.Name;
                        children.Add(child);
                    }
                }
                parent.Children = children;
            }
        }


        public void MakeQuery()
        {
            SetTree();
        }

Does this has to do with the fact that I am clearing and re-writing the property ObservableCollection<ParentNew> FamilyList through a ICommand interface?

public class SearchCommand : ICommand
{
    public RevitVM VM { get; set; }

    public SearchCommand(RevitVM revitVM)
    {
        VM = revitVM;
    }

    public event EventHandler? CanExecuteChanged;

    public bool CanExecute(object? parameter)
    {
        return true;
    }

    public void Execute(object? parameter)
    {
        VM.MakeQuery();
    }

The ParentNew class:

public class ParentNew
{
    public string Name { get; set; }
    public int Id { get; set; }
    public ObservableCollection<ChildNew> Children { get; set; }
}

I was expecting the updated FamilyList property to be shown on the Window while maintaining its structure after expanding the tree and clicking the Button.

It is worth noting that if TreeViewItem.IsExpanded is set to False, not manually expanded and the Button clicked, then the TreeView maintain its structure with the updated Children. When I set the TreeViewItem.IsExpanded to True in the xaml, the TreeView doesn't show the Children even if I am running the app for the first time. Is TreeViewItem.IsExpanded triggering something that I am not able to see?

At the moment I am not using any code behind and I don't intend to. I am also fairly new to the c# language and WPF so I tried to search for the answer by reading several StackOverflow posts on the subject but to no avail. What am I missing? Thanks in advance.


Solution

  • you assign Children collection (parent.Children = children;) but UI doesn't know about that.

    To ensure that UI is updated, implement INotifyPropertyChanged interface in ParentNew class and raise PropertyChanged event.

    But there is also a simple fix - assign collection before adding parent object to FamilyList:

    ObservableCollection<ChildNew> children = new ObservableCollection<ChildNew>();
    parent.Children = children;
    FamilyList.Add(parent); 
    
    foreach (var r in RoomsToShow)
    {
        if (r.Position == a.Position) 
        {
            ChildNew child = new ChildNew();
            child.Name = r.Name;
            children.Add(child);
        }
    }