2010-07-26 9 views
9

Ho sempre avuto tempi di caricamento lunghi con WPF DataGrid e non riesco a trovare rapporti simili online, quindi sospettavo che stavo facendo qualcosa di sbagliato. Ora ne sono sicuro, poiché l'aggiunta della complessità del layout rallenta notevolmente l'esecuzione. In un layout molto semplice, il DataGrid si popola instaniano, mentre il codice sotto richiede circa 3 secondi per essere eseguito.DataGrid irragionevole DataGrid Tempo di caricamento

Nel seguente codice, occorrono ~ 3 secondi per caricare 150 righe e 11 colonne, anche se ogni cella non è associata a nessuna proprietà e AutoGenerateColumns = False. (Ho un processore a 2 core, 2,6 GHz con abbondanza di RAM).

Il collo della bottiglia avviene quando la proprietà ItemsSource si trova in un layout come quello qui sotto:

<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

Potresti postare qualche esempio di codice che mostri questo? Non penso che WPF abbia una griglia di dati incorporata. –

+0

Grazie Alan. Credo che sto usando il DataGrid dal Toolkit. Ho aggiunto codice e nuovi commenti nella domanda originale. –

+0

@Alan: Funziona nella versione 4.0 del framework .Net – Goblin

risposta

17

Il problema si verifica quando il DataGrid viene incorporato all'interno di uno ScrollViewer come:

<ScrollViewer> 
    <Datagrid/> 
</ScrollViewer> 

Ciò ha senso perché questa configurazione fa sì che l'intera DataGrid da trarre contemporaneamente (per poter dimensione ScrollViewer del area client correttamente). In sostanza, sostituisce il comportamento di virtualizzazione incorporato di DataGrid, che implementa le proprie barre di scorrimento in modo che non tutto il contenuto debba essere inserito nel layout contemporaneamente.

In altre parole, l'incorporamento di un DataGrid in un ScrollViewer è raramente necessario perché DataGrid ha il proprio scorrimento automatico.

+2

Inoltre, non lasciare mai un DataGrid autosize in nessuna dimensione. Se hai intenzione di impilarli in altri controlli, è consigliabile impostare la proprietà MaxHeight su qualcosa di più piccolo delle dimensioni dello schermo, poiché DataGrid non sembra essere molto efficiente quando il suo layout è ampio. –

+0

+1 Lifesaver! Ho temporaneamente inserito un ScrollViewer e ne ho dimenticato tutto. Sono rimasto scioccato quando ho visto la scansione della griglia su un aggiornamento. – Gishu

+0

fantastico. grazie – jkl

3

Riesci a vedere se tutte le righe vengono generati Layout-saggio? Normalmente la virtualizzazione dovrebbe ostacolarlo e generare solo le righe visibili. (Provalo con un modello in una delle colonne e contalo nel costruttore). C'è un bug se WPF non è in grado di determinare la larghezza corretta di DataGrid mentre tenta di ridimensionare la colonna più grande, quindi deve generare tutte le righe per calcolare quella con la larghezza maggiore. (Per testare l'ultimo - posizionarlo in un pannello di espansione invece di una griglia - ancorato a sinistra oa destra)

Inoltre, provare VirtualizingStackPanel.VirtualizationMode = "Riciclaggio" per consentire di riciclare i modelli utilizzati.

+0

Grazie Goblin! Tu eri sulla buona strada in quanto il mio DataGrid stava provando a generare tutte le righe allo stesso tempo. Ma questo era dovuto all'incorporazione di DataGrid all'interno di una ScrollView. La virtualizzazione non era il problema (perché la dimensione del DataGrid non era vincolata alle dimensioni dello schermo). –

4

Avevo un problema simile con un UserControl che conteneva un DataGrid, a volte quando ho inserito UserControl in un nuovo modulo o in un altro UserControl avrebbe bloccato l'interfaccia (5 secondi?) Mentre si ridimensionava il DataGrid. Lo stesso con il ridimensionamento.

ho attribuito la cosa a

RowDefinition height = "Auto"

e lo stesso problema di prestazioni è successo anche se ho messo l'UserControl in uno StackPanel. Sembra avere molto a che fare con il bug di ridimensionamento precedentemente menzionato quando l'intero datagrid deve essere popolato per calcolare la dimensione del contenitore incapsulante.

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

Ho appena scoperto che l'impostazione MaxHeight = "[qualunque cosa]" per il ContentControl funziona anche, come da un commento precedente. Può essere più grande dello schermo.

+1

Grazie per aver sottolineato il fatto che quando Altezza è impostato su "Auto" il problema sorge. Nella mia risposta non l'ho sottolineato esplicitamente. Tuttavia, non direi che questo è un bug, solo un problema di design. Quando una griglia di dati gestisce le proprie barre di scorrimento e l'area client "virtuale", sono possibili molte ottimizzazioni, ma non possiamo aspettarci che tutte queste ottimizzazioni siano effettive quando l'area virtuale è gestita dal controllo genitore - o possiamo? Ad ogni modo, al momento, la griglia di dati sembra essere progettata per un uso molto specifico :(Fa schifo perché ci fa avere barre di scorrimento all'interno di barre di scorrimento in alcune UI. –

+0

Questo tipo di porta una limitazione a WPF che non ci aspetteremmo essere –

+0

Grazie per questa risposta - era davvero il problema che stavo avendo.Dopo aver rimosso l'altezza = "Auto" per la riga della griglia di dati ho avuto un enorme aumento delle prestazioni. –

0

Ho lo stesso problema con la griglia Data rilegata e noto che al primo caricamento è veloce ma al secondo e successivo è lento.Così, quando aggiungo in codice:

DataGrid.ItemsSource = Nothing 

e poi

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

è diventato molto veloce.