2013-04-12 13 views
9

Dzisiaj postanowiłem w końcu spróbować zwirtualizowanego TreeView. Aby to zrobić wymagane jest. Zdecydowałem się więc na test 2 rzeczy - HierarchicalDataTemplate oparty na typach + wirtualizacji.Problem z przewijaniem w zwirtualizowanym TreeView

Utworzono klasę podstawową dla niektórych danych. Utworzono 2 pochodne klasy z klasy bazowej. Wykonano 2 HierarchicalDataTemplate (1 dla każdej klasy pochodnej), aby uzyskać różne formatowanie węzłów. I bieżąca populacja 10 tys. Węzłów w 2 typach.

Ćwiczenia:

public class ListItem_Generic 
{ 
    public string Name { get; protected set; } 
    public ListItem_Generic(string Name = "") { this.Name = Name; } 
} 

public class ListItem_Single : ListItem_Generic 
{ 
    public ListItem_Single(string Name = "") : base(Name) { } 
} 

public class ListItem_Multi : ListItem_Generic 
{ 
    public List<ListItem_Generic> Items { get; protected set; } 
    public ListItem_Multi(string Name = "", List<ListItem_Generic> Items = null) 
     : base(Name) 
    { 
     if (Items == null) 
      this.Items = new List<ListItem_Generic>(); 
     else 
      this.Items = new List<ListItem_Generic>(Items); 
    } 
} 

Generowanie 10k węzły 1st level z niektórych dzieci, Oprawa:

public MainWindow() 
    { 
     InitializeComponent(); 

     // Create a list of sample items and populate them 
     var lst = new List<ListItem_Generic>(); 

     int MaxHeaders = 10000; 
     var rnd = new Random(); 
     // Now generate 10 000 records. First select random amount of headers 
     int HeadersCount = rnd.Next(MaxHeaders); 

     for (int i = 0; i < HeadersCount; i++) 
     { 
      var Childrencount = rnd.Next(100); 
      var children = new List<ListItem_Generic>(); 
      for (int j = 0; j < Childrencount; j++) 
       children.Add(new ListItem_Single("Child #"+j+" of parent #"+i)); 
      lst.Add(new ListItem_Multi("Header #" + i + " (" + Childrencount + ")", children)); 
     } 
     for (int i = 0; i < MaxHeaders - HeadersCount; i++) 
      lst.Add(new ListItem_Single("Line #" + i)); 

     // Bind lstView to lst 
     lstView.ItemsSource = lst; 
     lstView.UpdateLayout(); 
    } 

XAML z HierarchicalDataTemplates:

<Window x:Class="Test_DataTemplates.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:loc="clr-namespace:Test_DataTemplates" 
     Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <TreeView Name="lstView" VirtualizingPanel.IsVirtualizing="True"> 
      <TreeView.Resources> 
       <HierarchicalDataTemplate DataType="{x:Type loc:ListItem_Multi}" ItemsSource="{Binding Path=Items}"> 
        <Border Background="RosyBrown"> 
         <TextBlock Text="{Binding Path=Name}" Foreground="White" FontWeight="Bold"/> 
        </Border> 
       </HierarchicalDataTemplate> 
       <HierarchicalDataTemplate DataType="{x:Type loc:ListItem_Single}"> 
        <TextBlock Text="{Binding Path=Name}"/> 
       </HierarchicalDataTemplate> 
      </TreeView.Resources> 
     </TreeView> 
    </Grid> 
</Window> 

Wszystko działa doskonale:

  • katalogów dostaje wirtualizacji (łatwo zauważalne zużycie pamięci + czas ładowania)
  • węzłów pochodzące z rodzajów są prawidłowo sformatowany

Jednak podczas przewijania do powiedzmy nagłówka # 1000 i rozwijanie go - pozycja przewijania przeskoczyłaby w inne miejsce, czyniąc rozwinięty węzeł i jego dzieci NIEWIDZIANE.

Co zrobiłem źle? Czy istnieje sposób, aby to naprawić?

Aktualizacja: Usunięcie wirtualizacji powoduje również usunięcie błędu przewijania.

+0

związane: http://stackoverflow.com/questions/4074475/scrolling-bug-in-wpf-virtualized- treeview –

+0

o tym samym problemie tutaj – Laie

+0

@ Jee-heonOh imho, TreeView nie jest zbyt odpowiedni do wirtualizacji. Radziłbym zamiast tego używać ListBox/ListView. Łatwo jest symulować widok drzewopodobny za pomocą szablonów i symulować rozwijanie/zwijanie poziomu z dodawaniem/usuwaniem elementów do/z prostej (obserwowalnej) listy. Istnieje wiele przykładów dostępnych TreeListView. Jeśli jesteś skłonny do korzystania z TreeView (pamiętaj, że może on wirtualizować tylko hierarchię pierwszego poziomu) - zobacz obejście na stronie microsoft, opublikowane przez jednego z autorów. Przesłania kilka zdarzeń z drzewa, aby wyświetlić go w razie potrzeby, co rozwiązuje problem przewijania. – user2274578

Odpowiedz

0

spróbuj wyłączyć (nie) Recykling

VirtualizingStackPanel.VirtualizationMode="Standard" 

Optymalizacja artykuł odnosi się do jej włączeniem

How to: Improve the Performance of a TreeView

+0

Zarówno tryb Standart, jak i Recycling dają ten sam błąd przy przewijaniu, Blam. – user2274578

+0

Strzelaj Zostawię to dopóki nie otrzymasz odpowiedzi tylko po to, aby dać znać ludziom, że to nie jest odpowiedź. – Paparazzi

0

będę wdzięczny jeśli some1 może po prostu przetestować w swoim środowisku i potwierdź problem. Również zabawy trochę znaleźć inny dziwne zachowanie:

  • tryb zmienia się na Standart
  • realizowany IsExpanded dwukierunkową wiążącą (nie wiem, czy to jest w ogóle wymagane)

Następny bieg program:

  • Rozwiń nagłówek z jakiejś wielkiej liczby, jak 1000 - scroller przeskakuje do innego miejsca

ponownie uruchomić program (czystości eksperymentu)

  • rozwinąć jeden z nagłówków pierwszego, jak # 2.
  • Rozwiń nagłówek z jakiejś wielkiej liczby, jak 1000 - scroller pozostaje na właściwym miejscu ...

Rozszerzanie jednym z pierwszych węzłów wyglądać będzie obejście.

1

Po wielu problemach z wirtualizacją drzewa w C# WPF (w tym poważny problem, że tylko pierwszy poziom zostaje zwirtualizowany) - nie udało mi się znaleźć poprawnej poprawki. Microsoft zaakceptował zgłoszenie błędu i odpowiedział, że przewijany problem zostanie rozwiązany w jednej z przyszłych wersji.

Co do ostatecznego rozwiązania tego dla mnie osobiście - przełączyłem się na własną implementację ListTreeView, tj. Za pomocą listy i symulowania drzewa. Rozwiązało to wszystkie problemy związane z wirtualizacją i zachowaniem przewijania. Jedynym problemem było - usunięcie wielu elementów po zwinięciu węzła drzewa. Musiałem zaimplementować sprawdzanie, czy łatwiej jest/ponownie odtworzyć świeżą listę zamiast usuwać pozycje 1 przez 1.

0

Znalazłem rozwiązanie na MSDN. Najwyraźniej ma to coś wspólnego z domyślnym szablonem TreeView. Poniższe poprawki powodują migotanie paska przewijania i zatrzymują losowe węzły podczas ich szybkiego przewijania.

<Style x:Key="{x:Type TreeView}" TargetType="{x:Type TreeView}"> 
    <Setter Property="TreeView.Background" Value="Transparent"/> 
    <Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"/> 
    <Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling"/> 
    <Setter Property="TreeView.SnapsToDevicePixels" Value="True" /> 
    <Setter Property="TreeView.OverridesDefaultStyle" Value="True" /> 
    <Setter Property="ItemsControl.ItemsPanel"> 
     <Setter.Value> 
      <ItemsPanelTemplate> 
       <VirtualizingStackPanel IsItemsHost="True"/> 
      </ItemsPanelTemplate> 
     </Setter.Value> 
    </Setter> 
    <Setter Property="TreeView.Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="TreeView"> 
       <ScrollViewer Focusable="False" CanContentScroll="True" Padding="4"> 
        <ItemsPresenter HorizontalAlignment="Stretch"/> 
       </ScrollViewer> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

Próbowałem szybko przewijać i wydawało się, że już nie zauważyłem problemu. Niesamowitą rzeczą jest to, że nawet nie musisz rezygnować z wirtualizacji.

+0

Czy możesz dodać link do miejsca, w którym został znaleziony w witrynie MSDN? Byłbym szczególnie zainteresowany specyfiką miejsca, w którym domyślny szablon był nieprawidłowy. –

Powiązane problemy