2010-07-26 15 views
9

Zawsze miałem długi czas ładowania z WPF DataGrids, i nie mogę znaleźć podobnych raportów w Internecie, więc podejrzewałem, że robię coś nie tak. Teraz jestem tego pewien, ponieważ dodanie złożoności układu znacznie spowalnia wykonywanie. W bardzo prostym układzie, DataGrid zapełnia się instantaenously, podczas gdy poniższy kod zajmuje około 3 sekund, aby wykonać.Nierozsądne WPF DataGrid Czas ładowania

W poniższym kodzie ładowanie 150 wierszy i 11 kolumn trwa około 3 sekund, nawet jeśli każda komórka nie jest powiązana z żadną właściwością i z wartością AutoGenerateColumns = False. (Mam dwurdzeniowy procesor 2,6 GHz z dużą ilością pamięci RAM).

szyjce butelki ma miejsce, gdy właściwość ItemsSource jest ustawiony w układzie jak ten poniżej:

<Window x:Class="datagridtest.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" Height="350" Width="525"> 
<Border Background="LightSteelBlue" CornerRadius="10" Margin="10"> 
    <ScrollViewer Margin="10" HorizontalScrollBarVisibility="Auto"> 
     <Grid Margin="10,50,0,0"> 
      <Grid.RowDefinitions> 
       <RowDefinition Height="Auto" /> 
       <RowDefinition Height="auto" /> 
       <RowDefinition Height="auto" /> 

      </Grid.RowDefinitions> 
      <Expander IsExpanded="True" Name="expander1" Grid.Row="0"> 
       <Grid> 
        <DataGrid VirtualizingStackPanel.IsVirtualizing="True" AutoGenerateColumns="false" Name="dg" Height="auto" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserResizeRows="False" CanUserSortColumns="False"> 
         <DataGrid.Columns> 
          <DataGridTextColumn > 
           <DataGridTextColumn.Header > 
            <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock> 
           </DataGridTextColumn.Header> 
          </DataGridTextColumn> 



          <DataGridTextColumn > 
           <DataGridTextColumn.Header > 
            <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock> 
           </DataGridTextColumn.Header> 
          </DataGridTextColumn> 

          <DataGridTextColumn > 
           <DataGridTextColumn.Header > 
            <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock> 
           </DataGridTextColumn.Header> 
          </DataGridTextColumn> 

          <DataGridTextColumn > 
           <DataGridTextColumn.Header > 
            <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock> 
           </DataGridTextColumn.Header> 
          </DataGridTextColumn> 

          <DataGridTextColumn > 
           <DataGridTextColumn.Header > 
            <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock> 
           </DataGridTextColumn.Header> 
          </DataGridTextColumn> 


          <DataGridTextColumn > 
           <DataGridTextColumn.Header > 
            <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock> 
           </DataGridTextColumn.Header> 
          </DataGridTextColumn> 

          <DataGridTextColumn > 
           <DataGridTextColumn.Header > 
            <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock> 
           </DataGridTextColumn.Header> 
          </DataGridTextColumn> 

          <DataGridTextColumn > 
           <DataGridTextColumn.Header > 
            <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock> 
           </DataGridTextColumn.Header> 
          </DataGridTextColumn> 

          <DataGridTextColumn > 
           <DataGridTextColumn.Header > 
            <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock> 
           </DataGridTextColumn.Header> 
          </DataGridTextColumn> 

          <DataGridTextColumn > 
           <DataGridTextColumn.Header > 
            <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock> 
           </DataGridTextColumn.Header> 
          </DataGridTextColumn> 

          <DataGridTextColumn > 
           <DataGridTextColumn.Header > 
            <TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock> 
           </DataGridTextColumn.Header> 
          </DataGridTextColumn> 



         </DataGrid.Columns> 
         </DataGrid> 
       </Grid> 
      </Expander> 

      <Expander IsExpanded="true" Grid.Row="1"> 
       <Grid> 
        <DataGrid AutoGenerateColumns="True" Height="auto" /> 
       </Grid> 
      </Expander> 

      <Expander IsExpanded="true" Grid.Row="2"> 
       <Grid> 
        <DataGrid AutoGenerateColumns="True" Height="auto" /> 
       </Grid> 
      </Expander> 
      <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="121,-42,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click_2" /> 
     </Grid> 
    </ScrollViewer> 
</Border> 

using System.Collections.ObjectModel; 

namespace datagridtest 
{ 
/// <summary> 
/// Interaction logic for MainWindow.xaml 
/// </summary> 
public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 


    } 

    class row 
    { 
     public string Name { get; set; } 
     public double Age { get; set; } 
    } 



    private void button1_Click_2(object sender, RoutedEventArgs e) 
    { 
     ObservableCollection<row> src = new ObservableCollection<row>(); 

     for (int i = 0; i < 150; i++) 
      src.Add(new row { Name = i.ToString(), Age = i/2 }); 

     dg.ItemsSource = src; 
    } 
} 
} 
+0

Czy możesz opublikować przykładowy kod, który to pokazuje? Nie sądzę, że WPF ma wbudowaną siatkę danych. –

+0

Dzięki Alan. Myślę, że używam DataGrid z Toolkit. Dodałem kod i nowe komentarze w pierwotnym pytaniu. –

+0

@Alan: Działa w wersji 4.0 architektury .Net: – Goblin

Odpowiedz

17

Problem występuje tylko wtedy, gdy DataGrid jest osadzony wewnątrz ScrollViewer jak:

<ScrollViewer> 
    <Datagrid/> 
</ScrollViewer> 

Ma to sens, ponieważ ta konfiguracja powoduje, że cały DataGrid być sporządzone w tym samym czasie (aby móc wielkości ScrollViewer użytkownika obszar klienta poprawnie). Zasadniczo, zastępuje on wbudowane zachowanie wirtualizacyjne obiektu DataGrid, które implementuje własne paski przewijania, dzięki czemu nie cała jego zawartość musi być jednocześnie umieszczona w układzie.

Innymi słowy, osadzanie DataGrid wewnątrz ScrollViewer jest rzadko potrzebne, ponieważ DataGrid ma własne automatyczne przewijanie.

+2

Co więcej, nigdy nie pozwól, aby DataGrid była autosize w dowolnym wymiarze. Jeśli planujesz umieszczać je w innych kontrolkach, dobrze jest ustawić jego właściwość MaxHeight na mniejszą niż rozmiar ekranu, ponieważ DataGrid nie wydaje się być bardzo wydajna, gdy jej układ jest duży. –

+0

+1 Lifesaver! Tymczasowo włączyłem ScrollViewer i zapomniałem o wszystkim. Byłem wstrząśnięty, gdy zobaczyłem indeksowanie sieci w aktualizacji. – Gishu

+0

niesamowite. dziękuję – jkl

3

można sprawdzić, czy wszystkie wiersze są generowane układ mądry? Zwykle wirtualizacja powinna to utrudniać i generować tylko widoczne wiersze. (Przetestuj go za pomocą szablonu w jednej z kolumn i policz w konstruktorze). Występuje błąd, jeśli WPF nie może określić prawidłowej szerokości DataGrid, gdy próbuje dokonać wielkości do największej kolumny - w ten sposób musi wygenerować wszystkie wiersze, aby obliczyć ten o największej szerokości. (Aby przetestować ostatni - umieść go w panelu dokowania zamiast siatki - zadokowany w lewo lub w prawo)

Spróbuj również użyć VirtualizingStackPanel.VirtualizationMode = "Recycling", aby umożliwić recykling używanych szablonów.

+0

Dzięki Goblin! Byłeś na dobrej drodze, ponieważ moja DataGrid próbowała wygenerować wszystkie wiersze w tym samym czasie. Ale było to spowodowane osadzeniem DataGrid wewnątrz ScrollView. Wirtualizacja nie była problemem (ponieważ rozmiar DataGrid nie był ograniczony do wymiarów ekranu). –

4

Miałem podobny problem z UserControl, który zawierał DataGrid, czasami kiedy umieściłem UserControl na nowym formularzu lub innym UserControl, zablokowałoby to interfejs (5 sekund?) Podczas redagowania DataGrid. To samo dotyczy zmiany rozmiaru.

Śledziłem go do

RowDefinition height = "Auto"

i tego samego problemu z wydajnością też stało, gdyby umieścić UserControl w StackPanel. Wydaje się, że ma to wiele wspólnego ze wspomnianym wcześniej błędem zmiany rozmiaru, gdy cały datagrid musi zostać wypełniony, aby obliczyć rozmiar kapsułkującego pojemnika.

<UserControl x:Class="ExampleUserControl" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      mc:Ignorable="d" d:DesignHeight="481" d:DesignWidth="773"> 

    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="*" /> <!-- 'AUTO' CAUSES EXTREMELY POOR PERFORMANCE --> 
     </Grid.RowDefinitions> 

     <Grid Grid.Row="0"> <!-- CHANGING TO STACKPANEL CAUSES EXTREMELY POOR PERFORMANCE --> 
      <ContentControl Content="{Binding MyDataGridUserControl}" /> 
     </Grid> 
    </Grid> 

</UserControl> 

Właśnie odkryłem, że ustawienie MaxHeight = "[cokolwiek]" dla ContentControl również działa, jak w poprzednim komentarzu. Może być większy niż ekran.

+1

Dzięki za wskazanie, że kiedy wysokość jest ustawiona na "Auto", pojawia się problem. W mojej odpowiedzi nie podkreśliłem tego wyraźnie. Nie powiedziałbym jednak, że to błąd, tylko kwestia projektowania. Gdy siatka danych obsługuje własne paski przewijania i "wirtualny" obszar klienta, możliwe są liczne optymalizacje, ale nie możemy oczekiwać, że wszystkie te optymalizacje będą działać, gdy obszar wirtualny jest obsługiwany przez kontrolę nadrzędną - czy możemy? W każdym razie, obecnie sieć danych wydaje się być zaprojektowana do bardzo specyficznego zastosowania :(To jest do bani, ponieważ powoduje, że mamy paski przewijania w pasach przewijania w niektórych interfejsach. –

+0

Ten rodzaj przynosi ograniczenie do WPF, którego byśmy się nie spodziewali –

+0

Dziękuję za tę odpowiedź - to był rzeczywiście problem, który miałem .. Po usunięciu wysokości = "Auto" dla rzędu siatki danych miałem ogromny wzrost wydajności. –

0

Mam ten sam problem ze związaną siatką danych i zauważam, że przy pierwszym załadowaniu jest szybki, ale na drugim, a następnie wolny.kiedy więc dodać w kodzie:

DataGrid.ItemsSource = Nothing 

a następnie

TableAdapter.Fill(Mydataset.MyStoredProcedure,....) 
DataGrid.ItemsSource=Mydataset.MyStoredProcedure 

stało się bardzo szybko.