The follow code loads a hierarchical collection for of CompoundObjects then of strings. But unfortunately it inserts the strings at the top of the tree instead of at the bottom(which is the behavior I’ve always seen.
I’ve tried to change the order of the enumerators, the notifiers, etc and all produce the same results. I’ve I pre-load the list it looks normal(using the same code in the thread).
Any ideas whats going on?
CompoundObject.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
namespace ComplexTreeViewLazyLoadingTest
{
public class CompoundObject : IEnumerable<object>, INotifyCollectionChanged
{
public string Name { get; set; }
public ObservableCollection<CompoundObject> objects { get; private set; }
public ObservableCollection<string> Items { get; private set; }
void OnChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
App.Current.Dispatcher.Invoke((Action<object, NotifyCollectionChangedEventArgs>)((senderr, ee) => {
CollectionChanged(senderr, ee);
}), sender, e);
}
public CompoundObject(string name)
{
Name = name;
Items = new ObservableCollection<string>();
objects = new ObservableCollection<CompoundObject>();
Items.CollectionChanged += new NotifyCollectionChangedEventHandler(OnChanged);
objects.CollectionChanged += new NotifyCollectionChangedEventHandler(OnChanged);
}
public IEnumerator<object> GetEnumerator()
{
if (objects != null) foreach(var a in objects) yield return a;
if (Items != null) foreach (var a in Items) yield return a;
yield break;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
public event NotifyCollectionChangedEventHandler CollectionChanged;
}
}
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
namespace ComplexTreeViewLazyLoadingTest
{
public partial class MainWindow : Window
{
CompoundObject c = new CompoundObject("Root");
public MainWindow()
{
InitializeComponent();
treeView.DataContext = c;
treeView.ItemsSource = c;
ThreadPool.QueueUserWorkItem(new WaitCallback(Update));
}
void Update(object data)
{
for (int i = 0; i < 10; i++)
{
Application.Current.Dispatcher.Invoke((Action<CompoundObject>)((cc) => {
c.objects.Add(cc);
}), new CompoundObject("Object " + i));
for (int j = 0; j < 5; j++)
{
Thread.Sleep(100);
Application.Current.Dispatcher.Invoke((Action<CompoundObject>)((cc) =>
{
c.objects[i].objects.Add(cc);
}), new CompoundObject("subObject " + j));
}
}
for (int i = 0; i < 8; i++)
{
Thread.Sleep(250);
Application.Current.Dispatcher.Invoke((Action<string>)((ii) =>
{
c.Items.Add("Item " + ii);
}), i.ToString());
}
}
} // MainWindow
public class DTS : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
if (element != null && item != null)
{
if (item is CompoundObject)
{
return element.FindResource("CompoundTemplate") as DataTemplate;
}
if (item is int)
{
return element.FindResource("DefaultTemplate") as DataTemplate;
}
}
return null;
}
}
}
MainWindow.xaml
<Window x:Class="ComplexTreeViewLazyLoadingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ComplexTreeViewLazyLoadingTest;assembly="
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:DTS x:Key="DTS"/>
<HierarchicalDataTemplate x:Key="CompoundTemplate" ItemsSource="{Binding Path=.}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="DefaultTemplate" ItemsSource="{Binding Path=.}">
<TextBlock Text="{Binding Path=.}" Background="Aqua" />
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<TreeView Name="treeView" ItemTemplateSelector="{StaticResource DTS}"/>
</Grid>
</Window>
Because you’re combining two collections and subscribing to the CollectionChanged directly, the change notifications are for the sub-lists instead of the ‘combined’ list. This means that you’ll get a notification that ‘string added at 0’ when really you want it added at the end of the list. In order to make this work, you’ll need to subscribe to the CollectionChanged for each sub-collection and implement your own CollectionChanged callback properly (to add the first collection’s Count to all indices reported when adding/removing strings.)