0
votes

I have an ObservableCollection that I populate during runtime which is bound to the TreeView. When the collection is updated, the root object of the collection appears in the TreeView without any way to expand it (see the first image).

I assumed this meant there was an issue with the binding, however, removing TreeView.ItemCotainerStyle tag makes the arrow appear and everything works as intended (see the second image). This behaviour works in reverse too, if the style tag isn't in the view and I add it after the collection is updated then the arrows will appear.

The style tags aren't necessary for any function in my project to work, they're just leftover from an example I was working from.

<TreeView ItemsSource="{Binding CompileMessages}">
    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded"
                    Value="{Binding IsExpanded, Mode=TwoWay}" />
            <Setter Property="IsSelected"
                    Value="{Binding IsSelected, Mode=TwoWay}" />
            <Setter Property="FontWeight"
                    Value="Normal" />
        </Style>
    </TreeView.ItemContainerStyle>
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type models:CompileMessagesDto}"
                                  ItemsSource="{Binding Path=Children}">
            <StackPanel Orientation="Horizontal">
                <materialDesign:PackIcon Kind="{Binding Path=State}"
                                         Margin="3"
                                         Foreground="White" />
                <TextBlock Text="{Binding Path=Path}"
                           FontWeight="Normal"
                           Foreground="White"
                           FontSize="12"
                           Margin="3" />
                <TextBlock Text="{Binding Path=Description}"
                           FontWeight="Normal"
                           FontSize="12"
                           Foreground="#ff8000"
                           Margin="3" />
            </StackPanel>
        </HierarchicalDataTemplate>
    </TreeView.Resources>
</TreeView>
public class CompileMessagesDto
{
    public string State { get; set; } = "";
    public string Path { get; set; } = "";
    public string Description { get; set; } = "";
    public string Parent { get; set; }

    public ObservableCollection<CompileMessagesDto> Children { get; set; } = new ObservableCollection<CompileMessagesDto>();
}

TreeView before modification

TreeView after removing the style tag

Initialising the collection with test values displays the arrows, it's only after the collection is modified during runtime that the TreeView behaves like this. I use MaterialDesignInXaml if that helps at all.

Edit (how the collection is updated)

It's definitely not the best way to do it but I loop through a collection of compiler messages and add them to the collection. I'm left with every object and a reference to their parent.

foreach (CompilerResultMessage message in messages)
{
    CompileMessagesDto compileMessage = new CompileMessagesDto { State = message.State.ToString(), Path = message.Path, Description = message.Description, Parent = parent };

    MessageCrawl(message.Messages, message.Path);

    CompileMessages.Add(compileMessage);
}

Then I set each object's children, and remove every object from the collection that isn't the root object. Leaving the root with the tree of children in it.

List<CompileMessagesDto> removeItems = new List<CompileMessagesDto>();

foreach (CompileMessagesDto message in CompileMessages)
{
    message.Children = new ObservableCollection<CompileMessagesDto>(CompileMessages.Where(c => c.Parent == message.Path));

    if (message.Parent != "Root") { removeItems.Add(message); }
}

foreach (CompileMessagesDto message in removeItems)
{
    CompileMessages.Remove(message);
}
1
This is because your bindings in the Style throw errors. Your data models neither have a IsExpanded nor a IsSelected property.BionicCode
@BionicCode I edited the question to show how the collection is updated. Even when the style is removed before running the solution, the TreeView doesn't display correctly. If I add the style back in (the same way I remove it in the images) then the arrows will appear.wormit

1 Answers

0
votes

You are overwriting the binding source CompileMessagesDto.Children:

message.Children = new ObservableCollection<CompileMessagesDto>(...);

Since CompileMessagesDto doesn't implement INotifyProeprtyChanged the Binding won't be able to recognize the property change.

General rule of data binding: the binding source must always implement it's properties as DependencyProperty if the source is a subclass of DependencyObject. Otherwise the source object must implement INotifyPropertyChanged.
If you don't do this, your will create potential memory leaks.

To solve your problem either let CompileMessagesDto implement INotifyPropertyChanged and raise the PropertyChanged event from the CompileMessagesDto.Children property (recommended solution).

Alternatively keep the current ObservableCollection instance and use Add and Remove to modify it.

// Using for-statement allows to get rid of the 'removedItems' collection and the second foreach-statement (improve performance)
for (int index = CompileMessages.Count - 1; index >= 0; index--)
{
  CompileMessages
    .Where(c => c.Parent == message.Path)
    .ToList()
    .ForEach(message.Children.Add);

  if (message.Parent != "Root") 
  { 
    CompileMessages.Remove(message); 
  }
}

Also note that your Style does throw errors. CompileMessagesDto does neither have a IsExpanded nor a IsSelected property.