代码之家  ›  专栏  ›  技术社区  ›  David

数据网格单元格.样式绑定

  •  1
  • David  · 技术社区  · 15 年前

    我对WPF数据网格(.net 4.0)有性能问题

    首先,一些细节:

    • 我有一个数据网格,其中一个可观察的集合是ItemsSource。
    • 这个observableCollection本身包含对象的集合,每个集合都是一行,每个对象都是一个单元格(当然是“逻辑”单元格,而不是实际的dataGridCell)

    之所以这样做,是因为我只知道在运行时,我的数据网格中将有多少列。

    • 然后我将每个DataGridCell的值绑定到“logical”表中对象的值(=集合集合)

    现在的问题是,我还必须能够在应用程序运行时随时更改任何单元格的属性(如背景、前景、字体系列等)。

    我提出的解决方案是使用绑定到“逻辑”单元格属性的绑定来设置列的单元格样式

    下面是一个示例代码(我的应用程序中没有Xaml):

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

    我的问题是:当volumnvirtualization打开时(在我的例子中,我不需要行虚拟化),网格需要大约2秒的时间加载,然后每次我大步移动水平滚动条时需要1秒(滚动条bg中的clic,而不是箭头)

    这对我来说太过分了

    所以我的问题是:我做错了什么,如果错了,怎么办?我还有什么更好的办法?

    谢谢你的阅读

    2 回复  |  直到 15 年前
        1
  •  1
  •   vortexwolf    15 年前

    如果ColumnVirtualization带来了这么多问题,为什么需要它? 你可以做一些改进,但它们不能完全解决问题。

    1. 更改轻量级对象的文本框:

      public class TextItem
      {
          public string Text { get; set; }
          public Brush Background { get; set; }
          public Brush Foreground { get; set; }
      }
      
      
      public class Row : ObservableCollection<TextItem>
      {
      }
      
    2. 启用virtualizengstackpanel: dg.SetValue(VirtualizingStackPanel.IsVirtualizingProperty, true);

    3. 用模板替换样式:

          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);
          }
      
        2
  •  0
  •   Community Mohan Dere    9 年前

    经过一段时间的努力,我得出结论,我已经达到了极限。

    对于那些正在处理同一问题的人,这里有一些想法:

    1. 从.net 4.0开始,在WPF中管理单个单元格的可视属性并不是一个简单的方法:MS没有计划任何事情来简化此操作,因此基本上您只能使用两种方法来完成此操作:

      • 使用某种帮助函数获取实际的dataGridCell,然后直接更改其属性。这很容易做到,但如果启用虚拟化,可能会带来很大的麻烦。
      • 将每个单元格的可视属性绑定到dataGridCell样式中VM的依赖项属性。您可以使用DataGrid.CellStyle或Column.CellStyle来执行此操作,具体取决于您的约束。不过,这会使数据网格的速度慢很多,而且管理起来也很麻烦。

    2. 如果像我一样,您别无选择,只能使用第二个选项(因为我需要虚拟化),请考虑以下几点:

      • 你不会被C#困住的。实际上有一种方法可以在Xaml中实现你的CellStyle。见 Martino's post on this issue . 就我而言,这很有效。不过,为了不使用hack,我对其进行了一些调整:我在Xaml中定义了我的样式,并将其应用于Column.CellStyle。然后,当我在代码隐藏中创建一个列时,我只需创建一个继承此列的新样式,然后添加绑定设置为“[列的索引].Self”的标记设置器。这打破了MVVM模型,但我无论如何都不使用它,这样维护起来更容易。
      • 显然,绑定的属性越多,加载dataGrid所需的时间就越长,因此请坚持使用最小值(使用轻量级对象确实会有一点不同,正如Vorrtex所述)。
      • 虽然使用模板或样式对性能没有任何影响,但如果您使用的是dataGridTemplateColumns,显然,您希望直接在模板中设置绑定,而不是在模板上添加样式(这对大量数据确实有一点影响)

    如果有人有什么要补充的,请这样做!我仍然在寻找任何可以改善事情的想法,并会为你在这个问题上的疯狂想法而高兴。即使在三个月内。。。