Pregunta Desplazarse en WPF virtualizado TreeView es muy inestable


Si la virtualización está habilitada en TreeView con artículos que tienen varios tamaños, aparecen múltiples problemas:

  • La barra de desplazamiento vertical cambia su tamaño al azar y no recuerda el tamaño de los elementos después de ver todo el árbol. Desplazarse con el mouse es difícil.

  • Después de desplazarse hacia arriba y hacia abajo, ArgumentNullException se arroja desde el código de marco.

Reproducir es simple: crear una nueva aplicación WPF, luego poner este código en 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>

y este código en 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; }
    }
}

Cuando se ejecuta la aplicación, mueva el cursor del mouse hacia la vista de árbol, desplácese hacia abajo con la rueda del mouse, luego desplácese hacia arriba y luego comience a desplazarse hacia abajo nuevamente. En algún lugar en el medio, se lanza la siguiente excepción:

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

También puede ver que la excepción no es el único problema. Cuando se desplaza hacia arriba y hacia abajo, la barra de desplazamiento cambia constantemente su tamaño. (El mismo problema no aparece en ListBox que no puede predecir el tamaño, pero recuerda la altura total después de ver toda la lista).

Pregunta: ¿Cómo hacer que la barra de desplazamiento se comporte correctamente y deshacerse de la excepción? (No me importan los enlaces a controles TreeView alternativos o tal vez paneles de virtualización que admiten este escenario).


32
2017-12-30 17:08


origen


Respuestas:


Para que el enlace sea más prominente, también lo publico en una respuesta. Parece que hay un error dentro del código de la infraestructura y todavía no se han encontrado soluciones. He informado el error en Microsoft Connect:

Microsoft Connect: Desplazamiento en WPF virtualizado TreeView es muy inestable

También hay un error relacionado que se publicó en los comentarios de @sixlettervariables:

Microsoft Connect: la aplicación WPF se congela mientras se desplaza por TreeView bajo condiciones específicas

Si puede reproducir los errores, vote por ellos.


10
2018-06-05 07:54



De forma predeterminada, el panel Pila de virtualización utiliza la representación de píxeles para representar elementos secundarios y el modo de reciclaje descartará cada elemento dentro del contenedor de vista en árbol que ya no se necesita en la interfaz de usuario. Esto causa que el tamaño de la barra de desplazamiento cambie automáticamente. La técnica de renderizado VirtualizationPanel Pixel también ralentizará la opción de desplazamiento. Al cambiar a VirtualizingPanel.ScrollUnit = "Item" resolverá sus problemas. Debajo de xaml está funcionando bien para mí

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

1
2018-06-04 08:35