【问题标题】:Slow performance when filtering ICollectionView<object>过滤 ICollectionView<object> 时性能下降
【发布时间】:2019-03-12 23:19:41
【问题描述】:

在使用 MVVM 的 WPF 应用程序中,我查询数据库以获取客户端的 ObservableCollection,创建 ICollectionView 并应用过滤器函数。

在我的用户控件上,我将用于过滤器的文本绑定到一个文本框,并将 ICollectionView 绑定到一个列表框。

ICollectionView 最初包含 1104 个客户端(仅 ClientID 和 ClientName)。

从数据库中检索数据非常快。但是,填充列表框大约需要 4 秒。

当我在过滤器中输入文本时,如果要返回的客户端数量较少,则列表框会相对较快地重绘。但是,如果我清除文本框,则需要 4 秒才能重绘。

是我遗漏了什么,还是我的代码写得不好。

感谢您的任何建议/帮助。

查看:

<UserControl x:Class="ClientReports.Module.SchemeSelection.Views.Clients"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:ClientReports.Module.SchemeSelection.Views"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True" >
    <Grid>
        <StackPanel>
            <TextBox  materialDesign:HintAssist.Hint="Client Search"
                      Style="{StaticResource MaterialDesignFloatingHintTextBox}"
                      Text="{Binding Search, UpdateSourceTrigger=PropertyChanged}"/>
            <ListBox ItemsSource="{Binding ClientsFiltered}" DisplayMemberPath="ClientName" />
        </StackPanel>
    </Grid>
</UserControl>

视图模型:

using ClientReports.Common.Infrastructure.Models;
using ClientReports.Common.Infrastructure.Services;
using Prism.Mvvm;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Data;

namespace ClientReports.Module.SchemeSelection.ViewModels
{
    public class ClientsViewModel : BindableBase
    {
        private IClientService clientService;

        public ClientsViewModel(){ }

        public ClientsViewModel(IClientService clientService)
        {
            this.clientService = clientService;
            Clients = new ObservableCollection<Client>();
            GetClients().ContinueWith(x => { });
        }

        public ObservableCollection<Client> Clients { get; }

        public ICollectionView ClientsFiltered { get; set; }

        private string clientFilter;

        public string Search
        {
            get => clientFilter;
            set
            {
                clientFilter = value;
                ClientsFiltered.Refresh();
                RaisePropertyChanged("ClientsFiltered");
            }
        }

        private bool Filter(Client client)
        {
            return Search == null
                || client.ClientName.IndexOf(Search, StringComparison.OrdinalIgnoreCase) != -1;
        }


        private async Task GetClients()
        {
            var clients = await clientService.GetAllAsync();
            foreach (var client in clients)
            {
                Clients.Add(client);
            }
            ClientsFiltered = CollectionViewSource.GetDefaultView(Clients);
            ClientsFiltered.Filter = new Predicate<object>(c => Filter(c as Client));
        }
    }
}

【问题讨论】:

  • 我在您的构造函数中偶然发现了这一行:GetClients().ContinueWith(x =&gt; { });。只是不要那样做。改为阅读:msdn.microsoft.com/en-us/magazine/dn605875.aspx
  • 谢谢@ChristianMurschall。我已经接受了这一点,并根据文章重写了代码。
  • @ChristianMurschall 这没有帮助。没有解释来支持您的陈述,并且链接的文章对ContinueWith的引用为零。
  • 嗨@richarp,因为您使用的是 CollectionView 和过滤,您可能对“当您设置 Filter、SortDescriptions 或 GroupDescriptions 属性时;刷新发生。您不必调用 Refresh 方法设置这些属性之一后立即。有关如何延迟自动刷新的信息,请参阅 DeferRefresh。" 来源:[link] docs.microsoft.com/en-us/dotnet/api/… [/link]
  • @DonBoitnott 我同意我的评论缺乏解释。但由于构造函数中的异步操作不是 OPs 问题,我认为链接可能足够“落后”。顺便说一句,他的代码在 图 3 中的链接文章中进行了解释,不是吗?

标签: c# wpf performance mvvm data-binding


【解决方案1】:

ListBox 可能需要 4 秒来填充,因为未启用虚拟化,因此 WPF 必须创建 1104 个 ListBoxItem(并在清除过滤器时重新创建它们)。默认情况下,ListBox 启用了虚拟化,但您有时甚至可以在没有意识到的情况下禁用它。在您的示例中,您的 ListBox 位于垂直 StackPanel 中,这可能是导致此行为的原因。您可以尝试通过以下方式重写 XAML:

<UserControl x:Class="ClientReports.Module.SchemeSelection.Views.Clients"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:ClientReports.Module.SchemeSelection.Views"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True" >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

            <TextBox  materialDesign:HintAssist.Hint="Client Search"
                      Style="{StaticResource MaterialDesignFloatingHintTextBox}"
                      Text="{Binding Search, UpdateSourceTrigger=PropertyChanged}"
                      Grid.Row="0"/>
            <ListBox  ItemsSource="{Binding ClientsFiltered}"  
                      DisplayMemberPath="ClientName"
                      Grid.Row="1" />            
    </Grid>
</UserControl>

如果没有帮助,您可以尝试为您的 ListBox 设置固定高度并再次检查。

如果它也没有帮助,请查看 Microsoft 虚拟化文档以了解其他可能的原因:https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/optimizing-performance-controls

【讨论】:

  • 谢谢@Pavel。一旦我使用了您的 XAML,它就立即起作用了。非常有帮助。
猜你喜欢
  • 2011-01-09
  • 1970-01-01
  • 2016-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-15
  • 2018-08-20
相关资源
最近更新 更多