c# - net - Desplazarse en WPF virtualizado TreeView es muy inestable
ejemplos treeview vb net (2)
Si la virtualización está habilitada en TreeView
con elementos que tienen varios tamaños, aparecen varios 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 lanza desde el código de la estructura.
Reproducir es simple: cree una nueva aplicación WPF, luego ponga 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).
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>
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:
connect.microsoft.com/VisualStudio/feedback/details/763639/…
Si puede reproducir los errores, vote por ellos.