在虚拟化 WPF TreeView 中滚动非常不稳定

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14092691/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 06:49:51  来源:igfitidea点击:

Scrolling in virtualized WPF TreeView is very unstable

c#.netwpftreeviewvirtualizingstackpanel

提问by Athari

If virtualizing is enabled in TreeViewwith items having various sizes, multiple problems appear:

如果在TreeView具有各种大小的项目中启用虚拟化,则会出现多个问题:

  • 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, ArgumentNullExceptionis thrown from the framework code.

  • 垂直滚动条会随机改变其大小,并且在查看整个树后不会记住元素的大小。用鼠标滚动很难。

  • 上下滚动一些后,ArgumentNullException从框架代码中抛出。

Reproduciing is simple: create a new WPF application, then put this code into MainWindow.xaml

重现很简单:创建一个新的 WPF 应用程序,然后将此代码放入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

并将此代码写入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 ListBoxwhich can't predict size, but remembers total height after viewing the whole list.)

您还可以看到异常并不是唯一的问题。上下滚动时,滚动条的大小不断变化。(同样的问题不会出现在ListBox不能预测大小,但在查看整个列表后记住总高度。)

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

问题:如何使滚动条正常运行并摆脱异常?(我不介意链接到替代 TreeView 控件或支持这种情况的虚拟化面板。)

采纳答案by Athari

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 上报告了这个错误:

Microsoft Connect: Scrolling in virtualized WPF TreeView is very unstable

Microsoft Connect:在虚拟化 WPF TreeView 中滚动非常不稳定

There is also a maybe related bug which was posted in the comments by @sixlettervariables:

@sixlettervariables 在评论中也发布了一个可能相关的错误:

Microsoft Connect: WPF application freezes while scrolling the TreeView under specific conditions

Microsoft Connect:WPF 应用程序在特定条件下滚动 TreeView 时冻结

If you can reproduce the bugs, please vote them up.

如果您可以重现错误,请投票。

回答by Vimal CK

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

默认情况下,虚拟化堆栈面板使用像素渲染来渲染子元素,回收模式将丢弃 UI 中不再需要的树视图容器内的每个元素。这会导致滚动条大小自动更改。VirtualizationPanel 像素渲染技术也会导致滚动选项变慢。通过更改为 VirtualizingPanel.ScrollUnit="Item" 将解决您的问题。下面的 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"
              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>

回答by Hamit YILDIRIM

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.

我在加载窗口时在 wpf 应用程序中犯了同样的错误。Visual Studio 2017 经过一番研究,找到了类似这篇文章的内容,我注意到它是一个有趣的 WindowStyle 元素。

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

在我的情况下,wpf 和 windows 属性值中的 XAML 设计窗口中的错误是

WindowStyle ="none"

WindowStyle ="无"

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

我已将其值更改为WindowStyle ="SingleBorderWindow"并且此错误已消失