I'm trying to create a TreeView, where ItemsSource is a collection of different types of objects. Previously (VS2019) all I needed to do was to specify templates in TreeView.Resources.
<TreeView ItemsSource="{Binding collectionA}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type ns:type1}" ItemsSource="{Binding collectionB}">
<TextBlock Text="{Binding name}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type ns:type2}">
<CheckBox IsChecked="{Binding Checked}" Content="{Binding element.name}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type ns:type3}">
<CheckBox IsChecked="{Binding Checked}" Content="{Binding name}"/>
</DataTemplate>
</TreeView.Resources>
</TreeView>
Now for some reason when I try to build my project, I get an error:
All objects added to an IDictionary must have a Key attribute or some other type of key associated with them.
My understanding is that DataType should automatically generate a key ( x:Key="{x:Type ns:typeX}" ) and set a correct template. I tried defining the keys manually, but templates still don't get assigned. I managed to make it work when I added templates without keys to resources during runtime, but this is not a viable workaround.
Is this a bug in VS2022 or am I doing something wrong?
How should I make this code work? Is there a manual way of selecting templates?
CodePudding user response:
In the end I used a custom TemplateSelector to manually assign templates to objects from my collection.
public class TreeViewTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is type1)
{
var template = new HierarchicalDataTemplate();
Binding itemsSource = new Binding();
itemsSource.Path = new PropertyPath("collectionB");
template.ItemsSource = itemsSource;
FrameworkElementFactory fef = new FrameworkElementFactory(typeof(TextBlock));
Binding name = new Binding();
name.Path = new PropertyPath("name");
fef.SetBinding(TextBlock.TextProperty, name);
template.VisualTree = fef;
return template;
}
if (item is type2)
{
var template = new DataTemplate();
FrameworkElementFactory fef = new FrameworkElementFactory(typeof(CheckBox));
Binding name = new Binding();
name.Path = new PropertyPath("element.name");
Binding isChecked = new Binding();
isChecked.Path = new PropertyPath("Checked");
fef.SetBinding(CheckBox.ContentProperty, name);
fef.SetBinding(CheckBox.IsCheckedProperty, isChecked);
template.VisualTree = fef;
return template;
}
if (item is type3)
{
var template = new DataTemplate();
FrameworkElementFactory fef = new FrameworkElementFactory(typeof(CheckBox));
Binding name = new Binding();
name.Path = new PropertyPath("name");
Binding isChecked = new Binding();
isChecked.Path = new PropertyPath("Checked");
fef.SetBinding(CheckBox.ContentProperty, name);
fef.SetBinding(CheckBox.IsCheckedProperty, isChecked);
template.VisualTree = fef;
return template;
}
return null;
}
}
And in xaml:
<UserControl.Resources>
<ns:TreeViewTemplateSelector x:Key="templateSelector"/>
</UserControl.Resources>
(...)
<TreeView ItemsSource="{Binding collectionA}" ItemTemplateSelector="{StaticResource templateSelector}">