Scrolling in virtualized WPF TreeView is very unstable
Asked Answered
R

5

34

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

Rigadoon answered 30/12, 2012 at 17:8 Comment(14)
Are you using .NET 4 or 4.5 ?Babiche
@Babiche .NET 4.5, Windows 7 (Aero theme), VS 2012Rigadoon
@Rigadoon Tested on same box (32bit) no exception, size of scrollbar always the same.Riha
No issue with .NET 4.5 for me too. However I remember having issues on .NET 4Babiche
@Christian Strange. What happens if you scroll using arrows of the scrollbar? If I click and hold, scroll all the way to the bottom, then all the way to the top, the same thing happens.Rigadoon
@Babiche Strange. What happens if you scroll using arrows of the scrollbar? If I click and hold, scroll all the way to the bottom, then all the way to the top, the same thing happens.Rigadoon
@Rigadoon always works good... using arrow , mouse scroll etc. etc..Riha
Related connect.microsoft.com/VisualStudio/feedback/details/763639/…Pergrim
works fine for me with .Net 4.0 Win7 64 bitLancet
It could be related to the performance of your GPU or CPU. Also, is it better (as a test) if you put in a fallback value on the height binding? What if you change the scrollbar to always be visible using ScrollViewer.VerticalScrollBarVisibility?Simon
I was able to reproduce the issue with .NET 4.0, Win7 64 bit. In order to reproduce it is necessary to follow the instructions to the letter: scroll down all the way using the mouse wheel, then up all the way using the mouse wheel. At some point, it throws. Scrolling any other way does not trigger the issue.Sensorium
I posted an answer earlier, but after some testing the issue returned, it seems it is not a consistent repro. I would think it is related to the bug in Connect.Sensorium
have you tried a fix height for the treeview, i remember having something like this and solving it with a fix height for vertical scrolling..Sholes
Reported this specific bug on Microsoft Connect: Scrolling in virtualized WPF TreeView is very unstableRigadoon
R
10

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.

Rigadoon answered 5/6, 2013 at 7:54 Comment(5)
Issue is closed by microsoft? Any update is this resolved in .Net4.5?Steverson
@RohitVats Microsoft doesn't seem to have already included the fix considering their comment, "The exact nature and date of the update is TBD."Rigadoon
This is now fixed in .NET 4.5.2.Zigmund
A note for future readers, as of 4.6.1 if the items you bind (mvvm style) are structs, then it will crash. I'm not sure if it's related to this issue or not (I had a similar stack trace though). Switching to class solved the problem for me.Mcleod
Is this really the answer of this question?Constance
B
3

Happy 10-year anniversary to this bug! It still exists in .NET 6.

What triggers it for me is a TreeView where each TreeViewItem has some custom-formatted text (via TextBlock inlines), which is wide enough to necessitate a horizontal scrollbar. If I drag the horizontal scrollbar thumb all the way to the right, then drag the vertical scrollbar thumb up or down, I hit this exception.

I would like to add my workaround to the pile, since none of the others worked for me. The exception is from some non-public method called AsNonNullVisual. I just catch that particular exception and ignore it:

Dispatcher.UnhandledException += ( s, e ) => {
    if( e.Exception.TargetSite?.Name == "AsNonNullVisual" )
        e.Handled = true;
};

The vertical scrollbar thumb jitters a bit when the exception is thrown, but otherwise there are no other visual effects and scrolling continues as usual.

Put this in App.xaml.cs, your main window constructor, or anywhere else where it'll run early and once only.

Bise answered 16/2, 2023 at 19:32 Comment(0)
E
2

As of .NET 5 this issue still exists in WPF, and Microsoft has retired Microsoft Connect so it's unclear if this is even on their radar anymore. I encountered the same issue and stumbled across a fix that worked for me purely by accident. Essentially it's just doing the same thing the TreeView should be doing, using a HierarchichalDataTemplate to render each node, but where the built-in TreeView crashes while scrolling, this version doesn't (on the tree of items in my case).

<DockPanel>
   <DockPanel.Resources>
      <HierarchicalDataTemplate DataType="{x:Type src:Item}" ItemsSource="{Binding Path=Children}">
         <TextBlock Text"{Binding}"/>
      </HierarchicalDataTemplate>
   </DockPanel.Resources>

   <TreeView x:Name="tvwItems" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" ItemsSource="{Binding Items}">
   </TreeView>
</DockPanel>
Ellie answered 19/7, 2021 at 19:48 Comment(0)
R
1

By default Virtualization Stack panel uses pixel rendering to render child elements and the Recycling mode will discard each elements inside the treeview container that is no longer needed in UI. This cause the scroll bar size to change automatically. The VirtualizationPanel Pixel rendering technique will leads to slow down the scrolling option also. By changing to VirtualizingPanel.ScrollUnit="Item" will solve your issues. Below xaml is working fine for me

<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"
              VirtualizingPanel.ScrollUnit="Item"
              >
        <TreeView.ItemTemplate>
            <DataTemplate>
                <Border Height="{Binding Height}"
                        Width="{Binding Height}"
                        BorderThickness="1"
                        Background="DarkGray"
                        BorderBrush="DarkBlue" />
            </DataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
</Grid>
</Window>
Ratchford answered 4/6, 2013 at 8:35 Comment(1)
TreeView virtualization is pointless in ScrollUnit=Item mode, because every branch is considered an item. It works with this example just because there are no sub-items. Before ScrollUnit=Pixel was introduced, virtualization in TreeView was completely impossible.Rigadoon
U
0

i have taken same error in wpf application while loading window. Visual Studio 2017 After some researching find out something like this post and i have noticed it is interesting WindowStyle element.

In my case the error in XAML design window in wpf and windows attribute value was

WindowStyle ="none"

i have changed it's value to WindowStyle ="SingleBorderWindow" and this errors has disappeared

Uncommonly answered 22/11, 2018 at 9:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.