0
votes

I'm a newbie to WPF and C# and am building my first app mostly by using code examples. I'm sure there might be some better ways to do this, that I'm not understanding yet, so I'm coming to you guys for some advice.

I have a treeview control with of a bunch of nested objects that is downloaded into an ObservableCollection viewmodel from a WCF Service that I also built. I have the viewmodel declared in the Windows.Resources of the XAML.

My treeview then binds to that StaticResource by its key name.

Items="{Binding Source={StaticResource MyCatalogModel},Path=Items, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

The data in the tree is saved locally to a file. When the viewmodel is instantiated it reads the file, or it creates it if it doesn't exist.

I have 2 related problems right now.

  1. Sometimes the data object that is imported is rather large with lots of nested objects (children). This is taking a long time to update the tree. How can I speed this up? Can I "turn off" the Notify changed stuff of the ObservableCollection, and just reload (rebind?) the viewmodel when it's finished?

  2. I'd like to give the user the ability to basically clear out all the items from the tree and start from scratch. I have code that dumps the underlying file and as I said, it will be recreated when a new viewmodel is instantiated, but I don't know how to "reset" the binding of the resource and the tree. How do I do this?

Thanks to all who respond and any code snippets will be greatly appreciated!!

2
You can speed up the TreeView thing by 1) Binding the TreeView to a property on your viewmodel (like you already have, Items). 2) When you load your data, create a new ObservableCollection in a local variable and load your data into it. 3) Finally, assign the local variable to the Items property. This causes the TreeView to rebind/refresh to the contents of the new ObservableCollection that is already populated, instead of updating on every INotifyCollectionChanged event that's happening.Steve

2 Answers

0
votes

I had a similar problem, where I had a very large amount of data in a collection - and the event for OnPropertyChanged was firing for each item in the collection. I added an extension with a method to add a range to an ObservableCollection. Here is the code for the extension.

public class SmartCollection<T> : ObservableCollection<T>
{
    public SmartCollection()
        : base()
    {
        _suspendCollectionChangeNotification = false;
    }


    bool _suspendCollectionChangeNotification;

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (!_suspendCollectionChangeNotification)
        {
            base.OnCollectionChanged(e);
        }
    }

    public void SuspendCollectionChangeNotification()
    {
        _suspendCollectionChangeNotification = true;
    }

    public void ResumeCollectionChangeNotification()
    {
        _suspendCollectionChangeNotification = false;
    }


    public void AddRange(IEnumerable<T> items)
    {
        this.SuspendCollectionChangeNotification();
        int index = base.Count;
        try
        {
            foreach (var i in items)
            {
                base.InsertItem(base.Count, i);
            }
        }
        finally
        {
            this.ResumeCollectionChangeNotification();
            var arg = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
            this.OnCollectionChanged(arg);
        }
    }

}

So instead of an ObservableCollection its a SmartCollection. What I did was build my collection of objects into a List then you call the AddRange method and pass in your List of objects. This greatly improved the performance.

As far as recreating the tree - if its based on the viewmodel. Just new up the viewmodel that its bound to.

0
votes

It's amazing how many times the same old questions come up again and again. Right, I have no idea why you are Binding to a StaticResource, but that is a bad idea.

Just create a public property of type ObservableCollection<T> in your view model, set an instance of it as the DataContext of your view in whichever way you prefer or know. Make sure you implement the INotifyPropertyChanged Interface correctly in the code behind, or declare a DependencyProperty instead. Then you can Bind directly to this property, let's call it Items:

<TreeView ItemsSource="{Binding Items}" ... />

When you have it set up this way, then all you need to do to empty or reset the TreeView is this (in the view model):

Items = new ObservableCollection<YourItemDataType>();

As for speed, it's hard to know what you're doing, but WPF is known for being slow when rendering large collections. I can't help with that, sorry.