Scrolling in virtualized WPF TreeView is very unstable
.net, c#, treeview, virtualizingstackpanel, wpf
Solution
To make the link more prominent, I am posting it in an answer too. It looks like a bug is within the framework code and there are no workarounds found yet. I have reported the bug on Microsoft Connect:
Microsoft Connect: Scrolling in virtualized WPF TreeView is very unstable
There is also a maybe related bug which was posted in the comments by @sixlettervariables:
Microsoft Connect: WPF application freezes while scrolling the TreeView under specific conditions
If you can reproduce the bugs, please vote them up.
Problem
If virtualizing is enabled in `TreeView` with items having various sizes, multiple problems appear: Vertical scroll bar changes its size randomly and doesn't remember sizes of elements after viewing the whole tree. Scrolling with mouse is hard. After some scrolling up and down, `ArgumentNullException` is thrown from the framework code. Reproduciing is simple: create a new WPF application, then put this code into MainWindow.xaml ``` <Window x:Class="VirtualTreeView.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="800" Width="400" Left="0" Top="0" DataContext="{Binding RelativeSource={RelativeSource Self}}"> <Grid> <TreeView x:Name="tvwItems" ItemsSource="{Binding Items}" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling"> <TreeView.ItemTemplate> <DataTemplate> <Border Height="{Binding Height}" Width="{Binding Height}" BorderThickness="1" Background="DarkGray" BorderBrush="DarkBlue"/> </DataTemplate> </TreeView.ItemTemplate> </TreeView> </Grid> </Window> ``` and this code into MainWindow.xaml.cs ``` using System.Collections.ObjectModel; using System.Linq; namespace VirtualTreeView { public partial class MainWindow { public ObservableCollection<Item> Items { get; set; } public MainWindow () { Items = new ObservableCollection<Item>(Enumerable.Range(0, 20).Select(i => new Item { Height = i*20, })); InitializeComponent(); } } public class Item { public double Height { get; set; } } } ``` When application is ran, move mouse cursor into a treeview, scroll to the bottom using mouse wheel, then scroll to the top, then start scrolling down again. Somewhere in the middle the following exception is thrown: ``` System.ArgumentNullException was unhandled HResult=-2147467261 Message=Value cannot be null. Parameter name: element Source=PresentationCore ParamName=element StackTrace: at MS.Internal.Media.VisualTreeUtils.AsNonNullVisual(DependencyObject element, Visual& visual, Visual3D& visual3D) at System.Windows.Media.VisualTreeHelper.GetParent(DependencyObject reference) at System.Windows.Controls.VirtualizingStackPanel.FindScrollOffset(Visual v) at System.Windows.Controls.VirtualizingStackPanel.OnAnchorOperation(Boolean isAnchorOperationPending) at System.Windows.Controls.VirtualizingStackPanel.OnAnchorOperation() at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler) at System.Windows.Threading.DispatcherOperation.InvokeImpl() at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Windows.Threading.DispatcherOperation.Invoke() at System.Windows.Threading.Dispatcher.ProcessQueue() at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler) at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs) at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg) at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame) at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame) at System.Windows.Threading.Dispatcher.Run() at System.Windows.Application.RunDispatcher(Object ignore) at System.Windows.Application.RunInternal(Window window) at System.Windows.Application.Run(Window window) at System.Windows.Application.Run() at VirtualTreeView.App.Main() in d:\Docs\Projects\_Try\VirtualTreeView\obj\Debug\App.g.cs:line 0 at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() ``` You can also see that exception is not the only problem. When scrolling up and down, scroll bar constantly changes its size. (The same problem doesn't appear in `ListBox` which can't predict size, but remembers total height after viewing the whole list.) Question: How to make the scroll bar behave properly and get rid of the exception? (I don't mind links to alternative TreeView controls or maybe virtualizing panels which support this scenario.)