Интерфейс mvvm wpf зависает при установке коллекции itemssource в асинхронном методе

У меня проблема с ListView. Когда установлен ObservableCollection, который является ItemsSource, приложение зависает на несколько секунд.

Это вид списка:

 <UserControl.Resources>

            <CollectionViewSource x:Key="Sets" Source="{Binding Path=Entries}">
                <CollectionViewSource.GroupDescriptions>
                    <PropertyGroupDescription PropertyName="DailySet.WeeklySet" />
                    <PropertyGroupDescription PropertyName="DailySet" />
                </CollectionViewSource.GroupDescriptions>
            </CollectionViewSource>
        <utilities:InvertableBooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
    </UserControl.Resources>
<ListView ItemsSource="{Binding IsAsync=True, UpdateSourceTrigger=PropertyChanged}" DataContext="{StaticResource Sets}"   >
        <ListView.View >
                <GridView >
                    <GridViewColumn DisplayMemberBinding="{Binding StartTimeFormatted}" Header="Start" Width="60"  />
                    <GridViewColumn DisplayMemberBinding="{Binding EndTimeFormatted}" Header="End" Width="60"  />
                    <GridViewColumn DisplayMemberBinding="{Binding TotalHoursFormatted}" Header="Time" Width="80"  />
                    <GridViewColumn DisplayMemberBinding="{Binding CostCode}" Header="CostCode" Width="120"  />
                    <GridViewColumn DisplayMemberBinding="{Binding Operation}" Header="Operation" Width="160" />
                    <GridViewColumn DisplayMemberBinding="{Binding Comment}" Header="Comment"  >
                    <GridViewColumn.Width>
                        <MultiBinding Converter="{StaticResource LastColumnMaximizerConverter}">
                            <Binding Path="ActualWidth" 
                                         RelativeSource="{RelativeSource AncestorType=ListView}"/>
                            <Binding Path="View.Columns" 
                                         RelativeSource="{RelativeSource AncestorType=ListView}"/>
                        </MultiBinding>
                    </GridViewColumn.Width>
                </GridViewColumn>
                <GridViewColumn Header="Action" Width="60">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate >
                            <StackPanel Orientation="Horizontal" >
                                <Button IsEnabled="{Binding ElementName=thisUc, Path=DataContext.EditingAllowed}"
                                        BorderThickness="0" Margin="2" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Transparent" ToolTip="Edit this entry"
                                        Command="{Binding ElementName=thisUc, Path=DataContext.EditEntryCommand}" CommandParameter="{Binding}">
                                    <Button.Content>
                                        <Image Height="18" Width="20" Source="pack://application:,,,/Gfx/edit.png"></Image>
                                    </Button.Content>
                                </Button>

                                <Button IsEnabled="{Binding ElementName=thisUc, Path=DataContext.EditingAllowed}" BorderThickness="0" Margin="2" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Transparent" ToolTip="Delete this entry"
                                    Command="{Binding ElementName=thisUc, Path=DataContext.DeleteEntryCommand}" CommandParameter="{Binding}">
                                <Button.Content>
                                    <Image Height="18" Width="20" Source="pack://application:,,,/Gfx/delete.png"></Image>
                                </Button.Content>
                            </Button>
 </StackPanel>

                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
            </GridView>
            </ListView.View>
        <ListView.GroupStyle>
            <GroupStyle>
                <GroupStyle.ContainerStyle>
                    <Style TargetType="{x:Type GroupItem}">
                        <Setter Property="Margin" Value="0,0,0,5"/>
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type GroupItem}">
                                    <Expander IsExpanded="{Binding Path=Name.IsWeekOpen, Mode=OneWay}" >
                                        <Expander.Header>
                                           ... header label definition
                                        </Expander.Header>
                                        <Expander.Content>
                                            <ItemsPresenter />
                                        </Expander.Content>
                                    </Expander>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </GroupStyle.ContainerStyle>
            </GroupStyle>
            <GroupStyle>
                <GroupStyle.ContainerStyle>
                    <Style TargetType="{x:Type GroupItem}">
                        <Setter Property="Margin" Value="10,0,10,5"/>
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type GroupItem}">
                                    <Expander IsExpanded="False" BorderBrush="#FFA4B97F" 
                                              BorderThickness="0,0,0,1"  IsEnabled="{Binding Path=Name.DayDetailsGroupingHeader.RealEntriesAvailable}">
                                        <Expander.Header>
                                         ... header label definition
                                        </Expander.Header>
                                        <Expander.Content>
                                            <ItemsPresenter />
                                        </Expander.Content>
                                    </Expander>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </GroupStyle.ContainerStyle>
            </GroupStyle>
        </ListView.GroupStyle>
</ListView>

Это метод, который заполняет список записей:

public override async Task LoadLiveData()
{            
    IEnumerable<DailyDetailedInfo> details = await this.Connector.GetDetails();

    List<DailyDetailEntry> entries = this.Mapper.Map<IEnumerable<DailyDetailedInfo>, IEnumerable<DailyDetailEntry>>(details).ToList();
    //up to this point the execution takes ~300-500 miliseconds

    this.Entries = new ObservableCollection<DailyDetailEntry>(entries.OrderBy(x => x.ExtractedStartDate));
    // collection assigning takes few miliseconds as well
    //this.Entries = new ObservableCollection<DailyDetailEntry>(); when empty collection is assigned, the UI is not frozen, even though the Connector.GetDetails() is called
}

Я уверен, что нет никаких операций, связанных с процессором, и проблема возникает только при назначении коллекции this.Entries. Я попытался заменить назначение this.Entries пустой коллекцией, и в этом случае он отлично работает.

Я также пробовал добавлять элементы по одному, но без изменений:

foreach (DailyDetailEntry dailyDetailEntry in entries.OrderBy(x => x.ExtractedStartDate))
{
    this.Entries.Add(dailyDetailEntry);
}

Что мне делать, чтобы пользовательский интерфейс реагировал?

Отметим, что установка UpdateSourceTrigger=PropertyChanged в привязке ItemsSource бессмысленна. Никакого эффекта. Я также сомневаюсь, что IsAsync=True имеет какой-либо эффект, потому что он просто асинхронно вызывает метод получения свойства источника, который возвращает только экземпляр коллекции. Также неясно, как Entries вообще связаны с привязкой ItemsSource, поскольку нет пути привязки.

Clemens 11.04.2018 12:45

Примерно сколько элементов в списке и насколько сложен шаблон, к которому этот список привязан? Возможно, вам придется заняться виртуализацией элемента управления, к которому привязан список, чтобы создавались и повторно использовались только видимые шаблоны.

Bradley Uffner 11.04.2018 12:48

@BradleyUffner - от 50 до 100 - однако шаблон может быть немного сложным из-за двухуровневой группировки элементов.

Bartosz 11.04.2018 12:55

ListView, который показывает несколько элементов управления GridView, кажется ОЧЕНЬ дорогим. Особенно тот, который показывает 50-100 GridViews. Каждый из этих GridView создается в потоке пользовательского интерфейса при назначении ObservableCollection.

Bradley Uffner 11.04.2018 12:56

Возможно, вы сможете ускорить его, виртуализировав ListView, но этого может быть даже недостаточно. Вы определяете столбцы на GridView явно или позволяете ему делать выводы? Если гриды сами по себе содержат много строк, их, вероятно, также потребуется виртуализировать. Явные столбцы также будут быстрее. Я не уверен, что есть какой-то способ сделать ListView из элементов управления GridView действительно быстрым.

Bradley Uffner 11.04.2018 12:58

@BradleyUffner - спасибо - я обновил XAML в вопросе с полным определением списка.

Bartosz 11.04.2018 13:02

Ах, это полезно. На самом деле вы не создаете несколько элементов управления GridView, как я думал.

Bradley Uffner 11.04.2018 13:03
ListView виртуализирует элементы по умолчанию, но группировка отключает виртуализацию. Это, вероятно, убийца производительности, поскольку похоже, что ваш шаблон довольно дорогой. Есть ли другой способ разместить контент, чтобы получить тот же эффект, что и группировка, без фактического использования группировки (например, вложенные ItemsControl)?
Bradley Uffner 11.04.2018 13:13

@BradleyUffner - потенциально могу. Однако это большая разница. Итак, насколько я понимаю, в основном это ограничение ListView?

Bartosz 11.04.2018 13:17

Да, это странная причуда ListView. Он перестает виртуализироваться, если вы даже посмотрите на него смешно. Вы можете проверить это, используя живое дерево макета в VS, чтобы проверить ListView, чтобы увидеть, установлен ли IsVirtualizing.

Bradley Uffner 11.04.2018 13:22
2
10
243
0

Другие вопросы по теме