Some function provides a flat list of strings, which looks like:
var list = new List<int>{1,2,3,4};
A TreeView now wants to transform this list into a tree with two root nodes (one for odd numbers and one for even numbers). This is just an example as the real scenario must create a much more hierachical structure. The point is that the backend provides a flat list and the view wants to transform it into a tree.
We tried a Converter as the ItemsSource but as it creates a new structure it basically breaks the binding to the original list (makes async filling impossible). Explaining why
Here is a small reproduction code:
Codebehind:
public partial class MainWindow : Window
{
public ObservableCollection<int> TreeViewSource { get; set; }
public MainWindow()
{
InitializeComponent();
DataContext = this;
TreeViewSource = new ObservableCollection<int>();
Action filler =() => { Enumerable.Range(0, 100).ToList().ForEach((i) => { Thread.Sleep(20); TreeViewSource.Add(i); }); };
Task.Run(() => filler()); // ASYNC CALL DOES NOT WORK
//filler(); // SYNC CALL DOES WORK
}
}
public class TreeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var sourceCollection = value as ObservableCollection<int>;
var outputCollection = new List<Item>();
var odd = new Item { Text = "not divisible by 2" };
var even = new Item { Text = "divisible by 2" };
even.Children.AddRange(sourceCollection.Where(x => x % 2 == 0).Select(x => new Item { Text = x.ToString() }));
odd.Children.AddRange(sourceCollection.Where(x => x % 2 != 0).Select(x => new Item { Text = x.ToString() }));
outputCollection.Add(odd);
outputCollection.Add(even);
return outputCollection;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class Item
{
public string Text { get; set; }
public List<Item> Children { get; set; }
public Item()
{
Children = new List<Item>();
}
}
Xaml:
<Window x:Class="TreeViewAsync.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TreeViewAsync"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:TreeConverter x:Key="treeConverter"/>
</Window.Resources>
<Grid>
<TreeView Name="treeView"
ItemsSource="{Binding TreeViewSource, Converter={StaticResource treeConverter}}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Item}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Text}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Window>
This link says that a DataTemplate should be used instead of a Converter on an ItemsSource. But since the template is applied to each item individually, how can it create a full tree?
Again: The ViewModel does not need the data to be a tree and therefore does not provide a treestructure but a flat list only. The View wants to display it as a tree for convenience. Due to MVVM we would like to avoid codebehind in the View.