I am trying to build a WPF TreeView with three layers. CountryReportTitle is a string property and ArticleCategoryTitlesList is a collection, both exposed from my ViewModel. There is no class hierarchy defined. This is the structure I'm looking for:
This is my attempted Xaml but I'm getting an exception in the Xaml at runtime:
{"Item has already been added. Key in dictionary: 'DataTemplateKey(ISESApp.ViewModels.ReportViewModel)' Key being added: 'DataTemplateKey(ISESApp.ViewModels.ReportViewModel)'"}
Xaml:
<TreeView ItemsSource="{Binding CountryReportTitle}">
<TreeView ItemsSource="{Binding CountryReportTitle}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:ReportViewModel}"
ItemsSource="{Binding ArticleCategoryTitlesList}">
<TextBlock Text="{Binding CategoryTitle}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:ReportViewModel}"
ItemsSource="{Binding ArticleCatagoryTypesList}">
<TextBlock Text="{Binding ArticleTitle}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:ReportViewModel}">
<TextBlock Text="{Binding ArticleTitle}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
</TreeView>
Local: is a namespace to my ViewModel:
xmlns:local="clr-namespace:MyApp.ViewModels"
What am I doing wrong, what is the best approach for this problem?
Here's my go-to example for treeviews.
Use a HierarchicalDataTemplate for elements in the tree. Note that there are three layers, and each layer is its own type. This is for convenience, but you could define one type and use one template or any mix of types for your tree. Having different types represent different things in the tree makes using templates extremely convenient.
The data classes
public class ViewModel
{
public ObservableCollection<ItemA> ItemsA { get; set; }
public ViewModel()
{
ItemsA = new ObservableCollection<ItemA>(new[]{
new ItemA{Name = "A one"},
new ItemA{Name = "A Two"},
new ItemA{Name = "A Three"},
});
}
}
public class ItemA
{
public ObservableCollection<ItemB> ItemsB { get; set; }
public string Name { get; set; }
public ItemA()
{
ItemsB = new ObservableCollection<ItemB>(new[]{
new ItemB{Name = "B one"},
new ItemB{Name = "B Two"},
new ItemB{Name = "B Three"},
});
}
}
public class ItemB
{
public ObservableCollection<ItemC> ItemsC { get; set; }
public string Name { get; set; }
public ItemB()
{
ItemsC = new ObservableCollection<ItemC>(new[]{
new ItemC{Name = "C one"},
new ItemC{Name = "C Two"},
new ItemC{Name = "C Three"},
});
}
}
public class ItemC
{
public string Name { get; set; }
}
And the UI
<TreeView ItemsSource="{Binding ItemsA}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type t:ItemA}"
ItemsSource="{Binding ItemsB}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type t:ItemB}"
ItemsSource="{Binding ItemsC}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type t:ItemC}">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
gives you a simple treeview
You need to bind the TreeView Resources inside your TreeView:
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type local:FirstLayer}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock Text="{Binding ChildrenName}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate
DataType="{x:Type local:SecondLayer}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock Text="{Binding ChildrenName}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
I think you'll need to modify your project to bind your Treeview with your ViewModel instead of a list of string
Here is a good Example
Related
I have a class that contains multiple collections of properties:
class Foo{
public ObservableCollection<Bar> Bars {get; set;}
public ObservableCollection<Baz> Bazzes {get; set;}
}
I'm trying to display this in a TreeView, where the Foo node is at the root, and then under it is a node for the Bars collection containing each of the Bar elements as subnodes, and the same for the Bazzes collection. But I can't seem to get the data template right. The closest I've managed to get is like so:
<HierarchicalDataTemplate DataType="{x:Type local:Foo}">
<TreeViewItem Header="Root">
<TreeViewItem Header="Bars" ItemsSource="{Binding Path=Bars}"/>
<TreeViewItem Header="Bazzes" ItemsSource="{Binding Path=Bazzes}"/>
</TreeViewItem>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:Bar}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<TextBlock Text=" (" Foreground="Blue" />
<TextBlock Text="{Binding Type}" Foreground="Blue" />
<TextBlock Text=")" Foreground="Blue" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Baz}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
This displays a hierarchical tree with nodes I can open to display sub-items by clicking the little triangle, but when I try to click on any of the items, it selects the entire Foo with all of its sub-items as one big selection. I'm assuming this is because the nodes containing the collections are integrated into the template for Foo and so it's treating them as all being one big node somehow? But I don't know how to get the collections to show up as sub-nodes without doing it that way.
What's the correct way to do the type of setup I'm looking for, since this is obviously not quite right?
There are a couple of fundamental issues with your implementation. The first is that the Tree is simply a map onto the bound data structure which is expected to be a tree. First we have to make your Foo class a tree...
public class BarBazBase
{
public string Name { get; set; }
public string Type { get; set; }
}
public class Bar : BarBazBase
{
public string BarSpecial { get; set; }
}
public class Baz : BarBazBase
{
public string BazSpecial { get; set; }
}
public class Foo : ObservableCollection<ObservableCollection<BarBazBase>>
{
public ObservableCollection<BarBazBase> Bars { get; set; } = new ObservableCollection<BarBazBase>();
public ObservableCollection<BarBazBase> Bazzes { get; set; } = new ObservableCollection<BarBazBase>();
public Foo()
{
Add(Bars);
Add(Bazzes);
}
}
Next we need a different template for each type of tree node. We therefore need a data template selector
public class BasBazTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
FrameworkElement fe = container as FrameworkElement;
if(item is Foo)
{
return fe.FindResource("TreeHeader") as DataTemplate;
}
if (item is ObservableCollection<BarBazBase> baseCollection)
{
if (baseCollection.Count > 0 && baseCollection[0] is Bar)
return fe.FindResource("BarHeader") as DataTemplate;
else if (baseCollection.Count > 0 && baseCollection[0] is Baz)
return fe.FindResource("BazHeader") as DataTemplate;
else
return null;
}
else if (item is Bar)
{
return fe.FindResource("BarItemTemplate") as DataTemplate;
}
else if (item is Baz)
{
return fe.FindResource("BazItemTemplate") as DataTemplate;
}
else
{
return null;
}
}
}
Finally we are ready to pull everything together in XAML...
<TreeView x:Name="treeView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Beige"
ItemsSource="{Binding Root}" ItemTemplateSelector="{StaticResource BasBazTemplateSelector}">
<TreeView.Resources>
<HierarchicalDataTemplate x:Key="TreeHeader" ItemsSource="{Binding}" >
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="BazHeader" ItemsSource="{Binding}">
<Label>Baz</Label>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="BarHeader" ItemsSource="{Binding}">
<Label>Bar</Label>
</HierarchicalDataTemplate>
<DataTemplate x:Key="BarItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<TextBlock Text=" (" Foreground="Blue" />
<TextBlock Text="{Binding Type}" Foreground="Blue" />
<TextBlock Text=")" Foreground="Blue" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="BazItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
A few loose ends...
<Window.Resources>
<local:BasBazTemplateSelector x:Key="BasBazTemplateSelector"/>
</Window.Resources>
public Foo Root { get; set; }
Root = new Foo();
Root.Bars.Add(new Bar() { Name = "a", Type = "a0", BarSpecial = "a bar" });
Root.Bars.Add(new Bar() { Name = "b", Type = "b0", BarSpecial = "another bar" });
Root.Bazzes.Add(new Baz() { Name = "c", Type = "c0", BazSpecial = "a baz" });
Root.Bazzes.Add(new Baz() { Name = "d", Type = "d0", BazSpecial = "another baz" });
This is the result...
I'm binding to a ListBox, and within the ListBox, another ListBox.
My model looks like
public interface ICriteriaDetail
{
string Title { get; }
int NumberOfEvents { get; }
ICriteriaDetail ChildCriteria { get; }
}
So, I'm using a recursive approach for the children (not a list).
When I bind to my ListView, I get the items in a list, one under the other.
The issue is, the child item is not showing at all!
The dummy data
public static IEnumerable<ICriteriaDetail> GetCriteriaList()
{
var list = new List<CompoundCriteria.Ui.Model.Interfaces.ICriteriaDetail>();
list.Add(GetCriteriaDetail("My Title", 5, false));
list.Add(GetCriteriaDetail("Other", 3, false));
list.Add(GetCriteriaDetail("Biggy", 8, true));
var child = new CompoundCriteria.Ui.Model.CriteriaDetail.CriteriaDetail("childAgain", 43, null);
var child2 = new CompoundCriteria.Ui.Model.CriteriaDetail.CriteriaDetail("childAgainAgain", 13, child);
list.Add(new CompoundCriteria.Ui.Model.CriteriaDetail.CriteriaDetail("Really big", 86, new CompoundCriteria.Ui.Model.CriteriaDetail.CriteriaDetail("smaller", 15, child2)));
return list;
}
private static ICriteriaDetail GetCriteriaDetail(string title, int events, bool hasChild)
{
if (!hasChild)
return new CompoundCriteria.Ui.Model.CriteriaDetail.CriteriaDetail(title, events, null);
var child = new CompoundCriteria.Ui.Model.CriteriaDetail.CriteriaDetail("child" + title, 13 + events, null);
return new CompoundCriteria.Ui.Model.CriteriaDetail.CriteriaDetail(title, events, child);
}
The ViewModel
public MainWindowViewModel()
{
this.Criterias = DatasourceMockup.GetCriteriaList();
}
private IEnumerable<ICriteriaDetail> _criterias;
public IEnumerable<ICriteriaDetail> Criterias
{
get { return _criterias; }
set { _criterias = value; }
}
And the ListBox in the XAML
<ListBox ItemsSource="{Binding Criterias}" >
<ListBox.Resources>
<ControlTemplate x:Key="con">
<StackPanel Orientation="Horizontal">
<StackPanel Margin="15">
<TextBlock Text="{Binding Title}" />
<TextBlock Text="Events: ">
<Run Text="{Binding NumberOfEvents}" />
</TextBlock>
</StackPanel>
<ListBox ItemsSource="{Binding ChildCriteria} Visibility="{Binding ChildCriteria, Converter={StaticResource HideIfNull}}">">
<ListBox.Resources>
<DataTemplate DataType="{x:Type cDetail:CriteriaDetail}">
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</ListBox.Resources>
</ListBox>
</StackPanel>
</ControlTemplate>
<HierarchicalDataTemplate DataType="{x:Type cDetail:CriteriaDetail}">
<Control Template="{StaticResource con}" />
</HierarchicalDataTemplate>
</ListBox.Resources>
</ListBox>
After reading TreeView, HierarchicalDataTemplate and recursive Data it appears that HierarchicalDataTemplate will suffice (no need for a DataTemplate as well) but, I'm lost as to why I'm not seeing the result I expected (even if the children are not in the desired place, I'd still hope to see them)
EDIT
I have just added a converter which makes the ListBox (the one inside the ControlTemplate) hidden if the bound data is null. I have an outline (an empty ListBox) for just 1 of the 3 (which is correct) but it doesn't bind any of the content (the Title). The output window shows nothing of any use...
Please note, there is no limit of chidren. In the example above, there is a parent child relationship, but it could be parent-child-child-child etc
1) It is hard to understand your purpose , why ListBox, if you have ChildCriteria in model, not IEnumerable<ICriteriaDetail>?
2) Also you have binding errors, i changed xaml to
<ListBox ItemsSource="{Binding Criterias}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel Margin="15">
<TextBlock Text="{Binding Title}" />
<TextBlock Text="Events: ">
<Run Text="{Binding NumberOfEvents}" />
</TextBlock>
</StackPanel>
<TextBox Text="{Binding Path=DataContext.ChildCriteria.Title,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListBoxItem},
AncestorLevel=1}}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and got
EDIT1:
1) I changed data model to
public class CriteriaDetail : ICriteriaDetail
{
public CriteriaDetail(string title, int numberOfEvents, IEnumerable<ICriteriaDetail> childCriteria)
{
Title = title;
NumberOfEvents = numberOfEvents;
ChildCriteria = childCriteria;
}
public string Title { get; set; }
public int NumberOfEvents { get; set; }
public IEnumerable<ICriteriaDetail> ChildCriteria { get; set; }
}
2) Changed xaml to
<TreeView ItemsSource="{Binding Criterias}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding ChildCriteria}">
<TextBlock Foreground="Red" Text="{Binding Title}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
3) Changed GetCriteriaDetail to
private static ICriteriaDetail GetCriteriaDetail(string title, int events, bool hasChild)
{
if (!hasChild)
return new CriteriaDetail(title, events, null);
var child3 = new CriteriaDetail("child3" + title, 13 + events, null);
var child2 = new CriteriaDetail("child2" + title, 13 + events, new ICriteriaDetail[] { child3 });
var child1 = new CriteriaDetail("child1" + title, 13 + events, new ICriteriaDetail[] { child2 });
return new CriteriaDetail(title, events, new ICriteriaDetail[] {child1});
}
and got this. I think it looks more like you want
I'd like to display the following structure in a WPF Treeview:
public class Group{
public string Groupname;
public IEnumerable<Group> Groups;
public Ienumerable<User> Member;
}
My ViewModel looks like this:
public class ViewModel{
public Group RootGroup;
}
I think the XAML Code should look like this:
<TreeView>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource={Binding ViewModel.RootGroup}>
<TextBlock Text={Binding Groupname}/>
<HierarchicalDataTemplate ItemsSource={Binding Member}>
<TextBlock Text={Binding Displayname}/>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
What I want it to look like:
RootGroup-Name
-Member1
-Member2
-Member3
-Member4
-SubGroup1
-Member1
-Sub-SubGroup1
-Member1
-SubGroup2
-Sub-SubGroup2
-Sub-Sub-SubGroup1
-Member1
I've bound the DataContext to itself so this shouldn't be the reason why my TreeView wont show anything.
Finally I just found the solution by myself:
<TreeView Grid.Row="1" ItemsSource="{Binding MVM.RootGroup}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Groups}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Members}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Displayname}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
But the Users from the first level get lost.
To achieve that behavior you should change ViewModel and ItemTemplate. Here some code that I've used in my project.
<HierarchicalDataTemplate DataType="local:GItemViewModel" ItemsSource="{Binding Path=Nodes}">
<DockPanel>
<TextBlock Margin="2" VerticalAlignment="Center" Text="{Binding Path=Name}" FontSize="12" />
</DockPanel>
</HierarchicalDataTemplate>
ViewModel for this. Here Name is name for group and name for member depends on the object. Member is the node with Nodes is set to null.
public sealed class GItemViewModel : INotifyPropertyChanged
{
private string _name = string.Empty;
public string Name
{
get { return _name; }
set { _name = value; NotifyPropertyChanged( "Name" ); }
}
public ObservableCollection<GItemViewModel> Nodes { get; set; }
...
}
I hope this helps.
Update
To set root element Name you can add into the ViewModel elemet like this. And bind the root collection to the treeView like this.
GItemViewModel vm = new GItemViewModel();
GItemViewModel root = new GItemVieModel() { Name = "Root" };
vm.Nodes.Add( root );
treeView.ItemsSource = vm.Nodes;
I want to bind the object of my Node class to a treeview.
Node Class
public class Node
{
public string Text { get; set; }
public List<Node> Child { get; set; }
public List<NodeAttribute> Attributes { get; set; }
public bool IsLeafNode { get; set; }
public bool HasAttributes { get; set; }
}
NodeAttribute
public class NodeAttribute
{
public string Attribute { get; set; }
public string Value { get; set; }
}
I am able to dislplay Child using HierarchicalDataTemplate but not the Attributes.
<TreeView Name="tv" ItemsSource="{Binding Child}">
<TreeView.Resources>
<HierarchicalDataTemplate ItemsSource="{Binding Child}" DataType="{x:Type tvcc:Node}">
<StackPanel>
<TextBlock Text="{Binding Text}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
I want to style Child and attributes differently.
You need a CompositeCollection to combine both Child and Attributes and bind them to the HierarchicalDataTemplate's ItemsSource
I prepared a sample for you
Added a CompositeCollection ChildAndAttributes in Node class
public class Node
{
public Node()
{
Child = new List<Node>();
Attributes = new List<NodeAttribute>();
}
public string Text { get; set; }
public List<Node> Child { get; set; }
public List<NodeAttribute> Attributes { get; set; }
public bool IsLeafNode { get; set; }
public bool HasAttributes { get; set; }
CompositeCollection comp;
public CompositeCollection ChildAndAttributes
{
get
{
if (comp == null)
{
comp = new CompositeCollection();
comp.Add(new CollectionContainer() { Collection = Child });
comp.Add(new CollectionContainer() { Collection = Attributes });
}
return comp;
}
}
}
you can adjust the composite collection to have attributes first if needed, just change the order of adding to collection
XAML
<TreeView Name="tv">
<TreeView.Resources>
<!--created seperate tempates for Node and NodeAttribute-->
<DataTemplate DataType="{x:Type tvcc:Node}">
<TextBlock Text="{Binding Text}"
Foreground="Navy" />
</DataTemplate>
<DataTemplate DataType="{x:Type tvcc:NodeAttribute}">
<TextBlock Text="{Binding Attribute}"
Foreground="Crimson" />
</DataTemplate>
</TreeView.Resources>
<!--moved your original template to Item Template or tree-->
<TreeView.ItemTemplate>
<!--binded ChildAndAttributes to ItemsSource-->
<HierarchicalDataTemplate ItemsSource="{Binding ChildAndAttributes}">
<!--this will pick up the data template for respective element-->
<ContentControl Content="{Binding}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<!--below is sample data-->
<tvcc:Node Text="hello">
<tvcc:Node.Attributes>
<tvcc:NodeAttribute Attribute="attribute child" />
</tvcc:Node.Attributes>
<tvcc:Node.Child>
<tvcc:Node Text="hello child">
<tvcc:Node.Attributes>
<tvcc:NodeAttribute Attribute="attribute child 1" />
</tvcc:Node.Attributes>
</tvcc:Node>
</tvcc:Node.Child>
</tvcc:Node>
<tvcc:Node Text="hello2">
<tvcc:Node.Attributes>
<tvcc:NodeAttribute Attribute="attribute child 2" />
</tvcc:Node.Attributes>
<tvcc:Node.Child>
<tvcc:Node Text="hello child 2">
<tvcc:Node.Attributes>
<tvcc:NodeAttribute Attribute="attribute child 2" />
</tvcc:Node.Attributes>
</tvcc:Node>
</tvcc:Node.Child>
</tvcc:Node>
</TreeView>
you can modify the data templates as per your needs
Alternative approach
You can also modify your existing template to achieve similar without using CompositCollections
<HierarchicalDataTemplate ItemsSource="{Binding Child}"
DataType="{x:Type tvcc:Node}">
<StackPanel>
<TextBlock Text="{Binding Text}"
Foreground="Navy" />
<ItemsControl ItemsSource="{Binding Attributes}" Margin="10,0,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Attribute}"
Foreground="Crimson" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</HierarchicalDataTemplate>
You can try both and choose what is best suited for your needs
You will have to use CompositeCollection to solve your problem.
Here is the converter which converts your node to composite collection
public class CompositeNodeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var node = value as Node;
CompositeCollection collection = new CompositeCollection();
CollectionContainer container = new CollectionContainer();
container.Collection = node.Child;
collection.Add(container);
container = new CollectionContainer();
container.Collection = node.Attributes;
collection.Add(container);
return collection;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
and then you can bind this in your xaml as:
<TreeView Name="tv" ItemsSource="{Binding NodeCollection}">
<TreeView.Resources>
<controls:CompositeNodeConverter x:Key="CompositeNodeConverter"></controls:CompositeNodeConverter>
<HierarchicalDataTemplate ItemsSource="{Binding Converter={StaticResource CompositeNodeConverter}}" DataType="{x:Type controls:Node}">
<StackPanel>
<TextBlock Text="{Binding Text}"/>
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type controls:NodeAttribute}">
<StackPanel>
<TextBlock Text="{Binding Value}"/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
In my WPF application, if I open one of the treeviewitems to display all the child items, it goes on a chain event and slowly starts opening all the parent items. For example if I opened the Red, it'd slowly open the blue's until they are all open;
My TreeView Code;
<TreeView x:Name="tvTagList" Margin="15, 40, 15, 50" SelectedItemChanged="tvTagList_SelectedTagChanged" ItemsSource="{Binding}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="{x:Null}" BorderBrush="{DynamicResource ExtryzeAccentBrushSecondary}" BorderThickness="2" ScrollViewer.CanContentScroll="True" Foreground="White"
VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type DataBind:TagClass}" ItemsSource="{Binding Children}" >
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<TextBlock Text="{Binding TagClassMagic}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type DataBind:TagEntry}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<TextBlock Text="{Binding TagFileName}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
My classes;
public class TagClass
{
public string TagClassMagic { get; set; }
public ITagClass RawClass { get; set; }
public List<TagEntry> TagEntries = new List<TagEntry>();
public IList Children
{
get
{
return new CompositeCollection()
{
new CollectionContainer() { Collection = TagEntries }
};
}
}
}
public class TagEntry
{
public string TagFileName { get; set; }
public ITagEntry RawTag { get; set; }
}
I think this is because Children every time returns a new CompositeCollection which is propably expanded by default. Try returning TagEntries directly or return a new CompositeCollection at first time only.
Turns out we had to change;
VirtualizingStackPanel.VirtualizationMode="Recycling"
to
VirtualizingStackPanel.VirtualizationMode="Standard"