UI更新ObservableCollection<;T>;在TreeView控件中

本文关键字:TreeView 控件 gt 更新 lt UI ObservableCollection | 更新日期: 2023-09-27 17:58:40

场景

我有一个绑定到ObservableCollection<T>TreeView。每次最终用户修改其筛选器时,都会修改集合。当用户修改他们的过滤器时,会调用数据库(最多需要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>

这是更新列表的代码。

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);
    }
}

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

关于为什么每当对ObservableCollection<T>进行更改时,我的UI都会挂起,有什么想法吗?

部分解决方案

有了H.B.所说的并实现了BackgroundWorker,更新就更加流畅了。

UI更新ObservableCollection<;T>;在TreeView控件中

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

如果只需清除整个列表并将其替换为新列表,则不希望使用ObservableCollection,使用List并在数据完全加载后激发PropertyChanged事件。

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

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

如果设置了此属性,将通知绑定。这里我使用IEnumerable接口,所以没有人试图只向其中添加项(绑定不会注意到这一点)。但这只是一个建议,可能适用于您的特定场景,也可能不适用。

另请参阅sixtlettervariable的好评


只是一个旁注,这个代码:

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

是错误的,您可能会覆盖该列表,并且绑定将中断,因为在setter中没有触发PropertyChanged事件。

如果你使用ObservableCollection,它通常是这样使用的:

private readonly ObservableCollection<HierarchyViewModel> _hierarchy =
            new ObservableCollection<HierarchyViewModel>();
public ObservableCollection<HierarchyViewModel> Hierarchy
{
    get { return _hierarchy; }
}

最简单的方法是分离绑定到的项目,对列表进行所需的所有更改,然后重新附加它。

例如,将树状视图ItemsSource设置为NULL/NOTHING,运行For each,然后将ItemsSource设置回_hierarchy。您的添加将是即时的。