2012-12-14 14 views
5

Mam widok w mojej aplikacji z ogromnymi danymi tabelarycznymi do wyświetlenia. Dane są wyświetlane w dwóch zagnieżdżonych UniformGrids. UniformGrids są ItemPanels w ItemsControls i są powiązane z niektórymi ViewModels. Patrz poniższy obraz i jakiś przykład XAML-Code:Jak radzić sobie z wydajnością w wielkich UniformGrids?

view and viewmodel http://img593.imageshack.us/img593/8825/stackoverflowuniformgri.png

<!-- The green boxes --> 
<ItemsControl ItemsSource="{Binding BigCells}"> 

    <ItemsControl.ItemPanel> 
    <PanelTemplate> 
     <UniformGrid /> 
    </PanelTemplate> 
    </ItemsControl.ItemPanel> 

    <ItemsControl.ItemTemplate> 
    <DataTemplate> 

     <!-- The blue boxes --> 
     <ItemsControl ItemsSource="{Binding SmallCells}"> 
     <ItemsControl.ItemPanel> 
      <PanelTemplate> 
      <UniformGrid /> 
      </PanelTemplate> 
     </ItemsControl.ItemPanel> 
     </ItemsControl> 

    </DataTemplate> 
    </ItemsControl.ItemTemplate> 
</ItemsControl> 

Teraz chcę moim zdaniem być skalowalny, ale to nie działa dobrze w ogóle, ponieważ układ Każda małym pudełku jest obliczana.
To może przynajmniej dla rozmiaru skrzynek zrobić tylko raz, ponieważ jest równe dla wszystkich skrzynek.

Co to jest najlepsza praktyka do wyświetlania ogromnej ilości elementów sterujących w WPF/gdzie mogę rozpocząć optymalizację? Słowa kluczowe pomogłyby mi w dalszym odkrywaniu optymalizacji wydajności dla UniformGrids w WPF.

Odpowiedz

8

Podobny problem wystąpił w przypadku jednoczesnego korzystania z wielu produktów WPF DataGrid. To w końcu sprowadza się do dwóch głównych zagadnień:

  • słowniki zasobów
  • Wiązania

zasób słowników

Mogą to być poważne zatory jeśli używasz podstawowy WPF dysponuje, nie będąc ostrożnym.

W moim przypadku, dla jednego logicznego przewijania w ItemsControl który został zawierającego moje DataGrids, ja dostawałem coś jak 10 milionów zadzwonić do GetValue i GetValueWithoutLock na ResourceDictionary. Ponad 1 sekunda do przetworzenia.

Ta wielka liczba ResourceDictionary dostępów został spowodowany przez różnych źródeł:

  • pobieranie zasobów Dynamiczny: Jeśli masz ControlTemplates, a bardziej ogólnie zasobów umieszczonych w niektórych słownikach zasobów, a dostęp do nich poprzez {DynamicResource ...}, należy je usunąć . Mieć gdzieś statyczne miejsce i mieć do nich bezpośredni dostęp.
  • Pobieranie stylu: Jeśli nie masz stylu w używanym obrazie lub masz styl, ale nie ustawiono właściwości FrameworkElement.OverridesDefaultStyle, program WPF spróbuje znaleźć styl pasujący do formantu we wszystkich zasobach, co jest wynikiem w wielu dostępach do słowników zasobów. Aby tego uniknąć, należy zastąpić wszystkie szablony kontrolne kontrolek i ustawić Style={x:Null} dla każdej kontrolki (jeśli potrzebujesz stylu dla formantu, ustawić go w linii kontrolnej i dodać). DataTemplates: szablon danych niejawnych jest bardzo przydatny, ale daleko im do tego. Szukając aplikacji do zastosowania, WPF ponownie przejrzy twoje słowniki zasobów, aby znaleźć DataTemplate pasujące do typów ViewModels. Rozwiązaniem jest użycie selektora DataTemplate dla każdej kontrolki powiązanej z ViewModel i wdrożenie zoptymalizowanego sposobu pobrania poprawnego DataTemplate. (Osobiście posiadam pola statyczne dla mojej dataTemplate, którą pobieram tylko raz z resourceDictionary i zoptymalizowanego DataTemplateSelector, który zwraca je w razie potrzeby.)

Wiązania

Wiązania są bardzo przydatne, ale bardzo drogie, pamięć i wydajność mądry mądry. W bardzo rozsądnym środowisku - z punktu widzenia wydajności - nie można po prostu używać ich bez zachowania ostrożności. Na przykład, bindowanie może utworzyć do 3 WeakReferences dla każdego bindowanego obiektu, może używać odbicia, itp. W końcu skończyłem z usunięciem prawie całej mojej funkcji obsługi zdarzeń wiążącej i podłączonej w zdarzeniu PropertyChanged DataContext. Kiedy DataContext zmienia się w moich kontrolkach (masz zdarzenie DataContextChanged) testuję, czy mój DataContext obsługuje INotifyPropertyChanged, a jeśli tak jest, dołączam procedurę obsługi zdarzeń do zdarzenia PropertyChanged i aktualizuję właściwości w razie potrzeby. (Miałem tylko jeden sposób wiązania, dlatego wybrałem ten sposób robienia rzeczy, ale są inne rozwiązania).

Może wydawać się frustrujące, nie używając wiązań podczas korzystania z MVVM i WPF, ale w rzeczywistości nie było sposobu, aby zoptymalizować wiązania w moim przypadku.

Ostatnia rzecz: jeśli masz obiekty Freezable (np. Pędzle), nie wahaj się, jeśli możesz, pod Freeze.

Oto kilka porad, które mogą pomóc w zoptymalizowaniu kodu. Będziesz potrzebował profilera (zawsze korzystam z porady dotTrace, ponieważ jest to najlepszy profiler, jakiego kiedykolwiek używałem) do monitorowania tego, co się naprawdę dzieje i dostosowywania się do wyników.

Powodzenia!

+0

Dziękujemy za szczegółową odpowiedź. Zawiera już pewne punkty, które mogę poprawić w mojej aplikacji (freezables, domyślne nadpisanie stylu). Sprawdzę to i zgłoś później. –

+0

Jasne, daj mi znać, jak to działa! To było dobre ćwiczenie dla mnie, ponieważ muszę napisać post na moim blogu :) – Sisyphe

+0

Mamy wiele słowników zasobów dla wszystkiego i aplikacja jest nadal bardzo szybka.Sposób, w jaki do tego przystąpiliśmy, polegał na stworzeniu jednego słownika zasobów dla jednego zasobu (takiego jak styl, wektor, pędzel itd.), A następnie z każdego widoku odwołujemy się tylko do tych słowników zasobów, które są potrzebne. Problemem jest sama siatka, ponieważ nie wirtualizuje. – adminSoftDK

4

Jeśli masz pewność, że wszystkie pudełka mają taki sam rozmiar, zawsze możesz utworzyć własną klasę UniformGrid i zastąpić MeasureOverride, aby zmierzyć tylko jedno dziecko. Mogłem skończyć z niektórych dużych UniformGrids z wewnętrznymi kontrolami same wielkości w moim obecnym projekcie też, więc twój pomysł zaintrygował mnie i postanowiłem go wypróbować naprawdę szybkie:

użyłem UniformGridAlmost klasę Petzold jako inspiracji, ale podklasy z UniformGrid zamiast Panel, więc właściwości dependancji Wiersze i Kolumny nie muszą być duplikowane. UniformGrid nie wydaje się, aby upublicznić obliczoną liczbę wierszy i kolumn do jej klas pochodnych, więc pożyczyłem implementację od Silverlight's UniformGrid class i użyłem metody UpdateComputedValues.

MeasureOverride w mojej klasie wygląda to

protected override Size MeasureOverride(Size sizeAvailable) 
    { 
    if (InternalChildren.Count == 0) 
    { 
     return new Size(0, 0); 
    } 

    UpdateComputedValues(); 

    // Calculate a child size based on uniform rows and columns. 
    Size sizeChild = new Size(sizeAvailable.Width/ComputedColumns, 
           sizeAvailable.Height/ComputedRows); 

    // Assume children will measure to at least a comparable size. 
    UIElement child = InternalChildren[0]; 
    child.Measure(sizeChild); 

    double width = Math.Max(width, child.DesiredSize.Width); 
    double height = Math.Max(height, child.DesiredSize.Height); 
    return new Size(ComputedColumns * width, ComputedRows * height); 
    } 

Tak więc, oto kicker, gdy testowałem go na ItemsControl z 10000 TextBlocks, moja wersja UniformGrid był szybszy, ale tylko niewielka ilość (mniej niż 1% - potwierdziłem, że wezwanie do MeasureOverride jest około 95% szybsze). To prawdopodobnie nie jest pomocne, ale pomyślałem, że podzielę się swoim doświadczeniem. Twoja obserwacja, że ​​nie trzeba mierzyć każdego pudełka, aby ustalić jednolity układ w twoim przypadku, jest prawdą, ale pomiar nie jest tym, co go pogrąża. Jestem pewien, że to dlatego, że wszystkie te kontrolki muszą zostać narysowane, więc i tak będą one mierzone później. Najprawdopodobniej uzyskasz znacznie więcej z sugestii ResourceDictionary/Binding.

+0

Właśnie próbowałem tego UniformGridAlmost jak jest, aw moim przypadku jest około 3 razy szybciej niż standardowa jednolita siatka. Tak więc czas rysowania spadł z 20 sekund do około 7 sekund. – adminSoftDK

Powiązane problemy