Задержка переключения окна управления DataGrid в приложении WPF при большом количестве строк в не виртуализированной DataGrid - PullRequest
9 голосов
/ 23 июня 2011

У нас есть небольшая демоверсия .NET 4.0 DataGrid WPF с кодом, размещенным ниже.Он состоит из не виртуализированной DataGrid с 30 столбцами и 3000 строк.Он не виртуализирован, так как нам требуются возможности группировки, которые не поддерживают виртуализацию.

Когда вы запускаете это приложение и переключаетесь между ним и другими окнами, возникает заметная задержка (около 1 секунды),Это происходит только тогда, когда окно повторно активировано - после активации щелчок внутри не имеет никакой связанной задержки.

Мы профилировали эту задержку, которая возникает при повторной активации окна с помощью анализатора производительности, и обнаружили, что существует большая зависимостьуведомления о свойствах, запускаемые при возврате окна в фокус.Мы не знаем, почему это происходит, и это кажется ненужным.

Мы считаем, что эта задержка пропорциональна количеству строк в DataGrid.Кто-нибудь знает, как мы можем устранить или уменьшить эту задержку?

ОБНОВЛЕНИЕ: Кажется, что задержка фокуса происходит, даже когда вы остаетесь внутри приложения, но фокусируете внимание на другом элементе управления, таком как текстовое поле вне сетки.Поэтому теперь мы знаем, что это не проблема переключения окон, а проблема, связанная с изменением фокуса, но мы все еще не уверены в точной причине.

(MainWindow.xaml)

<Window x:Class="WpfApplication20.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid Name="dataGrid" VirtualizingStackPanel.IsVirtualizing="False" AutoGenerateColumns="True"/>
    </Grid>
</Window>

(MainWindow.xaml.cs)

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 XML):

<!--<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>-->

Ответы [ 4 ]

4 голосов
/ 30 июня 2011

Решение довольно простое.Используйте сетку, унаследованную от DataGrid, переопределите OnIsKeyboardFocusWithinChanged() и НЕ вызывайте base.OnIsKeyboardFocusWithinChangedand():

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)

Диспетчер визуальных состояний обновляет каждую ячейку в событии IsKeyboardFocusWithinChanged.

Кстати, достаточно интересно, если вы используете ILSpy для декомпиляции DataGrid.OnIsKeyboardFocusWithinChanged(), вы не найдете его там, как если бы он не переопределял поведение по умолчанию из базового класса. WinDbg показывает совершенно противоположное.

Надеюсь, это ответит на ваш вопрос, но я могу легко предвидеть еще много проблем без включенной виртуализации.

ОБНОВЛЕНИЕ Как найти причину?Просто подключите с помощью отладчика к вашему процессу, и каждый раз, когда вы видите, что медлительность врывается в отладчик, проверяйте трассировку стека потока пользовательского интерфейса.Если часто появляется один и тот же след стека - это, вероятно, ваш злодей.Я использовал WinDbg , чтобы отследить это.Отладчик VS также должен быть эффективным.

4 голосов
/ 27 июня 2011

Я бы попытался использовать свойства Dependecy вместо обычных свойств CLR в классе строк (или в классе rowViewModel), особенно с учетом того, что столбцов так много.

Используете ли вы какие-либо стили / шаблоны?я предполагаю, что код, который вы разместили, несколько упрощен :) также, вы говорите, что не видите эту задержку при открытии окна?только когда он снова сфокусирован?

-edit-

Хорошо, я провел несколько тестов и пришел к выводу, что привязка не является проблемой.Мне показалось, что я смог уменьшить пик процессора, когда окно фокусируется, задав собственный стиль ячейки (и установив его в общий):

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

Тем не менее, программа использует огромные объемы памяти,1 Гб. Кроме того, прокрутка в сторону на моей машине почему-то медленная, но прокрутка вниз - это нормально.я не думаю, что встроенная сетка данных действительно подходит для такого количества данных, по крайней мере, в невирулизированном режиме.Тем не менее, вы можете попробовать стиль ячейки и посмотреть, улучшит ли это вещи

1 голос
/ 01 июля 2011

Для тех, кто заинтересован, мы решили эту проблему, определив отдельные области фокусировки в нашем приложении. По умолчанию все окно является областью фокуса. Создавая отдельные и проверяя, что мы устанавливаем область фокуса на что-то другое, кроме сетки, перед тем как окно снова деактивируется и снова активируется, это обеспечивает его быстрое восстановление и возврат. Мы предполагаем, что в WPF есть некоторые недокументированные функциональные возможности, которые вызывают это явление.

0 голосов
/ 29 июня 2011

У нас была такая же проблема в компании, в которой я работаю.Мы заменили его сторонней сеткой, и проблема ушла.Большинство таблиц данных WPF имеют ужасную производительность.

...