Performant way to filter WPF virtualized TreeView The Next CEO of Stack OverflowIn WPF can you filter a CollectionViewSource without code behind?How to filter a wpf treeview hierarchy using an ICollectionView?Filter WPF TreeView using MVVM“BindingExpression path error” using ItemsControl and VirtualizingStackPanelWPF: Filter TreeView without collapsing it's nodesHow to filter a TreeView in WPF?What is the correct way to create a single-instance WPF application?Is DateTime.Now the best way to measure a function's performance?Virtual member call in a constructorWhat is the best way to iterate over a dictionary?WPF image resourcesWhat is the difference between an abstract function and a virtual function?Trying to bind a HierarchicalDataTemplate's ItemsSource property to a CollectionViewSource (to perform grouping and filtering)Data binding to SelectedItem in a WPF TreeviewHow do I exit a WPF application programmatically?WPF change Button Content on ViewModel.PropertyChanged event
Man transported from Alternate World into ours by a Neutrino Detector
What is the difference between "hamstring tendon" and "common hamstring tendon"?
Is it ever safe to open a suspicious HTML file (e.g. email attachment)?
Is there such a thing as a proper verb, like a proper noun?
Do scriptures give a method to recognize a truly self-realized person/jivanmukta?
IC has pull-down resistors on SMBus lines?
Towers in the ocean; How deep can they be built?
Spaces in which all closed sets are regular closed
My ex-girlfriend uses my Apple ID to login to her iPad, do I have to give her my Apple ID password to reset it?
Are the names of these months realistic?
Is a distribution that is normal, but highly skewed, considered Gaussian?
Redefining symbol midway through a document
Purpose of level-shifter with same in and out voltages
What day is it again?
Traduction de « Life is a roller coaster »
Which one is the true statement?
what's the use of '% to gdp' type of variables?
Physiological effects of huge anime eyes
Help! I cannot understand this game’s notations!
Players Circumventing the limitations of Wish
Is it convenient to ask the journal's editor for two additional days to complete a review?
Strange use of "whether ... than ..." in official text
What difference does it make using sed with/without whitespaces?
What CSS properties can the br tag have?
Performant way to filter WPF virtualized TreeView
The Next CEO of Stack OverflowIn WPF can you filter a CollectionViewSource without code behind?How to filter a wpf treeview hierarchy using an ICollectionView?Filter WPF TreeView using MVVM“BindingExpression path error” using ItemsControl and VirtualizingStackPanelWPF: Filter TreeView without collapsing it's nodesHow to filter a TreeView in WPF?What is the correct way to create a single-instance WPF application?Is DateTime.Now the best way to measure a function's performance?Virtual member call in a constructorWhat is the best way to iterate over a dictionary?WPF image resourcesWhat is the difference between an abstract function and a virtual function?Trying to bind a HierarchicalDataTemplate's ItemsSource property to a CollectionViewSource (to perform grouping and filtering)Data binding to SelectedItem in a WPF TreeviewHow do I exit a WPF application programmatically?WPF change Button Content on ViewModel.PropertyChanged event
I've got a hierarchical data structure which i'm trying to visualize with a WPF TreeView and hierarchical data templates. The number of items can reach millions, so i decided to try the virtualization features which work easy and well, besides that one problem which arises when i use "Recycling" instead of "Standard" for the VirtualizationMode. A long time ago someone else seemed to have a similar problem:
"BindingExpression path error" using ItemsControl and VirtualizingStackPanel
Nevertheless, i have troubles to implement a working and performant filtering.
I tried two different approaches, based on hints i could find on the internet, like
- How to filter a TreeView in WPF?
- In WPF can you filter a CollectionViewSource without code behind?
- Filter WPF TreeView using MVVM
- How to filter a wpf treeview hierarchy using an ICollectionView?
- WPF: Filter TreeView without collapsing it's nodes
- Filtering Treeview
The first is to use a Converter to set TreeViewItem.Visibility based on a filter, the second is to create ICollectionView ad hoc for all elements and assign the same filter predicate to each one.
I like the first approach because it requires less code, seems to be more clear, and requires less hacks, and i feel like i had to use that one hack (HackToForceVisibilityUpdate) only because i don't know better, but it slows down the UI considerably and i don't know how to fix that.
The problem with the second approach is that it collapses all nodes when the filter is changed (which i think could be fixed by tracking the states before the filter change and restoring them afterwards) and that it involves a lot of additional code (for the sake of the example, a singleton hack is used to not blow the code up too much and adding/removing items won't work).
I feel like both approaches could be fixed but i cannot figure out how and i really would like to fix the performance issues of the first approach.
It's a lot of code to show in a post and if you don't like the code blocks below, here are the files as pastebin
- approach 1 - cs
- approach 1 - xaml
- approach 2 - cs
- approach 2 - xaml
and as an archive containing a VS solution and
- a project WpfVirtualizedTreeViewPerItemVisibility for the 1st and
- a project WpfVirtualizedTreeViewPerItemVisibility3 for the 2nd approach
which can be found here
The first approach:
The data structures:
public class TvItemBase
public class TvItemType1 : TvItemBase
public string Name1 get; set;
public List<TvItemBase> Entries get; = new List<TvItemBase>();
public class TvItemType2 : TvItemBase
public string Name2 get; set;
public int i get; set;
The converter looks like this (it corresponds to the "filterFunc" in the 2nd approach)
public class TvItemType2VisibleConverter : IMultiValueConverter
public TvItemType2VisibleConverter()
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
if (values.Length < 2)
return Visibility.Visible;
var tvItem = values[0] as TreeViewItem;
if (tvItem == null)
return Visibility.Visible;
var entry = tvItem.DataContext as TvItemType2;
if (entry == null)
return Visibility.Visible;
var model = values[1] as IFilterProvider;
if (model == null)
return Visibility.Visible;
if (!model.ShowA)
return Visibility.Collapsed;
else if (entry.i % 2 == 0)
return Visibility.Collapsed;
else
return Visibility.Visible;
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) throw new System.NotImplementedException();
and the window impl, which is also the view model, looks like this
public interface IFilterProvider
bool ShowA get;
public partial class MainWindow : Window, INotifyPropertyChanged, IFilterProvider
public event PropertyChangedEventHandler PropertyChanged;
void NotifyChanged(string name) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
bool ShowA_ = true;
public bool ShowA
get return ShowA_;
set ShowA_ = value; NotifyChanged(nameof(ShowA)); NotifyChanged(nameof(HackToForceVisibilityUpdate));
public bool HackToForceVisibilityUpdate get return true;
void generateTestItems(TvItemType1 parent, int nof1, int nof2, int levels)
for (int i = 0; i < nof1; i++)
var i1 = new TvItemType1 Name1 = string.Format("F_0.1.2.3", levels, i, nof1, nof2) ;
parent.Entries.Add(i1);
if (levels > 0)
generateTestItems(i1, nof1, nof2, levels - 1);
for (int i = 0; i < nof2; i++)
parent.Entries.Add(new TvItemType2 Name2 = string.Format("0.1.2.3", levels, nof1 + i, nof1, nof2), i = nof1 + i );
public MainWindow()
InitializeComponent();
DataContext = this;
var i1 = new TvItemType1 Name1 = "root" ;
generateTestItems(i1, 10, 1000, 3);
tv.ItemsSource = new List<TvItemBase> i1 ;
Finally, here's the xaml:
<Window.Resources>
<local:TvItemType2VisibleConverter x:Key="TvItemType2VisibleConverter"/>
<HierarchicalDataTemplate DataType="x:Type local:TvItemType1" ItemsSource="Binding Entries">
<TextBlock Text="Binding Name1" />
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="StaticResource TvItemType2VisibleConverter">
<Binding RelativeSource="RelativeSource Self" />
<!-- todo: how to specify the filter provider through a view model property (using Path and Source?) -->
<Binding RelativeSource="RelativeSource Mode=FindAncestor, AncestorType=local:IFilterProvider" />
<!-- todo: how to enforce filter reevaluation without this hack -->
<Binding Path="HackToForceVisibilityUpdate" RelativeSource="RelativeSource Mode=FindAncestor, AncestorType=local:IFilterProvider" />
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="IsExpanded" Value="True" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="x:Type local:TvItemType2">
<TextBlock Text="Binding Name2" />
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<ToggleButton Content="A" IsChecked="Binding ShowA, Mode=TwoWay" Width="20" Height="20" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TreeView x:Name="tv"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.CanContentScroll="True"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard" Margin="0,24,0,0" />
<!-- todo: when using VirtualizingStackPanel.VirtualizationMode="Recycling", a lot of
System.Windows.Data Error: 40 : BindingExpression path error: 'Entries' property not found on 'object' ''TvItemType2' (HashCode=...)'. BindingExpression:Path=Entries; DataItem='TvItemType2' (HashCode=...); target element is 'TreeViewItem' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
are flooding the output window.
See f.e. https://stackoverflow.com/questions/4950208/bindingexpression-path-error-using-itemscontrol-and-virtualizingstackpanel -->
</Grid>
Code for the second approach:
The data structures:
public class TvItemBase
public class TvItemType1 : TvItemBase
public string Name1 get; set;
public List<TvItemBase> Entries get; = new List<TvItemBase>();
public ICollectionView FilteredEntries
get
var dv = CollectionViewSource.GetDefaultView(Entries);
dv.Filter = MainWindow.singleton.filterFunc; // todo:hack
return dv;
public class TvItemType2 : TvItemBase
public string Name2 get; set;
public int i get; set;
and the window impl, which is also the view model, looks like this
public interface IFilterProvider
bool ShowA get;
public partial class MainWindow : Window, INotifyPropertyChanged, IFilterProvider
public event PropertyChangedEventHandler PropertyChanged;
void NotifyChanged(string name) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
bool ShowA_ = true;
public bool ShowA
get return ShowA_;
set
ShowA_ = value;
// todo:hack
// todo:why does this update the whole tree
(tv.ItemsSource as ICollectionView).Refresh();
NotifyChanged(nameof(ShowA));
void generateTestItems(TvItemType1 parent, int nof1, int nof2, int levels)
for (int i = 0; i < nof1; i++)
var i1 = new TvItemType1 Name1 = string.Format("F_0.1.2.3", levels, i, nof1, nof2) ;
parent.Entries.Add(i1);
if (levels > 0)
generateTestItems(i1, nof1, nof2, levels - 1);
for (int i = 0; i < nof2; i++)
parent.Entries.Add(new TvItemType2 Name2 = string.Format("0.1.2.3", levels, nof1 + i, nof1, nof2), i = nof1 + i );
public bool filterFunc(object obj)
var entry = obj as TvItemType2;
if (entry == null)
return true;
var model = this;
if (!model.ShowA)
return false;
else if (entry.i % 2 == 0)
return false;
else
return true;
public MainWindow()
InitializeComponent();
DataContext = this;
singleton = this; // todo:hack
var i1 = new TvItemType1 Name1 = "root" ;
generateTestItems(i1, 10, 1000, 3);
//generateTestItems(i1, 3, 10, 3);
var l = new List<TvItemBase> i1 ;
var dv = CollectionViewSource.GetDefaultView(l);
dv.Filter = filterFunc;
tv.ItemsSource = dv;
public static MainWindow singleton = null; // todo:[really big]hack
Finally, here's the xaml:
<Window.Resources>
<HierarchicalDataTemplate DataType="x:Type local:TvItemType1" ItemsSource="Binding FilteredEntries">
<TextBlock Text="Binding Name1" />
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="x:Type local:TvItemType2">
<TextBlock Text="Binding Name2" />
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<ToggleButton Content="A" IsChecked="Binding ShowA, Mode=TwoWay" Width="20" Height="20" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TreeView x:Name="tv"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.CanContentScroll="True"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard" Margin="0,24,0,0" />
</Grid>
c# wpf treeview
add a comment |
I've got a hierarchical data structure which i'm trying to visualize with a WPF TreeView and hierarchical data templates. The number of items can reach millions, so i decided to try the virtualization features which work easy and well, besides that one problem which arises when i use "Recycling" instead of "Standard" for the VirtualizationMode. A long time ago someone else seemed to have a similar problem:
"BindingExpression path error" using ItemsControl and VirtualizingStackPanel
Nevertheless, i have troubles to implement a working and performant filtering.
I tried two different approaches, based on hints i could find on the internet, like
- How to filter a TreeView in WPF?
- In WPF can you filter a CollectionViewSource without code behind?
- Filter WPF TreeView using MVVM
- How to filter a wpf treeview hierarchy using an ICollectionView?
- WPF: Filter TreeView without collapsing it's nodes
- Filtering Treeview
The first is to use a Converter to set TreeViewItem.Visibility based on a filter, the second is to create ICollectionView ad hoc for all elements and assign the same filter predicate to each one.
I like the first approach because it requires less code, seems to be more clear, and requires less hacks, and i feel like i had to use that one hack (HackToForceVisibilityUpdate) only because i don't know better, but it slows down the UI considerably and i don't know how to fix that.
The problem with the second approach is that it collapses all nodes when the filter is changed (which i think could be fixed by tracking the states before the filter change and restoring them afterwards) and that it involves a lot of additional code (for the sake of the example, a singleton hack is used to not blow the code up too much and adding/removing items won't work).
I feel like both approaches could be fixed but i cannot figure out how and i really would like to fix the performance issues of the first approach.
It's a lot of code to show in a post and if you don't like the code blocks below, here are the files as pastebin
- approach 1 - cs
- approach 1 - xaml
- approach 2 - cs
- approach 2 - xaml
and as an archive containing a VS solution and
- a project WpfVirtualizedTreeViewPerItemVisibility for the 1st and
- a project WpfVirtualizedTreeViewPerItemVisibility3 for the 2nd approach
which can be found here
The first approach:
The data structures:
public class TvItemBase
public class TvItemType1 : TvItemBase
public string Name1 get; set;
public List<TvItemBase> Entries get; = new List<TvItemBase>();
public class TvItemType2 : TvItemBase
public string Name2 get; set;
public int i get; set;
The converter looks like this (it corresponds to the "filterFunc" in the 2nd approach)
public class TvItemType2VisibleConverter : IMultiValueConverter
public TvItemType2VisibleConverter()
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
if (values.Length < 2)
return Visibility.Visible;
var tvItem = values[0] as TreeViewItem;
if (tvItem == null)
return Visibility.Visible;
var entry = tvItem.DataContext as TvItemType2;
if (entry == null)
return Visibility.Visible;
var model = values[1] as IFilterProvider;
if (model == null)
return Visibility.Visible;
if (!model.ShowA)
return Visibility.Collapsed;
else if (entry.i % 2 == 0)
return Visibility.Collapsed;
else
return Visibility.Visible;
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) throw new System.NotImplementedException();
and the window impl, which is also the view model, looks like this
public interface IFilterProvider
bool ShowA get;
public partial class MainWindow : Window, INotifyPropertyChanged, IFilterProvider
public event PropertyChangedEventHandler PropertyChanged;
void NotifyChanged(string name) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
bool ShowA_ = true;
public bool ShowA
get return ShowA_;
set ShowA_ = value; NotifyChanged(nameof(ShowA)); NotifyChanged(nameof(HackToForceVisibilityUpdate));
public bool HackToForceVisibilityUpdate get return true;
void generateTestItems(TvItemType1 parent, int nof1, int nof2, int levels)
for (int i = 0; i < nof1; i++)
var i1 = new TvItemType1 Name1 = string.Format("F_0.1.2.3", levels, i, nof1, nof2) ;
parent.Entries.Add(i1);
if (levels > 0)
generateTestItems(i1, nof1, nof2, levels - 1);
for (int i = 0; i < nof2; i++)
parent.Entries.Add(new TvItemType2 Name2 = string.Format("0.1.2.3", levels, nof1 + i, nof1, nof2), i = nof1 + i );
public MainWindow()
InitializeComponent();
DataContext = this;
var i1 = new TvItemType1 Name1 = "root" ;
generateTestItems(i1, 10, 1000, 3);
tv.ItemsSource = new List<TvItemBase> i1 ;
Finally, here's the xaml:
<Window.Resources>
<local:TvItemType2VisibleConverter x:Key="TvItemType2VisibleConverter"/>
<HierarchicalDataTemplate DataType="x:Type local:TvItemType1" ItemsSource="Binding Entries">
<TextBlock Text="Binding Name1" />
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="StaticResource TvItemType2VisibleConverter">
<Binding RelativeSource="RelativeSource Self" />
<!-- todo: how to specify the filter provider through a view model property (using Path and Source?) -->
<Binding RelativeSource="RelativeSource Mode=FindAncestor, AncestorType=local:IFilterProvider" />
<!-- todo: how to enforce filter reevaluation without this hack -->
<Binding Path="HackToForceVisibilityUpdate" RelativeSource="RelativeSource Mode=FindAncestor, AncestorType=local:IFilterProvider" />
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="IsExpanded" Value="True" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="x:Type local:TvItemType2">
<TextBlock Text="Binding Name2" />
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<ToggleButton Content="A" IsChecked="Binding ShowA, Mode=TwoWay" Width="20" Height="20" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TreeView x:Name="tv"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.CanContentScroll="True"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard" Margin="0,24,0,0" />
<!-- todo: when using VirtualizingStackPanel.VirtualizationMode="Recycling", a lot of
System.Windows.Data Error: 40 : BindingExpression path error: 'Entries' property not found on 'object' ''TvItemType2' (HashCode=...)'. BindingExpression:Path=Entries; DataItem='TvItemType2' (HashCode=...); target element is 'TreeViewItem' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
are flooding the output window.
See f.e. https://stackoverflow.com/questions/4950208/bindingexpression-path-error-using-itemscontrol-and-virtualizingstackpanel -->
</Grid>
Code for the second approach:
The data structures:
public class TvItemBase
public class TvItemType1 : TvItemBase
public string Name1 get; set;
public List<TvItemBase> Entries get; = new List<TvItemBase>();
public ICollectionView FilteredEntries
get
var dv = CollectionViewSource.GetDefaultView(Entries);
dv.Filter = MainWindow.singleton.filterFunc; // todo:hack
return dv;
public class TvItemType2 : TvItemBase
public string Name2 get; set;
public int i get; set;
and the window impl, which is also the view model, looks like this
public interface IFilterProvider
bool ShowA get;
public partial class MainWindow : Window, INotifyPropertyChanged, IFilterProvider
public event PropertyChangedEventHandler PropertyChanged;
void NotifyChanged(string name) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
bool ShowA_ = true;
public bool ShowA
get return ShowA_;
set
ShowA_ = value;
// todo:hack
// todo:why does this update the whole tree
(tv.ItemsSource as ICollectionView).Refresh();
NotifyChanged(nameof(ShowA));
void generateTestItems(TvItemType1 parent, int nof1, int nof2, int levels)
for (int i = 0; i < nof1; i++)
var i1 = new TvItemType1 Name1 = string.Format("F_0.1.2.3", levels, i, nof1, nof2) ;
parent.Entries.Add(i1);
if (levels > 0)
generateTestItems(i1, nof1, nof2, levels - 1);
for (int i = 0; i < nof2; i++)
parent.Entries.Add(new TvItemType2 Name2 = string.Format("0.1.2.3", levels, nof1 + i, nof1, nof2), i = nof1 + i );
public bool filterFunc(object obj)
var entry = obj as TvItemType2;
if (entry == null)
return true;
var model = this;
if (!model.ShowA)
return false;
else if (entry.i % 2 == 0)
return false;
else
return true;
public MainWindow()
InitializeComponent();
DataContext = this;
singleton = this; // todo:hack
var i1 = new TvItemType1 Name1 = "root" ;
generateTestItems(i1, 10, 1000, 3);
//generateTestItems(i1, 3, 10, 3);
var l = new List<TvItemBase> i1 ;
var dv = CollectionViewSource.GetDefaultView(l);
dv.Filter = filterFunc;
tv.ItemsSource = dv;
public static MainWindow singleton = null; // todo:[really big]hack
Finally, here's the xaml:
<Window.Resources>
<HierarchicalDataTemplate DataType="x:Type local:TvItemType1" ItemsSource="Binding FilteredEntries">
<TextBlock Text="Binding Name1" />
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="x:Type local:TvItemType2">
<TextBlock Text="Binding Name2" />
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<ToggleButton Content="A" IsChecked="Binding ShowA, Mode=TwoWay" Width="20" Height="20" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TreeView x:Name="tv"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.CanContentScroll="True"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard" Margin="0,24,0,0" />
</Grid>
c# wpf treeview
Have you given any though to having a UI design that doesn't require millions of items in a treeview simultaneously?
– Robert Harvey♦
Mar 7 at 20:07
@RobertHarvey The use case is a regression test with hundreds of our projects, generating thausands of files each, which will be automatically compared by parsing and listing the comparison results in a tree if there are major differences. The desired result are no or only minor differences and then there's no need to display all these files or even a tree at all, but when strange patterns arise, it's beneficial to look at the difference sets simultaneously. But also if it's just for academic reasons, i'd like to visualize huge containers really performant.
– wonko realtime
Mar 7 at 20:56
add a comment |
I've got a hierarchical data structure which i'm trying to visualize with a WPF TreeView and hierarchical data templates. The number of items can reach millions, so i decided to try the virtualization features which work easy and well, besides that one problem which arises when i use "Recycling" instead of "Standard" for the VirtualizationMode. A long time ago someone else seemed to have a similar problem:
"BindingExpression path error" using ItemsControl and VirtualizingStackPanel
Nevertheless, i have troubles to implement a working and performant filtering.
I tried two different approaches, based on hints i could find on the internet, like
- How to filter a TreeView in WPF?
- In WPF can you filter a CollectionViewSource without code behind?
- Filter WPF TreeView using MVVM
- How to filter a wpf treeview hierarchy using an ICollectionView?
- WPF: Filter TreeView without collapsing it's nodes
- Filtering Treeview
The first is to use a Converter to set TreeViewItem.Visibility based on a filter, the second is to create ICollectionView ad hoc for all elements and assign the same filter predicate to each one.
I like the first approach because it requires less code, seems to be more clear, and requires less hacks, and i feel like i had to use that one hack (HackToForceVisibilityUpdate) only because i don't know better, but it slows down the UI considerably and i don't know how to fix that.
The problem with the second approach is that it collapses all nodes when the filter is changed (which i think could be fixed by tracking the states before the filter change and restoring them afterwards) and that it involves a lot of additional code (for the sake of the example, a singleton hack is used to not blow the code up too much and adding/removing items won't work).
I feel like both approaches could be fixed but i cannot figure out how and i really would like to fix the performance issues of the first approach.
It's a lot of code to show in a post and if you don't like the code blocks below, here are the files as pastebin
- approach 1 - cs
- approach 1 - xaml
- approach 2 - cs
- approach 2 - xaml
and as an archive containing a VS solution and
- a project WpfVirtualizedTreeViewPerItemVisibility for the 1st and
- a project WpfVirtualizedTreeViewPerItemVisibility3 for the 2nd approach
which can be found here
The first approach:
The data structures:
public class TvItemBase
public class TvItemType1 : TvItemBase
public string Name1 get; set;
public List<TvItemBase> Entries get; = new List<TvItemBase>();
public class TvItemType2 : TvItemBase
public string Name2 get; set;
public int i get; set;
The converter looks like this (it corresponds to the "filterFunc" in the 2nd approach)
public class TvItemType2VisibleConverter : IMultiValueConverter
public TvItemType2VisibleConverter()
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
if (values.Length < 2)
return Visibility.Visible;
var tvItem = values[0] as TreeViewItem;
if (tvItem == null)
return Visibility.Visible;
var entry = tvItem.DataContext as TvItemType2;
if (entry == null)
return Visibility.Visible;
var model = values[1] as IFilterProvider;
if (model == null)
return Visibility.Visible;
if (!model.ShowA)
return Visibility.Collapsed;
else if (entry.i % 2 == 0)
return Visibility.Collapsed;
else
return Visibility.Visible;
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) throw new System.NotImplementedException();
and the window impl, which is also the view model, looks like this
public interface IFilterProvider
bool ShowA get;
public partial class MainWindow : Window, INotifyPropertyChanged, IFilterProvider
public event PropertyChangedEventHandler PropertyChanged;
void NotifyChanged(string name) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
bool ShowA_ = true;
public bool ShowA
get return ShowA_;
set ShowA_ = value; NotifyChanged(nameof(ShowA)); NotifyChanged(nameof(HackToForceVisibilityUpdate));
public bool HackToForceVisibilityUpdate get return true;
void generateTestItems(TvItemType1 parent, int nof1, int nof2, int levels)
for (int i = 0; i < nof1; i++)
var i1 = new TvItemType1 Name1 = string.Format("F_0.1.2.3", levels, i, nof1, nof2) ;
parent.Entries.Add(i1);
if (levels > 0)
generateTestItems(i1, nof1, nof2, levels - 1);
for (int i = 0; i < nof2; i++)
parent.Entries.Add(new TvItemType2 Name2 = string.Format("0.1.2.3", levels, nof1 + i, nof1, nof2), i = nof1 + i );
public MainWindow()
InitializeComponent();
DataContext = this;
var i1 = new TvItemType1 Name1 = "root" ;
generateTestItems(i1, 10, 1000, 3);
tv.ItemsSource = new List<TvItemBase> i1 ;
Finally, here's the xaml:
<Window.Resources>
<local:TvItemType2VisibleConverter x:Key="TvItemType2VisibleConverter"/>
<HierarchicalDataTemplate DataType="x:Type local:TvItemType1" ItemsSource="Binding Entries">
<TextBlock Text="Binding Name1" />
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="StaticResource TvItemType2VisibleConverter">
<Binding RelativeSource="RelativeSource Self" />
<!-- todo: how to specify the filter provider through a view model property (using Path and Source?) -->
<Binding RelativeSource="RelativeSource Mode=FindAncestor, AncestorType=local:IFilterProvider" />
<!-- todo: how to enforce filter reevaluation without this hack -->
<Binding Path="HackToForceVisibilityUpdate" RelativeSource="RelativeSource Mode=FindAncestor, AncestorType=local:IFilterProvider" />
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="IsExpanded" Value="True" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="x:Type local:TvItemType2">
<TextBlock Text="Binding Name2" />
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<ToggleButton Content="A" IsChecked="Binding ShowA, Mode=TwoWay" Width="20" Height="20" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TreeView x:Name="tv"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.CanContentScroll="True"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard" Margin="0,24,0,0" />
<!-- todo: when using VirtualizingStackPanel.VirtualizationMode="Recycling", a lot of
System.Windows.Data Error: 40 : BindingExpression path error: 'Entries' property not found on 'object' ''TvItemType2' (HashCode=...)'. BindingExpression:Path=Entries; DataItem='TvItemType2' (HashCode=...); target element is 'TreeViewItem' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
are flooding the output window.
See f.e. https://stackoverflow.com/questions/4950208/bindingexpression-path-error-using-itemscontrol-and-virtualizingstackpanel -->
</Grid>
Code for the second approach:
The data structures:
public class TvItemBase
public class TvItemType1 : TvItemBase
public string Name1 get; set;
public List<TvItemBase> Entries get; = new List<TvItemBase>();
public ICollectionView FilteredEntries
get
var dv = CollectionViewSource.GetDefaultView(Entries);
dv.Filter = MainWindow.singleton.filterFunc; // todo:hack
return dv;
public class TvItemType2 : TvItemBase
public string Name2 get; set;
public int i get; set;
and the window impl, which is also the view model, looks like this
public interface IFilterProvider
bool ShowA get;
public partial class MainWindow : Window, INotifyPropertyChanged, IFilterProvider
public event PropertyChangedEventHandler PropertyChanged;
void NotifyChanged(string name) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
bool ShowA_ = true;
public bool ShowA
get return ShowA_;
set
ShowA_ = value;
// todo:hack
// todo:why does this update the whole tree
(tv.ItemsSource as ICollectionView).Refresh();
NotifyChanged(nameof(ShowA));
void generateTestItems(TvItemType1 parent, int nof1, int nof2, int levels)
for (int i = 0; i < nof1; i++)
var i1 = new TvItemType1 Name1 = string.Format("F_0.1.2.3", levels, i, nof1, nof2) ;
parent.Entries.Add(i1);
if (levels > 0)
generateTestItems(i1, nof1, nof2, levels - 1);
for (int i = 0; i < nof2; i++)
parent.Entries.Add(new TvItemType2 Name2 = string.Format("0.1.2.3", levels, nof1 + i, nof1, nof2), i = nof1 + i );
public bool filterFunc(object obj)
var entry = obj as TvItemType2;
if (entry == null)
return true;
var model = this;
if (!model.ShowA)
return false;
else if (entry.i % 2 == 0)
return false;
else
return true;
public MainWindow()
InitializeComponent();
DataContext = this;
singleton = this; // todo:hack
var i1 = new TvItemType1 Name1 = "root" ;
generateTestItems(i1, 10, 1000, 3);
//generateTestItems(i1, 3, 10, 3);
var l = new List<TvItemBase> i1 ;
var dv = CollectionViewSource.GetDefaultView(l);
dv.Filter = filterFunc;
tv.ItemsSource = dv;
public static MainWindow singleton = null; // todo:[really big]hack
Finally, here's the xaml:
<Window.Resources>
<HierarchicalDataTemplate DataType="x:Type local:TvItemType1" ItemsSource="Binding FilteredEntries">
<TextBlock Text="Binding Name1" />
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="x:Type local:TvItemType2">
<TextBlock Text="Binding Name2" />
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<ToggleButton Content="A" IsChecked="Binding ShowA, Mode=TwoWay" Width="20" Height="20" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TreeView x:Name="tv"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.CanContentScroll="True"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard" Margin="0,24,0,0" />
</Grid>
c# wpf treeview
I've got a hierarchical data structure which i'm trying to visualize with a WPF TreeView and hierarchical data templates. The number of items can reach millions, so i decided to try the virtualization features which work easy and well, besides that one problem which arises when i use "Recycling" instead of "Standard" for the VirtualizationMode. A long time ago someone else seemed to have a similar problem:
"BindingExpression path error" using ItemsControl and VirtualizingStackPanel
Nevertheless, i have troubles to implement a working and performant filtering.
I tried two different approaches, based on hints i could find on the internet, like
- How to filter a TreeView in WPF?
- In WPF can you filter a CollectionViewSource without code behind?
- Filter WPF TreeView using MVVM
- How to filter a wpf treeview hierarchy using an ICollectionView?
- WPF: Filter TreeView without collapsing it's nodes
- Filtering Treeview
The first is to use a Converter to set TreeViewItem.Visibility based on a filter, the second is to create ICollectionView ad hoc for all elements and assign the same filter predicate to each one.
I like the first approach because it requires less code, seems to be more clear, and requires less hacks, and i feel like i had to use that one hack (HackToForceVisibilityUpdate) only because i don't know better, but it slows down the UI considerably and i don't know how to fix that.
The problem with the second approach is that it collapses all nodes when the filter is changed (which i think could be fixed by tracking the states before the filter change and restoring them afterwards) and that it involves a lot of additional code (for the sake of the example, a singleton hack is used to not blow the code up too much and adding/removing items won't work).
I feel like both approaches could be fixed but i cannot figure out how and i really would like to fix the performance issues of the first approach.
It's a lot of code to show in a post and if you don't like the code blocks below, here are the files as pastebin
- approach 1 - cs
- approach 1 - xaml
- approach 2 - cs
- approach 2 - xaml
and as an archive containing a VS solution and
- a project WpfVirtualizedTreeViewPerItemVisibility for the 1st and
- a project WpfVirtualizedTreeViewPerItemVisibility3 for the 2nd approach
which can be found here
The first approach:
The data structures:
public class TvItemBase
public class TvItemType1 : TvItemBase
public string Name1 get; set;
public List<TvItemBase> Entries get; = new List<TvItemBase>();
public class TvItemType2 : TvItemBase
public string Name2 get; set;
public int i get; set;
The converter looks like this (it corresponds to the "filterFunc" in the 2nd approach)
public class TvItemType2VisibleConverter : IMultiValueConverter
public TvItemType2VisibleConverter()
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
if (values.Length < 2)
return Visibility.Visible;
var tvItem = values[0] as TreeViewItem;
if (tvItem == null)
return Visibility.Visible;
var entry = tvItem.DataContext as TvItemType2;
if (entry == null)
return Visibility.Visible;
var model = values[1] as IFilterProvider;
if (model == null)
return Visibility.Visible;
if (!model.ShowA)
return Visibility.Collapsed;
else if (entry.i % 2 == 0)
return Visibility.Collapsed;
else
return Visibility.Visible;
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) throw new System.NotImplementedException();
and the window impl, which is also the view model, looks like this
public interface IFilterProvider
bool ShowA get;
public partial class MainWindow : Window, INotifyPropertyChanged, IFilterProvider
public event PropertyChangedEventHandler PropertyChanged;
void NotifyChanged(string name) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
bool ShowA_ = true;
public bool ShowA
get return ShowA_;
set ShowA_ = value; NotifyChanged(nameof(ShowA)); NotifyChanged(nameof(HackToForceVisibilityUpdate));
public bool HackToForceVisibilityUpdate get return true;
void generateTestItems(TvItemType1 parent, int nof1, int nof2, int levels)
for (int i = 0; i < nof1; i++)
var i1 = new TvItemType1 Name1 = string.Format("F_0.1.2.3", levels, i, nof1, nof2) ;
parent.Entries.Add(i1);
if (levels > 0)
generateTestItems(i1, nof1, nof2, levels - 1);
for (int i = 0; i < nof2; i++)
parent.Entries.Add(new TvItemType2 Name2 = string.Format("0.1.2.3", levels, nof1 + i, nof1, nof2), i = nof1 + i );
public MainWindow()
InitializeComponent();
DataContext = this;
var i1 = new TvItemType1 Name1 = "root" ;
generateTestItems(i1, 10, 1000, 3);
tv.ItemsSource = new List<TvItemBase> i1 ;
Finally, here's the xaml:
<Window.Resources>
<local:TvItemType2VisibleConverter x:Key="TvItemType2VisibleConverter"/>
<HierarchicalDataTemplate DataType="x:Type local:TvItemType1" ItemsSource="Binding Entries">
<TextBlock Text="Binding Name1" />
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="StaticResource TvItemType2VisibleConverter">
<Binding RelativeSource="RelativeSource Self" />
<!-- todo: how to specify the filter provider through a view model property (using Path and Source?) -->
<Binding RelativeSource="RelativeSource Mode=FindAncestor, AncestorType=local:IFilterProvider" />
<!-- todo: how to enforce filter reevaluation without this hack -->
<Binding Path="HackToForceVisibilityUpdate" RelativeSource="RelativeSource Mode=FindAncestor, AncestorType=local:IFilterProvider" />
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="IsExpanded" Value="True" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="x:Type local:TvItemType2">
<TextBlock Text="Binding Name2" />
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<ToggleButton Content="A" IsChecked="Binding ShowA, Mode=TwoWay" Width="20" Height="20" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TreeView x:Name="tv"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.CanContentScroll="True"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard" Margin="0,24,0,0" />
<!-- todo: when using VirtualizingStackPanel.VirtualizationMode="Recycling", a lot of
System.Windows.Data Error: 40 : BindingExpression path error: 'Entries' property not found on 'object' ''TvItemType2' (HashCode=...)'. BindingExpression:Path=Entries; DataItem='TvItemType2' (HashCode=...); target element is 'TreeViewItem' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
are flooding the output window.
See f.e. https://stackoverflow.com/questions/4950208/bindingexpression-path-error-using-itemscontrol-and-virtualizingstackpanel -->
</Grid>
Code for the second approach:
The data structures:
public class TvItemBase
public class TvItemType1 : TvItemBase
public string Name1 get; set;
public List<TvItemBase> Entries get; = new List<TvItemBase>();
public ICollectionView FilteredEntries
get
var dv = CollectionViewSource.GetDefaultView(Entries);
dv.Filter = MainWindow.singleton.filterFunc; // todo:hack
return dv;
public class TvItemType2 : TvItemBase
public string Name2 get; set;
public int i get; set;
and the window impl, which is also the view model, looks like this
public interface IFilterProvider
bool ShowA get;
public partial class MainWindow : Window, INotifyPropertyChanged, IFilterProvider
public event PropertyChangedEventHandler PropertyChanged;
void NotifyChanged(string name) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
bool ShowA_ = true;
public bool ShowA
get return ShowA_;
set
ShowA_ = value;
// todo:hack
// todo:why does this update the whole tree
(tv.ItemsSource as ICollectionView).Refresh();
NotifyChanged(nameof(ShowA));
void generateTestItems(TvItemType1 parent, int nof1, int nof2, int levels)
for (int i = 0; i < nof1; i++)
var i1 = new TvItemType1 Name1 = string.Format("F_0.1.2.3", levels, i, nof1, nof2) ;
parent.Entries.Add(i1);
if (levels > 0)
generateTestItems(i1, nof1, nof2, levels - 1);
for (int i = 0; i < nof2; i++)
parent.Entries.Add(new TvItemType2 Name2 = string.Format("0.1.2.3", levels, nof1 + i, nof1, nof2), i = nof1 + i );
public bool filterFunc(object obj)
var entry = obj as TvItemType2;
if (entry == null)
return true;
var model = this;
if (!model.ShowA)
return false;
else if (entry.i % 2 == 0)
return false;
else
return true;
public MainWindow()
InitializeComponent();
DataContext = this;
singleton = this; // todo:hack
var i1 = new TvItemType1 Name1 = "root" ;
generateTestItems(i1, 10, 1000, 3);
//generateTestItems(i1, 3, 10, 3);
var l = new List<TvItemBase> i1 ;
var dv = CollectionViewSource.GetDefaultView(l);
dv.Filter = filterFunc;
tv.ItemsSource = dv;
public static MainWindow singleton = null; // todo:[really big]hack
Finally, here's the xaml:
<Window.Resources>
<HierarchicalDataTemplate DataType="x:Type local:TvItemType1" ItemsSource="Binding FilteredEntries">
<TextBlock Text="Binding Name1" />
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="x:Type local:TvItemType2">
<TextBlock Text="Binding Name2" />
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<ToggleButton Content="A" IsChecked="Binding ShowA, Mode=TwoWay" Width="20" Height="20" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TreeView x:Name="tv"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.CanContentScroll="True"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard" Margin="0,24,0,0" />
</Grid>
c# wpf treeview
c# wpf treeview
asked Mar 7 at 17:54
wonko realtimewonko realtime
378619
378619
Have you given any though to having a UI design that doesn't require millions of items in a treeview simultaneously?
– Robert Harvey♦
Mar 7 at 20:07
@RobertHarvey The use case is a regression test with hundreds of our projects, generating thausands of files each, which will be automatically compared by parsing and listing the comparison results in a tree if there are major differences. The desired result are no or only minor differences and then there's no need to display all these files or even a tree at all, but when strange patterns arise, it's beneficial to look at the difference sets simultaneously. But also if it's just for academic reasons, i'd like to visualize huge containers really performant.
– wonko realtime
Mar 7 at 20:56
add a comment |
Have you given any though to having a UI design that doesn't require millions of items in a treeview simultaneously?
– Robert Harvey♦
Mar 7 at 20:07
@RobertHarvey The use case is a regression test with hundreds of our projects, generating thausands of files each, which will be automatically compared by parsing and listing the comparison results in a tree if there are major differences. The desired result are no or only minor differences and then there's no need to display all these files or even a tree at all, but when strange patterns arise, it's beneficial to look at the difference sets simultaneously. But also if it's just for academic reasons, i'd like to visualize huge containers really performant.
– wonko realtime
Mar 7 at 20:56
Have you given any though to having a UI design that doesn't require millions of items in a treeview simultaneously?
– Robert Harvey♦
Mar 7 at 20:07
Have you given any though to having a UI design that doesn't require millions of items in a treeview simultaneously?
– Robert Harvey♦
Mar 7 at 20:07
@RobertHarvey The use case is a regression test with hundreds of our projects, generating thausands of files each, which will be automatically compared by parsing and listing the comparison results in a tree if there are major differences. The desired result are no or only minor differences and then there's no need to display all these files or even a tree at all, but when strange patterns arise, it's beneficial to look at the difference sets simultaneously. But also if it's just for academic reasons, i'd like to visualize huge containers really performant.
– wonko realtime
Mar 7 at 20:56
@RobertHarvey The use case is a regression test with hundreds of our projects, generating thausands of files each, which will be automatically compared by parsing and listing the comparison results in a tree if there are major differences. The desired result are no or only minor differences and then there's no need to display all these files or even a tree at all, but when strange patterns arise, it's beneficial to look at the difference sets simultaneously. But also if it's just for academic reasons, i'd like to visualize huge containers really performant.
– wonko realtime
Mar 7 at 20:56
add a comment |
0
active
oldest
votes
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55050070%2fperformant-way-to-filter-wpf-virtualized-treeview%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55050070%2fperformant-way-to-filter-wpf-virtualized-treeview%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Have you given any though to having a UI design that doesn't require millions of items in a treeview simultaneously?
– Robert Harvey♦
Mar 7 at 20:07
@RobertHarvey The use case is a regression test with hundreds of our projects, generating thausands of files each, which will be automatically compared by parsing and listing the comparison results in a tree if there are major differences. The desired result are no or only minor differences and then there's no need to display all these files or even a tree at all, but when strange patterns arise, it's beneficial to look at the difference sets simultaneously. But also if it's just for academic reasons, i'd like to visualize huge containers really performant.
– wonko realtime
Mar 7 at 20:56