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










1















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>









share|improve this question






















  • 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















1















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>









share|improve this question






















  • 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













1












1








1


1






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>









share|improve this question














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






share|improve this question













share|improve this question











share|improve this question




share|improve this question










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

















  • 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












0






active

oldest

votes












Your Answer






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
);



);













draft saved

draft discarded


















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















draft saved

draft discarded
















































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.




draft saved


draft discarded














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





















































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







Popular posts from this blog

1928 у кіно

Захаров Федір Захарович

Ель Греко