This is a migrated thread and some comments may be shown as answers.

TreeListView with a ListCollectionView is leaking the performance

3 Answers 63 Views
TreeListView
This is a migrated thread and some comments may be shown as answers.
BENN
Top achievements
Rank 1
BENN asked on 22 Aug 2016, 08:18 AM

I took an example for a TreeView where you filter items with a Boolean "IsRemovedByFilter". The example had the items re-created with a copy constructor.

I did the same on a tree list view, and replaced the copy constructor with ListCollectionView.EditItem and CommitEdit.

 

The more times you filter the items, the slower it gets.

 

Here is the code:

MainWindow:

<Window x:Class="TreeListView_Filtering_Issue.MainWindow"
        Title="MainWindow" Height="350" Width="525">
     
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition />
        </Grid.RowDefinitions>
         
        <TextBox Text="{Binding FilterText, UpdateSourceTrigger=PropertyChanged}" />
         
        <telerik:RadTreeListView ItemsSource="{Binding Items}" AutoGenerateColumns="False" Grid.Row="1">
            <telerik:RadTreeListView.ChildTableDefinitions>
                <telerik:TreeListViewTableDefinition ItemsSource="{Binding FilteredChildren}" />
            </telerik:RadTreeListView.ChildTableDefinitions>
             
            <telerik:RadTreeListView.Columns>
                <telerik:GridViewDataColumn DataMemberBinding="{Binding Name}" IsReadOnly="True"/>
            </telerik:RadTreeListView.Columns>
        </telerik:RadTreeListView>
    </Grid>
</Window>

 

The data context is being set by (just a quick and dirty way for this example):

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new MainViewModel();
        }

 

 

The ViewModels are:

public class MainViewModel : ViewModelBase
{
    private ObservableCollection<Person> _persons = new ObservableCollection<Person>();
    private ListCollectionView _items;
    private string _filterText = string.Empty;
 
    public MainViewModel()
    {
        for (int i = 0; i < 100; i++)
        {
            Person person = new Person(null)
            {
                Name = "Test" + i,
            };
 
            _persons.Add(person);
 
            for (int j = 0; j < 20; j++)
            {
                Person child = new Person(person)
                {
                    Name = "Child." + i + "." + j,
                };
 
                person.Children.Add(child);
            }
        }
 
        _items = new ListCollectionView(_persons);
        _items.Filter = nonFilteredChidrenPredicate;
    }
 
    internal static bool nonFilteredChidrenPredicate(object obj)
    {
        if (obj is Person)
        {
            return !(obj as Person).IsRemovedByFilter;
        }
        return false;
    }
 
    public ListCollectionView Items
    {
        get
        {
            return _items;
        }
    }
 
    public string FilterText
    {
        get
        {
            return _filterText;
        }
        set
        {
            if (value == null)
                value = string.Empty;
            _filterText = value;
            FilterItems();
        }
    }
 
    internal void FilterItems()
    {
        Queue<Person> queue = new Queue<Person>();
 
        foreach (var item in _persons)
        {
            queue.Enqueue(item);
        }
        while (queue.Count > 0)
        {
            Person current = queue.Dequeue();
            current.IsRemovedByFilter = false;
 
            if (current.Name.ToUpper().Contains(FilterText.ToUpper()))
            {
                current.IsRemovedByFilter = false;
                //if (!string.IsNullOrEmpty(FilterText))
                //{
                //    current.IsExpanded = true;
 
                //    if (selectedItem == null)
                //        selectedItem = current;
                //}
                // show and expand parent if one child fits with filter
                setParentFilterStatus(current);
            }
            else
            {
                current.IsRemovedByFilter = true;
            }
            // filter tree leaves
            if (current.Children.Count > 0)
            {
                foreach (var item in current.Children)
                {
                    queue.Enqueue(item);
                }
            }
        }
 
        foreach (var root in _persons)
        {
            Items.EditItem(root);
            Items.CommitEdit();
            refreshFilterStatus(root, root.Children);
        }
    }
 
    private void refreshFilterStatus(Person parent, IEnumerable<Person> items)
    {
        foreach (var item in items)
        {
            parent.FilteredChildren.EditItem(item);
            parent.FilteredChildren.CommitEdit();
        }
        foreach (var item in items)
        {
            refreshFilterStatus(item, item.Children);
        }
    }
 
    private void setParentFilterStatus(Person person)
    {
        Person currentPerson = person;
        while (currentPerson.Parent != null)
        {
            currentPerson.Parent.IsRemovedByFilter = false;
 
            //// if a filter is applied, expand parent's nodes
            //if (!string.IsNullOrEmpty(FilterText))
            //    currentPerson.Parent.IsExpanded = true;
 
            currentPerson = currentPerson.Parent;
        }
    }
}
 
public class Person : ViewModelBase
{
    public string Name { get; set; }
    public bool IsRemovedByFilter { get; set; }
    public Person Parent { get; private set; }
    public ObservableCollection<Person> Children { get; private set; }
    public ListCollectionView FilteredChildren { get; private set; }
 
    public Person(Person parent)
    {
        Children = new ObservableCollection<Person>();
        FilteredChildren = new ListCollectionView(Children);
    }
}

 

 

Run the example, and type the letter z. The filtering is fast. Delete the z, it now took a little more time.

Repeat this procedure few more times. You should notice that it becomes slower and slower each time (my guess would be event handlers leaking, but I didn't confirm it).

 

With dotTrace I can see that when the first z was typed, OnSourceViewColelctionChanged was called 5150 times.

The the z was deleted, OnSourceViewColelctionChanged was called 15150 times.

After repeating it 5 more times, OnSourceViewColelctionChanged was called 105150 times when the z was typed, and  115150 times when the z was deleted.

 

 

 

Currently, it is much faster (also due to virtualization) to just refresh the entire ListCollectionView

3 Answers, 1 is accepted

Sort by
0
Ivan Ivanov
Telerik team
answered on 25 Aug 2016, 08:16 AM
Hi,

Thank you for reporting the issue to us. I manage to reproduce it on our side. It is now logged in our system and here is the public item in our feedback portal, which you can follow to get update about our progress with it. I am adding 1000 Telerik points to your account, in accordance to this report.

Regards,
Ivan Ivanov
Telerik by Progress
Do you need help with upgrading your AJAX, WPF or WinForms project? Check the Telerik API Analyzer and share your thoughts.
0
BENN
Top achievements
Rank 1
answered on 25 Aug 2016, 08:14 PM

Thanks.

 

Btw, I'm unable to access the link you gave me for the public portal. When trying to browse:

http://tpdogfood.telerik.com/view#item/199578

 

I get:

Server Error in Application "TPPORTAL.TELERIK.COM"
Internet Information Services 7.5
Error Summary
HTTP Error 403.6 - Forbidden
The IP address from which you are browsing is not permitted to access the requested Web site.
Detailed Error Information
Module IpRestrictionModule
Notification BeginRequest
Handler ExtensionlessUrlHandler-Integrated-4.0
Error Code 0x80070005
Requested URL http://tpdogfood.telerik.com:80/view
Physical Path C:\Telerik\WebSites\tpportal.telerik.com\view
Logon Method Not yet determined
Logon User Not yet determined

Most likely causes:
The server, site, application, or page requested has explicitly denied the IP address of the client computer.
Things you can try:
Verify the IP and domain restrictions in IIS Manager.
Remove the IP restrictions from the configuration/system.webServer/security/ipSecurity section of the configuration file for the server, site, application, or page.

 

 

I also tried browsing the link from a web proxy, but no avail.

 

0
Ivan Ivanov
Telerik team
answered on 26 Aug 2016, 02:31 PM
Hello,

Please excuse me. It is my bad. Here is the correct link to the public portal.

Regards,
Ivan Ivanov
Telerik by Progress
Do you need help with upgrading your AJAX, WPF or WinForms project? Check the Telerik API Analyzer and share your thoughts.
Tags
TreeListView
Asked by
BENN
Top achievements
Rank 1
Answers by
Ivan Ivanov
Telerik team
BENN
Top achievements
Rank 1
Share this question
or