开发者

UI slow at updating ObservableCollection<T> in TreeView control

Scenario

I have a TreeView that is bound to ObservableCollection<T>. The collection gets modified every time the end-user modifies their filters. When users modify their filters a call to the database is made (takes 1-2ms tops) and the data returned gets parsed to create a hierarchy. I also have some XAML that ensures each TreeViewItem is expanded, which appears to be part of the problem. Keep in mind that I'm only modifying ~200 objects with a max node depth of 3. I would expect开发者_StackOverflow中文版 this to instant.

Problem

The problem is that whenever filters get modified and the TreeView hierarchy gets changed the UI hangs for ~1 second.

Here is the XAML responsible for create the TreeView hierarchy.

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

And here is the code for updating the list.

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

I also have <Setter Property="IsExpanded" Value="True" /> as a style. I have tried using a BindingList<T> and disabling notifications, but that didn't help.

Any ideas as to why my UI hangs whenever changes are made to the ObservableCollection<T>?

Partial Solution

With what H.B. said and implementing a BackgroundWorker the update is much more fluid.


The problem is probably the foreach loop. Every time you add an object the CollectionChanged event is fired and the tree is rebuilt.

You do not want to use an ObservableCollection if all you do is clear the whole list and replace it with a new one, use a List and fire a PropertyChanged event once the data is fully loaded.

i.e. just bind to a property like this (requires implementation of INotifyPropertyChanged):

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

If you set this property bindings will be notified. Here i use the IEnumerable interface so no-one tries to just add items to it (which would not be noticed by the binding). But this is just one suggestion which may or may not work for your specific scenario.

(Also see sixlettervariable's good comment)


Just a side note, this code:

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

is bad, you could overwrite the list and the binding would break because there is no PropertyChanged event being fired in the setter.

If you use an ObservableCollection it normally is used like this:

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


The easiest thing to do is detach the item you are bound to, make all the changes you need to the list, then reattach it.

For example, set the treeviews ItemsSource to NULL/NOTHING, run through your for each, then set the ItemsSource back to _hierarchy. Your adds will be instant.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜