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

DataGrid数据绑定属性Get调用过多

  •  0
  • GWLlosa  · 技术社区  · 14 年前

    我们在绑定到对象集合的窗口上有一个数据网格。我们在这个窗口上的性能非常差;加载最多需要20秒,然后每次“滚动”数据网格需要5-7秒。到目前为止一共有12项。当我们调查时,似乎导致增长放缓的主要原因是属性getter;我们的一些getter被调用了20000次以上(即每个对象1666.667次)!仪器显示我们的吸气剂不是 尤其 慢;最慢的花了0.002秒。不幸的是,当你乘以0.0002*20k,你很容易得到我们正在经历的延迟。

    在这个概念证明的例子中,这不是一个问题;但是我们的实际对象要复杂得多,我们不能承受40秒的延迟来访问一个属性20000次!有人知道这是怎么回事吗?我怎样才能让网格不那么积极地轮询我的对象呢?我们正在使用WPF工具包DataGrid和.NET版本3.5。PoC的示例代码如下:

    *****窗口代码*****

    <Window x:Class="PocApp.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:WpfToolkit="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
        Title="Window1" Height="200" Width="200">
        <Grid>
            <WpfToolkit:DataGrid ItemsSource="{Binding Path=MyEntries}">
    
            </WpfToolkit:DataGrid>
        </Grid>
    </Window>
    

    *****窗口代码隐藏*****

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace PocApp
    {
        /// <summary>
        /// Interaction logic for Window1.xaml
        /// </summary>
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
                this.DataContext = new EntryViewModel();
            }
        }
    }
    

    *****入门级*****

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace PocApp
    {
        public class Entry
        {
            public Entry(string first, string last)
            {
                FirstName = first;
                LastName = last;
            }
    
            public string FirstName
            {
                get
                {
                    firstCall++;
                    System.Console.WriteLine("FirstName Call:" + firstCall);
                    return _firstname;
                }
                set
                {
                    _firstname = value;
                }
            }
            public int FirstNameCallCount
            {
                get
                {
                    return firstCall;
                }
            }
            public string LastName
            {
                get
                {
                    lastCall++;
                    System.Console.WriteLine("LastName Call:" + lastCall);
                    return _lastname;
                }
                set
                {
                    _lastname = value;
                }
            }
            public int LastNameCallCount
            {
                get
                {
                    return lastCall;
                }
            }
            private string _firstname,_lastname;
            private int firstCall,lastCall  = 0;
        }
    }
    

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Data;
    
    namespace PocApp
    {
        public class EntryViewModel
        {
            public EntryViewModel()
            {
                List<Entry> myCoolEntries = new List<Entry>()
                {
                    new Entry("A","A1"),
                    new Entry("B","B1"),
                    new Entry("C","C1"),
                    new Entry("D","D1"),
                    new Entry("E","E1"),
                    new Entry("F","F1"),
                    new Entry("G","G1"),
                    new Entry("H","H1"),
                    new Entry("I","I1"),
                    new Entry("J","J1"),
                    new Entry("K","K1"),
                    new Entry("L","L1"),
                    new Entry("M","M1"),
                    new Entry("N","N1"),
                    new Entry("O","O1"),
                    new Entry("P","P1"),
                    new Entry("Q","Q1"),
                };
                MyEntries = (CollectionView)CollectionViewSource.GetDefaultView(myCoolEntries);
            }
            public CollectionView MyEntries
            {
                get;
                private set;
            }
        }
    }
    
    1 回复  |  直到 14 年前
        1
  •  2
  •   Alex Paven    14 年前

    你在概念证明中看到的数字实际上是正常的。它们是datagrid中行虚拟化的结果;当一个项目被滚动出视图时,容器行被重新用于显示一个新输入的项目,从而相对于创建和操作的实际可视控件保持较低的内存使用率。显示另一项意味着重新查询绑定属性。

    通常我认为这不会是一个问题;我处理过相当复杂的业务对象,并在datagrid中显示了相当多的数据,而没有您提到的那种问题。我建议您看看应用于datagrid的任何样式,因为它们可能会干扰行的虚拟化;而且,执行耗时2毫秒的属性getter并不是我所说的fast:)

    Edit:实际上,只有禁用虚拟化才能避免对属性getter的额外调用,这将创建 巨大的

    另一个编辑:我更仔细地重读了这个问题,我注意到你说 12 项目?!我很抱歉,但现在我非常坚决地说,不应该把这归咎于datagrid,除非你的对象有成千上万个属性,都绑定到datagrid中的列,我怀疑这是不可能的。请检查其余的代码和样式,并试图确定任何潜在的问题领域,我们可以建议你。。。不知道还能说什么。还要检查实体是否不必要地引发NotifyPropertyChanged事件,因为这将导致绑定控件重新查询属性。