UI在TreeView控件中更新ObservableCollection <T>时速度很慢

gcs*_*cso 2 c# wpf xaml

脚本

我有一个TreeView必然的ObservableCollection<T>.每次最终用户修改其过滤器时,都会修改该集合.当用户修改他们的过滤器时,会调用数据库(需要1-2ms顶部),并且返回的数据将被解析以创建层次结构.我也有一些XAML确保每个都TreeViewItem被扩展,这似乎是问题的一部分.请记住,我只修改了200个最大节点深度为3的对象.我希望这是即时的.

问题

问题是每当过滤器被修改并且TreeView层次结构发生变化时,UI会挂起约1秒钟.

这是负责创建TreeView层次结构的XAML .

<TreeView VerticalAlignment="Top" ItemsSource="{Binding Hierarchy}" Width="240"
          ScrollViewer.VerticalScrollBarVisibility="Auto" Grid.Row="1">
    <TreeView.ItemTemplate>
        <!-- Hierarchy template -->
        <HierarchicalDataTemplate ItemsSource="{Binding Stations}">
            <TextBlock Text="{Binding}" />
            <!-- Station template -->
            <HierarchicalDataTemplate.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Locates}">
                    <TextBlock Text="{Binding Name}" />
                    <!-- Locate template -->
                    <HierarchicalDataTemplate.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding TicketNo}" />
                        </DataTemplate>
                    </HierarchicalDataTemplate.ItemTemplate>
                </HierarchicalDataTemplate>
            </HierarchicalDataTemplate.ItemTemplate>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>
Run Code Online (Sandbox Code Playgroud)

这是更新列表的代码.

public ObservableCollection<HierarchyViewModel> Hierarchy
{
    get { return _hierarchy; }
    set { _hierarchy = value; }
}

public void UpdateLocates(IList<string> filterIds)
{
    _hierarchy.Clear();

    // Returns 200 records max    
    var locates = _locateRepository.GetLocatesWithFilters(filterIds);
    var dates = locates.Select(x => x.DueDate);

    foreach (var date in dates)
    {
        var vm = new HierarchyViewModel
        {
            DueDate = date
        };
        var groups = locates.Where(x => x.DueDate.Date.Equals(date.Date)).GroupBy(x => x.SentTo);

        // Logic ommited for brevity

        _hierarchy.Add(vm);
    }
}
Run Code Online (Sandbox Code Playgroud)

我也有<Setter Property="IsExpanded" Value="True" />一种风格.我尝试过使用BindingList<T>和禁用通知,但这没有帮助.

任何想法,为什么我的UI挂起,只要对其进行更改ObservableCollection<T>

部分解决方案

随着HB的说法和实施BackgroundWorker更新更加流畅.

H.B*_*.B. 6

问题可能是foreach循环.每次添加对象时,CollectionChanged都会触发事件并重建树.

ObservableCollection如果你所做的只是清除整个列表并用新的列表替换它,你就不想使用它,在数据完全加载后使用a List并触发PropertyChanged事件.

即只绑定到这样的属性(需要实现INotifyPropertyChanged):

private IEnumerable<HierarchyViewModel> _hierarchy = null;
public IEnumerable<HierarchyViewModel> Hierarchy
{
    get { return _hierarchy; }
    set
    {
        if (_hierarchy != value)
        {
            _hierarchy = value;
            NotifyPropertyChanged("Hierarchy");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果设置此属性绑定将被通知.在这里我使用IEnumerable界面,所以没有人试图只是添加项目(绑定不会注意到).但这只是一个建议,可能适用于您的具体情况,也可能不适用.

(另见sixlettervariable的好评)


只是旁注,这段代码:

public ObservableCollection<HierarchyViewModel> Hierarchy
{
    get { return _hierarchy; }
    set { _hierarchy = value; }
}
Run Code Online (Sandbox Code Playgroud)

是坏的,你可以覆盖列表,绑定会破坏,因为在setter中没有触发PropertyChanged事件.

如果使用ObservableCollection,通常使用如下:

private readonly ObservableCollection<HierarchyViewModel> _hierarchy =
            new ObservableCollection<HierarchyViewModel>();
public ObservableCollection<HierarchyViewModel> Hierarchy
{
    get { return _hierarchy; }
}
Run Code Online (Sandbox Code Playgroud)

  • +1,或者他可以使用`foreach`但分两部分.一部分将创建所有VM,一部分将添加所有VM.另一种可能性是自定义集合,它是`INotifyCollectionChanged`并实现了一个合适的`AddRange`. (2认同)