2012-12-30 89 views
30

如果虛擬化在TreeView啓用了具有不同尺寸的物品,許多問題出現:滾動虛擬化WPF的TreeView很不穩定

  • 垂直滾動條隨機改變它的大小和後不記得元件的尺寸查看整棵樹。用鼠標滾動很困難。

  • 經過一番上下滾動,ArgumentNullException從框架代碼拋出。

Reproduciing很簡單:創建一個新的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> 

這段代碼添加到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; } 
    } 
} 

當應用程序正在運行,移動鼠標光標成樹狀,滾動到底部使用鼠標滾輪,然後滾動到頂部,然後再次開始向下滾動。中間某處引發以下例外情況:

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

您還可以看到該異常不是唯一的問題。當上下滾動,滾動條,不斷改變其大小。 (同樣的問題不會出現在ListBox無法預測的大小,而是看整個名單後記得總高度)。

問題:如何使滾動條舉止得體,並擺脫了異常? (我不介意鏈接到可選的TreeView控件,或者虛擬化支持此場景的面板。)

+0

您使用的是.NET 4還是4.5? – Sisyphe

+0

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

+0

@Athari在同一個盒子(32位)上測試也不例外,滾動條的大小總是相同。 –

回答

10

,使鏈接更加突出,我在回答中張貼了。它看起來像一個錯誤是在框架代碼中,並沒有找到解決方法。我已報告的錯誤的微軟連接:

Microsoft Connect: Scrolling in virtualized WPF TreeView is very unstable

也有可能與錯誤,這是由@sixlettervariables張貼在評論:

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

如果你能重現錯誤,請將它們投票。

+0

問題由微軟關閉?任何更新都是在.Net4.5中解決的。 –

+0

@RohitVats考慮到他們的評論,微軟似乎沒有包含該修復,「更新的確切性質和日期是待定。」 – Athari

+3

這現在已經在.NET 4.5.2中修復了。 – user704772

0

默認情況下,虛擬化堆棧面板使用像素呈現來呈現子元素,而回收模式將丟棄treeview容器內的每個元素在UI中不再需要。這會導致滾動條大小自動更改。該VirtualizationPanel像素渲染技術將導致也減慢滾動選項。通過更改爲VirtualizingPanel.ScrollUnit =「項目」,將解決你的問題。下面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> 
+2

TreeView的虛擬化是在'ScrollUnit = Item'模式沒有意義的,因爲每個分支* *被視爲一個項目。它僅適用於這個例子,因爲沒有子項目。在引入'ScrollUnit = Pixel'之前,TreeView中的虛擬化是完全不可能的。 – Athari