2010-07-26 7 views
9

Siempre he tenido largos tiempos de carga con WPF DataGrids, y no puedo encontrar ningún informe similar en línea, por lo que sospeché que estaba haciendo algo mal. Ahora estoy seguro de ello, ya que agregar la complejidad del diseño ralentiza considerablemente la ejecución. En un diseño muy simple, DataGrid se completa de forma instantánea, mientras que el código siguiente tarda alrededor de 3 segundos en ejecutarse.WPF DataGrid irrazonable Loading Time

En el siguiente código, se tardan ~ 3 segundos en cargar 150 filas y 11 columnas, incluso si cada celda no está vinculada a ninguna propiedad y con AutoGenerateColumns = False. (Tengo un procesador de dos núcleos y 2.6GHz con mucha RAM).

El cuello de la botella tiene lugar cuando la propiedad ItemsSource se establece en una disposición como la siguiente:

<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

¿Podría publicar algún código de muestra que muestre esto? No creo que WPF tenga una cuadrícula de datos integrada. –

+0

Gracias Alan. Supongo que estoy usando DataGrid del Toolkit. Agregué código y nuevos comentarios en la pregunta original. –

+0

@ Alan: lo hace en la versión 4.0 del .Net framework – Goblin

Respuesta

17

El problema sólo se produce cuando la cuadrícula de datos está incrustado dentro de un ScrollViewer como:

<ScrollViewer> 
    <Datagrid/> 
</ScrollViewer> 

Esto tiene sentido ya que esta configuración hace que todo el DataGrid que se puede extraer al mismo tiempo (para ser capaz de tamaño la ScrollViewer de área del cliente correctamente). En esencia, anula el comportamiento de virtualización incorporado de DataGrid, que implementa sus propios ScrollBars para que no todo su contenido se coloque en el diseño simultáneamente.

En otras palabras, la incrustación de un DataGrid dentro de un ScrollViewer rara vez se necesita porque el DataGrid tiene su propio desplazamiento automático.

+2

Además, nunca permita que un DataGrid autosize en ninguna dimensión. Si planea apilarlos dentro de otros controles, es una buena idea establecer su propiedad MaxHeight en algo más pequeño que el tamaño de su pantalla, ya que DataGrid no parece ser muy eficiente cuando su diseño es grande. –

+0

+1 Lifesaver! Puse temporalmente un ScrollViewer y lo olvidé por completo ... me sorprendió ver una actualización en la cuadrícula. – Gishu

+0

impresionante. gracias – jkl

3

¿Puede usted ver si todas las filas están siendo generados diseño-sabio? Normalmente la virtualización debería obstaculizar eso y solo generar las filas visibles. (Pruébelo con una plantilla en una de las columnas y cuente en el constructor). Existe un error si WPF no puede determinar el ancho correcto de DataGrid cuando intenta dimensionar la columna más grande, por lo que debe generar todas las filas para calcular la que tiene el ancho más grande. (Para probar el último, colóquelo en un panel de base en lugar de en una cuadrícula).

Además, intente VirtualizingStackPanel.VirtualizationMode = "Reciclar" para que pueda reciclar las plantillas utilizadas.

+0

¡Gracias Duende! Estabas en el camino correcto porque mi DataGrid intentaba generar todas las filas al mismo tiempo. Pero esto se debió a la integración de DataGrid dentro de ScrollView. La virtualización no era el problema (porque el tamaño de DataGrid no estaba restringido a las dimensiones de la pantalla). –

4

Tuve un problema similar con un UserControl que contenía un DataGrid, a veces cuando colocaba el UserControl en un nuevo formulario u otro UserControl bloqueaba la interfaz (¿5 segundos?) Mientras redibujaba el DataGrid. Lo mismo con el cambio de tamaño.

que lo rastreó a

RowDefinition Altura = "Auto"

y el mismo problema de rendimiento también sucedió si pongo el control de usuario en un StackPanel. Parece tener mucho que ver con el error de cambio de tamaño mencionado anteriormente cuando toda la cuadrícula de datos necesita ser popluada para calcular el tamaño del contenedor de encapsulado.

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

acabo de descubrir que la fijación de MaxHeight = "[lo]" para el ContentControl también funciona, de acuerdo con un comentario anterior. Puede ser más grande que la pantalla.

+1

Gracias por señalar el hecho de que cuando Altura se establece en "Auto", surge el problema. En mi respuesta, no lo enfaticé explícitamente. Sin embargo, no diría que esto es un error, solo un problema de diseño. Cuando una cuadrícula de datos maneja sus propias barras de desplazamiento y el área de cliente "virtual", son posibles muchas optimizaciones, pero no podemos esperar que todas estas optimizaciones tengan efecto cuando el control principal maneja el área virtual, ¿o no? De todos modos, en este momento, la cuadrícula de datos parece estar diseñada para un uso muy específico :(Es una mierda porque nos causa tener barras de desplazamiento dentro de las barras de desplazamiento en algunas UI. –

+0

Esto trae una limitación a WPF que no esperaríamos allí para ser. –

+0

Gracias por esta respuesta: de hecho era el problema que estaba teniendo. Después de eliminar la altura = "Auto" para la fila de cuadrícula de datos, tuve un gran aumento de rendimiento. –

0

Tengo el mismo problema con la cuadrícula de datos encuadernados, y me doy cuenta de que en la primera carga es rápido, pero en el segundo y el siguiente es lento.Así que cuando agrego en código:

DataGrid.ItemsSource = Nothing 

y luego

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

se hizo muy rápido.