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.
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);
}
}