2012-12-30 11 views
30

Se Virtualizzazione è attivata in TreeView con gli oggetti che hanno varie dimensioni, molteplici problemi appaiono:scorrimento in virtualizzato WPF TreeView è molto instabile

  • barra di scorrimento verticale cambia la sua dimensione in modo casuale e non ricorda le dimensioni di elementi dopo guardando l'intero albero. Scorrere con il mouse è difficile.

  • Dopo un po 'di scorrimento su e giù, ArgumentNullException viene generato dal codice quadro.

Reproduciing è semplice: creare una nuova applicazione WPF, poi mettere il codice in 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> 

e questo codice in 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; } 
    } 
} 

Quando si esegue l'applicazione, spostare il cursore del mouse su una vista ad albero, scorrere verso il basso utilizzando la rotellina del mouse, quindi scorrere fino a in alto, quindi iniziare di nuovo a scorrere verso il basso. Da qualche parte nel mezzo viene lanciata la seguente eccezione:

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

Si può anche vedere che l'eccezione non è l'unico problema. Quando si scorre su e giù, la barra di scorrimento cambia costantemente le sue dimensioni. (Lo stesso problema non appare in ListBox che non può prevedere dimensione, ma ricorda altezza totale dopo aver visto l'intera lista.)

Domanda: Come rendere la barra di scorrimento si comportano correttamente e sbarazzarsi di eccezione ? (Non mi occupo di collegamenti a controlli TreeView alternativi o forse pannelli di virtualizzazione che supportano questo scenario.)

+0

Stai utilizzando .NET 4 o 4.5? – Sisyphe

+0

@Sisyphe .NET 4.5, Windows 7 (tema Aero), VS 2012 – Athari

+0

@Athari Testato sulla stessa casella (32 bit) nessuna eccezione, la dimensione della barra di scorrimento è sempre la stessa. –

risposta

10

Per rendere il collegamento più prominente, lo sto postando anche in una risposta. Sembra che un bug si trovi all'interno del codice framework e non ci sono ancora soluzioni alternative. Ho riportato il bug in Microsoft Connect:

Microsoft Connect: Scrolling in virtualized WPF TreeView is very unstable

C'è anche un bug forse correlato che è stato pubblicato nei commenti da @sixlettervariables:

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

Se è possibile riprodurre i bug , per favore votali.

+0

Il problema è stato chiuso da microsoft? Qualche aggiornamento è risolto in .Net4.5? –

+0

@RohitVats Microsoft non sembra aver già incluso la correzione considerando il loro commento, "La natura esatta e la data dell'aggiornamento sono TBD." – Athari

+3

Questo è ora risolto in .NET 4.5.2. – user704772

0

Per impostazione predefinita il riquadro Stack di virtualizzazione utilizza il rendering pixel per il rendering di elementi figlio e la modalità Riciclaggio eliminerà ogni elemento all'interno del contenitore di treeview che non è più necessario nell'interfaccia utente. Ciò causa la modifica della dimensione della barra di scorrimento automaticamente. La tecnica di rendering Pixel VirtualizationPanel porterà anche a rallentare l'opzione di scorrimento. Passando a VirtualizingPanel.ScrollUnit = "Item" risolverà i tuoi problemi. Sotto xaml sta funzionando bene per 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> 
+2

La virtualizzazione TreeView non ha senso in modalità 'ScrollUnit = Item', perché ogni * ramo * è considerato un articolo. Funziona con questo esempio solo perché non ci sono voci secondarie. Prima dell'introduzione di 'ScrollUnit = Pixel', la virtualizzazione in TreeView era completamente impossibile. – Athari