c# wpf performance data-binding datagrid

c# - Enlace de DataGrid Cell.Style



wpf performance (2)

Tengo un problema de rendimiento con WPF DataGrid (.net 4.0)

primero, algunos detalles:

  • Tengo una cuadrícula de datos con una colección Observable como ItemsSource.
  • esta colección observable contiene colecciones de objetos, cada colección es una fila, cada objeto es una celda (célula "lógica", por supuesto, no dataGridCell)

La razón por la que hago esto es porque solo sé en tiempo de ejecución cuántas columnas tendré en mi dataGrid.

  • luego, ato el valor de cada DataGridCell al valor del objeto en la tabla "lógica" (= la colección de colecciones)

ahora el problema que tengo es que también tengo que poder cambiar las propiedades de cualquier celda (como Background, Foreground, FontFamily, etc.) en cualquier momento mientras se ejecuta la aplicación.

La solución que se me ocurrió es una que implica establecer cellStyles de columnas con enlaces que se unen a las propiedades de las células "lógicas"

aquí hay un código de muestra (no Xaml en mi aplicación):

public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); Width = 1200; Height = 780; Top = 60; Left = 200; DataGrid dg = new DataGrid(); Content = dg; ObservableCollection<Row> Source = new ObservableCollection<Row>(); dg.ItemsSource = Source; dg.SelectionMode = DataGridSelectionMode.Extended; dg.IsSynchronizedWithCurrentItem = true; dg.CanUserSortColumns = false; dg.CanUserReorderColumns = true; dg.CanUserResizeColumns = true; dg.CanUserResizeRows = true; dg.CanUserAddRows = false; dg.CanUserDeleteRows = false; dg.AutoGenerateColumns = false; dg.EnableColumnVirtualization = true; dg.EnableRowVirtualization = false; // unuseful in my case : I alawys have less lines than the DG can contain dg.VerticalScrollBarVisibility = ScrollBarVisibility.Visible; dg.GridLinesVisibility = DataGridGridLinesVisibility.None; dg.HorizontalGridLinesBrush = Brushes.LightGray; dg.MinRowHeight = 20; dg.RowHeaderWidth = 20; for (int i = 0; i < 100; i++) { DataGridTextColumn column = new DataGridTextColumn(); column.Binding = new Binding(String.Format(CultureInfo.InvariantCulture, "[{0}].Text", i)); Style style = new Style(typeof(DataGridCell)); style.Setters.Add(new Setter(DataGridCell.BackgroundProperty, new Binding(String.Format(CultureInfo.InvariantCulture, "[{0}].Background", i)))); style.Setters.Add(new Setter(DataGridCell.ForegroundProperty, new Binding(String.Format(CultureInfo.InvariantCulture, "[{0}].Foreground", i)))); column.CellStyle = style; column.Header = "Column " + i; dg.Columns.Add(column); } for (int i = 0; i < 35; i++) { Row dgRow = new Row(); Source.Add(dgRow); for (int j = 0; j < 100; j++) dgRow.Add(new TextBox() { Text = "cell " + i + "/" + j, Background = Brushes.AliceBlue, Foreground = Brushes.BlueViolet }); } } } public class Row : ObservableCollection<TextBox> { }

mi problema es: con VolumnVirtualisation On (no necesito Virtualización de filas en mi caso), la cuadrícula tarda aproximadamente 2 segundos en cargarse, y luego 1 segundo cada vez que muevo la barra de desplazamiento horizontal con un gran salto (haga clic en el scrollBar bg, no la flecha)

esto es demasiado para mi propósito

entonces mi pregunta es: ¿estoy haciendo algo mal y si es así, qué? ¿Qué mejor manera de hacer esto tengo?

gracias por leer


Si ColumnVirtualization lo convierte en un problema, ¿por qué lo necesita? Puede hacer varias mejoras, pero no pueden resolver el problema por completo.

  1. Cambiar cuadros de texto para objetos livianos:

    public class TextItem { public string Text { get; set; } public Brush Background { get; set; } public Brush Foreground { get; set; } } public class Row : ObservableCollection<TextItem> { }

  2. Habilitar VirtualizingStackPanel: dg.SetValue(VirtualizingStackPanel.IsVirtualizingProperty, true);

  3. Reemplazar estilos con plantillas:

    for (int i = 0; i < 100; i++) { DataGridTemplateColumn column = new DataGridTemplateColumn(); column.CellTemplate = (DataTemplate)XamlReader.Parse( "<DataTemplate xmlns=''http://schemas.microsoft.com/winfx/2006/xaml/presentation''>" + "<TextBlock DataContext=''{Binding [" + i + "]}'' Text=''{Binding Text}'' Background=''{Binding Background}'' Foreground=''{Binding Foreground}''/>" + "</DataTemplate>"); column.Header = "Column " + i; dg.Columns.Add(column); }


Después de mucho tiempo poner esto en práctica, llegué a la conclusión de que había llegado al límite.

Aquí hay algunos pensamientos para aquellos que están lidiando con el mismo problema:

  1. No hay una manera fácil de administrar las propiedades visuales de una sola celda en WPF a partir de .NET 4.0: MS no planeó nada para que esto sea fácil, así que básicamente tienes dos posibilidades para hacer esto:

    • obtener el dataGridCell real utilizando algún tipo de función auxiliar y luego cambiar sus propiedades directamente. Esto se hace fácilmente, pero puede provocar grandes problemas si la virtualización está activada.
    • enlazar las propiedades visuales de cada celda a las propiedades de dependencia de su VM dentro del Estilo de dataGridCell. puede usar DataGrid.CellStyle o Column.CellStyle para hacerlo, dependiendo de sus limitaciones. Esto ralentiza un poco la dataGrid, y es bastante complicado de manejar.

  2. si, como yo, no tienes más opción que usar la segunda opción (porque necesito virtualización), aquí hay algunas cosas que debes considerar:

    • no estás atascado con C #. En realidad, hay una manera de hacer tu CellStyle en Xaml. Vea la publicación de Martino sobre este tema . En lo que a mí respecta, esto funciona bastante bien. Lo modifiqué un poco para no tener que usar el truco: defino mi estilo en Xaml y lo aplico al Column.CellStyle. Luego, cuando creo una columna en mi código, simplemente creo un nuevo Estilo heredando este, y agrego el Setter con un conjunto de enlaces a: "[índice de la columna] .Self". Esto rompe el modelo MVVM, pero no lo estoy usando de todos modos y es más fácil mantenerlo así.
    • obviamente, cuantas más propiedades tengas que unir, más tiempo tardará tu dataGrid en cargarse, así que mantente al mínimo (usar objetos livianos hace una pequeña diferencia, como dice Vorrtex).
    • mientras usa plantillas o estilos no hace absolutamente ninguna diferencia con respecto al rendimiento, si está utilizando dataGridTemplateColumns, querría establecer sus enlaces directamente en la plantilla en lugar de agregar un estilo encima de la plantilla, obviamente (esto hace una pequeña diferencia con una gran cantidad de datos)

si alguien tiene algo que agregar a esto, ¡por favor hazlo! Todavía estoy buscando alguna idea que pueda mejorar las cosas y me alegrará cualquier idea loca que tengas sobre el tema. Incluso en 3 meses ...