DataGrid Cell.Style绑定

时间:2010-11-23 08:59:57

标签: c# wpf performance data-binding datagrid

我的WPF DataGrid(.net 4.0)存在性能问题

首先,一些细节:

  • 我有一个带有Observable集合的datagrid作为ItemsSource。
  • 这个observableCollection本身包含对象集合,每个集合因此是一行,每个对象都是一个单元格(当然是“逻辑”单元格,而不是实际的dataGridCell)

我这样做的原因是因为我只在运行时知道我在dataGrid中会有多少列。

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

现在我遇到的麻烦是我还必须能够在应用程序运行时随时更改任何单元格的属性(如Background,Foreground,FontFamily等等)。

我想出的解决方案是将列的'cellStyles设置为绑定到“逻辑”单元格属性的绑定

这是一个示例代码(我的应用程序中没有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>
{
}

我的问题是:使用VolumnVirtualisation On(在我的情况下我不需要行虚拟化),网格需要大约2秒加载,然后每次移动水平滚动条大幅跳跃1秒(clic in the scrollBar bg,而不是箭头)

这对我来说太过分了

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

感谢阅读

2 个答案:

答案 0 :(得分:1)

如果ColumnVirtualization出现问题,为什么还需要它? 你可以做一些改进,但他们无法完全解决问题。

  1. 更改轻量级对象的TextBox:

    public class TextItem
    {
        public string Text { get; set; }
        public Brush Background { get; set; }
        public Brush Foreground { get; set; }
    }
    
    
    public class Row : ObservableCollection<TextItem>
    {
    }
    
  2. 启用VirtualizingStackPanel: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);
        }
    

答案 1 :(得分:0)

经过大量的时间投入,我得出结论,我已达到极限。

以下是处理同一问题的人的一些想法:

  1. 从.net 4.0开始,没有简单的方法可以在WPF中管理单个单元格的视觉属性:MS没有计划任何事情来简化这一点,所以基本上你有两种可能性来做到这一点:

    • 使用某种辅助函数获取实际的dataGridCell,然后直接更改其属性。这很容易完成,但如果打开虚拟化会导致大麻烦。
    • 将每个单元格的可视属性绑定到dataGridCell样式中VM的依赖属性。您可以使用DataGrid.CellStyle或Column.CellStyle来执行此操作,具体取决于您的约束。这会使dataGrid相当缓慢,并且管理起来非常麻烦。

  2. 如果像我一样你别无选择,只能使用第二种选择(因为我需要虚拟化),这里有几点需要考虑:

    • 你不会被C#困住。实际上有一种方法可以在Xaml中进行CellStyle。见Martino's post on this issue。就我而言,这非常有效。我稍微调整了一下,以便不必使用hack:我在Xaml中定义我的样式并将其应用于Column.CellStyle。然后当我在我的代码后面创建一个列时,我只是创建一个继承这个列的新样式,然后我添加一个带有绑定集的Tag setter:“[column's Index] .Self”。这打破了MVVM模型,但我还是没有使用它,而且这样更容易维护。
    • 显然,你必须绑定的属性越多,你的dataGrid加载所需的时间就越多,所以坚持最小(使用轻量级对象确实会产生很小的差异,如Vorrtex所述)。
    • 虽然使用模板或样式在性能方面没有任何区别,但如果你使用的是dataGridTemplateColumns,你需要直接在模板中设置绑定,而不是在模板顶部添加样式,这显然是与大量数据的微小差异)
  3. 如果有人要添加任何内容,请执行此操作!我仍然在寻找可以改善现状的任何想法,并且对于你对这个主题的任何疯狂想法感到高兴。即使在3个月内......