Erwin Erwin - 3 years ago 159
C# Question

Hierarchical structure does not get updated in view

I have basically this XAML and ViewModel:

<ResourceDictionary>

<DataTemplate DataType="{x:Type bizzTypes:WindowsDirectory}" x:Key="directory">
<Expander Header="{Binding Path=Name}" Expanded="Expander_Expanded" Cursor="Hand" MouseLeftButtonDown="Expander_MouseLeftButtonDown">
<Expander.Content>
<ItemsControl ItemsSource="{Binding Path=Directories, Mode=TwoWay}" ItemTemplate="{DynamicResource ResourceKey=directory}">
</ItemsControl>
</Expander.Content>
</Expander>
</DataTemplate>
<DataTemplate DataType="{x:Type bizzTypes:WindowsDrive}" x:Key="drive">
<Expander Header="{Binding Path=PathFormattedHeader}" Cursor="Hand">
<Expander.Content>
<ItemsControl ItemsSource="{Binding Path=Directories, Mode=TwoWay}" ItemTemplate="{StaticResource ResourceKey=directory}"></ItemsControl>
</Expander.Content>
</Expander>
</DataTemplate>
</ResourceDictionary>
</Base:ControlBase.Resources>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="350" />
<ColumnDefinition Width="3" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition />
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<GridSplitter Grid.Column="1" Grid.Row="1" Width="3" HorizontalAlignment="Left" />
<ItemsControl Grid.Row="0" Grid.ColumnSpan="3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
</ItemsControl>
<ScrollViewer HorizontalAlignment="Stretch" Grid.Row="1" VerticalAlignment="Stretch" x:Name="Drives">
<ItemsControl ItemsSource="{Binding Path=Drives, Mode=TwoWay}" ItemTemplate="{DynamicResource drive}">
</ItemsControl>
</ScrollViewer>


And I have this viewmodel behind it

public class WindowsDirectory
{
private ObservableCollection<WindowsDirectory> _directories { get; set; }
public ObservableCollection<WindowsDirectory> Directories
{
get
{
return _directories;
}
private set
{
_directories = value;
RaisePropertyChanged(nameof(Directories));
}
}
}


And it is basically working if I fill the data-tree before rendering the WPF UI. But whenever I update the
ObservableCollection<WindowsDirectory> Directories
the WPF UI does not reflect the changes made in the Obervable. The directory does not update with
RaisePropertyChanged(nameof(Directories));
and without
RaisePropertyChanged
I get an empty
ItemsControl


Anyone any ideas on why this happens?

Answer Source

Here's an example featuring:

  • HierarchicalDataTemplate
  • live update with ObservableCollection<T>
  • a button that adds random elements to the tree

Element

namespace WpfApp2
{
    public interface IFileSystemElement
    {
        string Name { get; }
    }
}

Element helper

namespace WpfApp2
{
    public abstract class FileSystemElement : IFileSystemElement
    {
        protected FileSystemElement(string name)
        {
            Name = name;
        }

        public string Name { get; }

        public override string ToString()
        {
            return Name;
        }
    }
}

File

namespace WpfApp2
{
    public class File : FileSystemElement
    {
        public File(string name) : base(name)
        {
        }
    }
}

Directory

using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace WpfApp2
{
    public class Directory : FileSystemElement, IEnumerable<IFileSystemElement>
    {
        public Directory(string name) : base(name)
        {
        }

        public ICollection<IFileSystemElement> Elements { get; } = new ObservableCollection<IFileSystemElement>();

        public IEnumerator<IFileSystemElement> GetEnumerator()
        {
            return Elements.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable) Elements).GetEnumerator();
        }

        public T Add<T>(T item) where T : IFileSystemElement
        {
            Elements.Add(item);
            return item;
        }
    }
}

View

using System;
using System.Windows;

namespace WpfApp2
{
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();

            var level0 = new Directory("level 0");
            var level1 = level0.Add(new Directory("level 1"));
            var level2 = level1.Add(new Directory("level 2"));
            var file1 = level2.Add(new File("file 1"));
            var file2 = level2.Add(new File("file 2"));
            DataContext = new[] {level0}; // to show root in tree
        }

        private Random Random { get; } = new Random();

        private Directory SelectedDirectory { get; set; }

        private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            SelectedDirectory = e.NewValue as Directory;
            Button.IsEnabled = SelectedDirectory != null;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var next = Random.Next(2);

            switch (next)
            {
                case 0:
                    SelectedDirectory.Add(new Directory("New directory"));
                    break;
                case 1:
                    SelectedDirectory.Add(new File("New file"));
                    break;
            }
        }
    }
}

View

<Window
    x:Class="WpfApp2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WpfApp2"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Grid>

        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <TreeView
            Grid.Row="0"
            ItemsSource="{Binding}"
            SelectedItemChanged="TreeView_OnSelectedItemChanged">
            <TreeView.ItemContainerStyle>
                <Style TargetType="TreeViewItem">
                    <Setter Property="IsExpanded" Value="True" />
                </Style>
            </TreeView.ItemContainerStyle>
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate DataType="local:Directory" ItemsSource="{Binding Elements}">
                    <TextBlock Text="{Binding}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>

        <Button
            x:Name="Button"
            Grid.Row="1"
            Click="Button_Click"
            Content="Button"
            IsEnabled="False" />

    </Grid>
</Window>
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download