C# 当非虚拟化数据网格中有很多行时,WPF应用程序数据网格控制窗口切换延迟

C# 当非虚拟化数据网格中有很多行时,WPF应用程序数据网格控制窗口切换延迟,c#,.net,wpf,datagrid,wpfdatagrid,C#,.net,Wpf,Datagrid,Wpfdatagrid,我们有一个小的.NET4.0DataGrid WPF演示,代码发布在下面。它由一个30列3000行的非虚拟化数据网格组成。它是非虚拟化的,因为我们需要分组功能,而分组功能不支持虚拟化 当您运行此应用程序并在它和其他窗口之间切换时,会出现明显的延迟(约1秒)。只有当窗口被重新激活时才会发生这种情况-一旦激活,在窗口内部单击没有相关的延迟 我们使用performance analyzer分析了窗口重新激活时出现的这种延迟,并发现当窗口重新成为焦点时,会触发许多依赖属性通知。我们不知道为什么会发生这种

我们有一个小的.NET4.0DataGrid WPF演示,代码发布在下面。它由一个30列3000行的非虚拟化数据网格组成。它是非虚拟化的,因为我们需要分组功能,而分组功能不支持虚拟化

当您运行此应用程序并在它和其他窗口之间切换时,会出现明显的延迟(约1秒)。只有当窗口被重新激活时才会发生这种情况-一旦激活,在窗口内部单击没有相关的延迟

我们使用performance analyzer分析了窗口重新激活时出现的这种延迟,并发现当窗口重新成为焦点时,会触发许多依赖属性通知。我们不知道为什么会发生这种情况,而且似乎没有必要

我们发现这个延迟与数据网格中的行数成正比。有人知道我们如何消除或减少这种滞后吗

更新:即使在应用程序内部,但关注另一个控件(如网格外的文本框)时,焦点延迟似乎也会发生。因此,我们现在知道这不是一个窗口切换问题,而是一个由焦点变化引起的问题,但仍然不确定确切的原因

(MainWindow.xaml)


(MainWindow.xaml.cs)

使用System.Collections.Generic;
使用System.Windows;
命名空间WpfApplication20
{
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
列表行=新列表();
对于(int i=0;i<3000;i++)
{
行=新行(i);
行。添加(行);
}
dataGrid.ItemsSource=行;
}
}
公共类行
{
公共双精度1{get;set;}
公共双精度2{get;set;}
公共双精度3{get;set;}
公共双精度4{get;set;}
公共双精度5{get;set;}
公共双精度6{get;set;}
公共双精度7{get;set;}
公共双精度8{get;set;}
公共双精度9{get;set;}
公共双精度10{get;set;}
公共双精度11{get;set;}
公共双精度12{get;set;}
公共双精度13{get;set;}
公共双精度14{get;set;}
公共双精度15{get;set;}
公共双精度16{get;set;}
公共双精度17{get;set;}
公共双精度18{get;set;}
公共双精度19{get;set;}
公共双精度20{get;set;}
公共双精度21{get;set;}
公共双精度22{get;set;}
公共双精度23{get;set;}
公共双精度24{get;set;}
公共双精度25{get;set;}
公共双精度26{get;set;}
公共双精度27{get;set;}
公共双精度28{get;set;}
公共双精度29{get;set;}
公共双精度30{get;set;}
公共行(双d)
{
双1=d;
Double2=d+1;
双3=d+2;
双4=d+3;
双5=d+4;
双6=d+5;
双7=d+6;
双8=d+7;
双9=d+8;
双10=d+9;
双11=d+10;
双12=d+11;
双13=d+12;
双14=d+13;
双15=d+14;
双16=d+15;
双17=d+16;
双18=d+17;
双19=d+18;
双20=d+19;
双21=d+20;
双22=d+21;
双23=d+22;
双24=d+23;
双25=d+24;
双26=d+25;
双27=d+26;
双28=d+27;
双29=d+28;
双30=d+29;
}             
}                 
}
(组样式-可通过放入DataGrid XML来启用):


我会尝试在row类(或rowViewModel类)中使用Dependecy属性,而不是常规的CLR属性,尤其是因为列太多了

您是否使用任何样式/模板?我假设您发布的代码有点简化:) 还有,你是说当窗口打开时,你看不到这种延迟?只有当它重新成为焦点时

-编辑-

好的,我做了一些测试,也得出结论,绑定不是问题所在。通过定义自定义单元格样式(并将其设置为共享),我似乎能够在窗口获得焦点时降低cpu峰值:



不过,该程序使用了大量内存,几乎为1GB。。另外,由于某些原因,在我的机器上横向滚动速度很慢,但向下滚动是可以的。我不认为内置的datagrid真的适合这种数据量,至少在非虚拟化模式下是这样。不过,你还是可以试试这种单元格样式,看看它是否能改善情况。在我工作的公司,我们也遇到了同样的问题。我们用第三方电网取代了它,问题就消失了。大多数WPF数据网格的性能都很糟糕。

解决方案非常简单。使用从
DataGrid
继承的网格,重写
OnIsKeyboardFocusWithinChanged()
,不要调用
base.OnIsKeyboardFocusWithinChangedand()

性能应该更好。至少对于示例代码:)

以下堆栈跟踪是瓶颈原因:

0012dd90 58cd5818 System.Windows.DependencyObject.LookupEntry(Int32)
0012ddb0 58cd923b System.Windows.DependencyObject.GetValueSource(System.Windows.DependencyProperty, System.Windows.PropertyMetadata, Boolean ByRef, Boolean ByRef, Boolean ByRef, Boolean ByRef, Boolean ByRef)
0012dde0 58cd91f2 System.Windows.DependencyObject.GetValueSource(System.Windows.DependencyProperty, System.Windows.PropertyMetadata, Boolean ByRef)
0012de04 56c78477 System.Windows.VisualStateManager.GetVisualStateGroupsInternal(System.Windows.FrameworkElement)
0012de14 56c504e7 System.Windows.VisualStateManager.GoToStateCommon(System.Windows.FrameworkElement, System.Windows.FrameworkElement, System.String, Boolean)
0012de38 56c504ac System.Windows.VisualStateManager.GoToState(System.Windows.FrameworkElement, System.String, Boolean)
0012de50 5753506a System.Windows.Controls.DataGridCell.ChangeVisualState(Boolean)
0012de64 56c5012c System.Windows.Controls.Control.UpdateVisualState(Boolean)
0012de74 575353fa System.Windows.Controls.DataGridCell.NotifyPropertyChanged(System.Windows.DependencyObject, System.String, System.Windows.DependencyPropertyChangedEventArgs, System.Windows.Controls.DataGridNotificationTarget)
0012debc 5753b4d0 System.Windows.Controls.Primitives.DataGridCellsPresenter.NotifyPropertyChanged(System.Windows.DependencyObject, System.String, System.Windows.DependencyPropertyChangedEventArgs, System.Windows.Controls.DataGridNotificationTarget)
0012df04 5752a1d5 System.Windows.Controls.DataGridRow.NotifyPropertyChanged(System.Windows.DependencyObject, System.String, System.Windows.DependencyPropertyChangedEventArgs, System.Windows.Controls.DataGridNotificationTarget)
0012df4c 5751becd System.Windows.Controls.DataGrid.NotifyPropertyChanged(System.Windows.DependencyObject, System.String, System.Windows.DependencyPropertyChangedEventArgs, System.Windows.Controls.DataGridNotificationTarget)
0012dfa4 57512d98 System.Windows.Controls.DataGrid.OnIsKeyboardFocusWithinChanged(System.Windows.DependencyObject, System.Windows.DependencyPropertyChangedEventArgs)
Visual state manager使用更改的事件更新
isKeyboardFocus上的每个单元格

顺便说一句,如果您使用ILSpy进行反编译,那就足够有趣了
using System.Collections.Generic;
using System.Windows;

namespace WpfApplication20
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            List<Row> rows = new List<Row>();

            for(int i = 0; i < 3000; i++)
            {
                Row row = new Row(i);
                rows.Add(row);
            }

            dataGrid.ItemsSource = rows;
        }
    }

    public class Row
    {
        public double Double1 { get; set; }
        public double Double2 { get; set; }
        public double Double3 { get; set; }
        public double Double4 { get; set; }
        public double Double5 { get; set; }
        public double Double6 { get; set; }
        public double Double7 { get; set; }
        public double Double8 { get; set; }
        public double Double9 { get; set; }
        public double Double10 { get; set; }
        public double Double11 { get; set; }
        public double Double12 { get; set; }
        public double Double13 { get; set; }
        public double Double14 { get; set; }
        public double Double15 { get; set; }
        public double Double16 { get; set; }
        public double Double17{ get; set; }
        public double Double18 { get; set; }
        public double Double19 { get; set; }
        public double Double20 { get; set; }
        public double Double21 { get; set; }
        public double Double22 { get; set; }
        public double Double23 { get; set; }
        public double Double24 { get; set; }
        public double Double25 { get; set; }
        public double Double26 { get; set; }
        public double Double27 { get; set; }
        public double Double28 { get; set; }
        public double Double29 { get; set; }
        public double Double30 { get; set; }

        public Row(double d)
        {
            Double1 = d;
            Double2 = d + 1;
            Double3 = d + 2;
            Double4  = d + 3;
            Double5  = d + 4;
            Double6  = d + 5;
            Double7  = d + 6;
            Double8  = d + 7;
            Double9  = d + 8;
            Double10 = d + 9;
            Double11 = d + 10;
            Double12 = d + 11;
            Double13 = d + 12;
            Double14 = d + 13;
            Double15 = d + 14;
            Double16 = d + 15;
            Double17 = d + 16;
            Double18 = d + 17;
            Double19 = d + 18;
            Double20 = d + 19;
            Double21 = d + 20;
            Double22 = d + 21;
            Double23 = d + 22;
            Double24 = d + 23;
            Double25 = d + 24;
            Double26 = d + 25;
            Double27 = d + 26;
            Double28 = d + 27;
            Double29 = d + 28;
            Double30 = d + 29;
        }             
    }                 
}
<!--<DataGrid.GroupStyle>
                <GroupStyle>
                    <GroupStyle.ContainerStyle>
                        <Style TargetType="{x:Type GroupItem}">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type GroupItem}">
                                        <Border BorderBrush="DarkGray" BorderThickness="1" Padding="4,0" >
                                            <Expander VerticalContentAlignment="Center" IsExpanded="True">
                                                <Expander.Header>
                                                    <Canvas>
                                                        <StackPanel Orientation="Horizontal" Canvas.Top="-11" Canvas.Left="4">
                                                            <Label Content="{Binding Name}" Visibility="{Binding DataContext.ShowGroupHeaderVisibility, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
                                                            <Label Content="{Binding ItemCount}" Visibility="{Binding DataContext.ShowGroupCountVisibility, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
                                                        </StackPanel>
                                                    </Canvas>
                                                </Expander.Header>
                                                <ItemsPresenter/>
                                            </Expander>
                                        </Border>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </GroupStyle.ContainerStyle>
                </GroupStyle>
            </DataGrid.GroupStyle>-->
<Window>
  <Grid>
    <Grid.Resources>
      <Style TargetType="DataGridCell"
             x:Key="cs"
             x:Shared="True">
        <Setter Property="Content"
                Value="{Binding}" />
      </Style>
    </Grid.Resources>
    <DataGrid Name="dataGrid"
              VirtualizingStackPanel.IsVirtualizing="False"
              CellStyle="{StaticResource cs}">
    </DataGrid>
  </Grid>
</Window>
public class MyGrid : DataGrid
{
    protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e)
    {
        // Do not call base.OnIsKeyboardFocusWithinChanged(e);
    }
}
0012dd90 58cd5818 System.Windows.DependencyObject.LookupEntry(Int32)
0012ddb0 58cd923b System.Windows.DependencyObject.GetValueSource(System.Windows.DependencyProperty, System.Windows.PropertyMetadata, Boolean ByRef, Boolean ByRef, Boolean ByRef, Boolean ByRef, Boolean ByRef)
0012dde0 58cd91f2 System.Windows.DependencyObject.GetValueSource(System.Windows.DependencyProperty, System.Windows.PropertyMetadata, Boolean ByRef)
0012de04 56c78477 System.Windows.VisualStateManager.GetVisualStateGroupsInternal(System.Windows.FrameworkElement)
0012de14 56c504e7 System.Windows.VisualStateManager.GoToStateCommon(System.Windows.FrameworkElement, System.Windows.FrameworkElement, System.String, Boolean)
0012de38 56c504ac System.Windows.VisualStateManager.GoToState(System.Windows.FrameworkElement, System.String, Boolean)
0012de50 5753506a System.Windows.Controls.DataGridCell.ChangeVisualState(Boolean)
0012de64 56c5012c System.Windows.Controls.Control.UpdateVisualState(Boolean)
0012de74 575353fa System.Windows.Controls.DataGridCell.NotifyPropertyChanged(System.Windows.DependencyObject, System.String, System.Windows.DependencyPropertyChangedEventArgs, System.Windows.Controls.DataGridNotificationTarget)
0012debc 5753b4d0 System.Windows.Controls.Primitives.DataGridCellsPresenter.NotifyPropertyChanged(System.Windows.DependencyObject, System.String, System.Windows.DependencyPropertyChangedEventArgs, System.Windows.Controls.DataGridNotificationTarget)
0012df04 5752a1d5 System.Windows.Controls.DataGridRow.NotifyPropertyChanged(System.Windows.DependencyObject, System.String, System.Windows.DependencyPropertyChangedEventArgs, System.Windows.Controls.DataGridNotificationTarget)
0012df4c 5751becd System.Windows.Controls.DataGrid.NotifyPropertyChanged(System.Windows.DependencyObject, System.String, System.Windows.DependencyPropertyChangedEventArgs, System.Windows.Controls.DataGridNotificationTarget)
0012dfa4 57512d98 System.Windows.Controls.DataGrid.OnIsKeyboardFocusWithinChanged(System.Windows.DependencyObject, System.Windows.DependencyPropertyChangedEventArgs)