RadListView group, sort, filter descriptors

10 posts, 0 answers
  1. Jonathan
    Jonathan avatar
    7 posts
    Member since:
    Nov 2016

    Posted 21 Jan Link to this post

    Hi,

     

    I'm having a hard time wrapping my head around how to handle list view group, sort and filter descriptors in an MVVM fashion.

    My scenario is pretty simple: change the grouping, sorting or filtering rules of a list view by clicking a button.

    Like so: Button => click => issue View Model command => affect change to the grouping, sorting, filtering rules owned by the view model that are supposedly bound to the list view

     

    But here are some problems:

    - The descriptors cannot be bound.

    - The descriptors are read-only properties of type ObservableCollection, which means that they are owned by the RadListView instance. The caller cannot set an ObservableCollection owned by the view model.

    So here are a couple of ideas I have been playing around with, both pretty bad:

    - So one can write descriptors with bound properties to the view model (e.g. the sorting order), and force the list view to reload from the view model (by setting the ItemsSource to a new collection instance of the same items via a binding, which is ugly and causes flickering if the item count is high but couldn't find any other way).

    - Or, I'll say presumably because I haven't tested that assumption, modify the descriptors' ObservableCollection on the list view (e.g. remove an alphabetical ascending sort descriptor from the collection and add a descending sort descriptor). This means having a reference to the list around when such change is needed. To avoid coupling the view and the view model, this could happen in the parent view, or page, and be isolated from the view model. Which is not great for reusing those features in different views with the same view model...

     

    Does anyone have real life experience on that matter and could enlighten us on how to approach this?

     

    And question for the Telerik staff: Why aren't the descriptor properties bindable and/or settable? Are they planned to be bindable and/or settable at some point?

     

    Thanks in advance,

    Jonathan

  2. Jonathan
    Jonathan avatar
    7 posts
    Member since:
    Nov 2016

    Posted 21 Jan in reply to Jonathan Link to this post

    Some interesting findings on a ~400 items list while changing the grouping (or sorting):

    - NOT changing the ItemsSource BUT changing the descriptors (clear the descriptors collection then add a new descriptor) = huge lag

    - NOT changing the descriptors (using delegates to pick the right property to group or sort by) BUT changing the ItemsSource (just to trigger the descriptors to be re-evaluated, it's a new instance but the items are the same) = totally smooth

  3. Tsvyatko
    Admin
    Tsvyatko avatar
    857 posts

    Posted 25 Jan Link to this post

    Hi Jonathan,

    Thank you for contacting us!

    Indeed, currently Listview descriptors are not bindable properties which makes them hard to be consumed directly in the viewmodel. Currently, it can be achieved by using attached behavior by passing its instance from the listview into the viewmodel.

    We do have plans to make these properties bindable for easier usage from viewmodel directly. I have logged this as a public feedback item, so you can track its progress - https://feedback.telerik.com/Project/168/Feedback/Details/211065-listview-make-descriptors-collection-bindable-properties.

    Regarding the performance issue reported - would it be possible to share come additional information regarding the setup as well as the platform on which you encounter this problem.

    Regards,
    Tsvyatko
    Telerik by Progress
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
  4. Jonathan
    Jonathan avatar
    7 posts
    Member since:
    Nov 2016

    Posted 30 Jan in reply to Tsvyatko Link to this post

    Hi Tsvyatko,

     

    Thanks for opening a public feedback item!

     

    The performance issue is most apparent on Android. I benchmarked the number of calls made to the sorting and grouping descriptors' extractor and comparer methods on Android and iPhone with the same collection containing 428 objects.

    This is the result when changing the grouping descriptors; actually, an instance of the same delegate descriptor is inserted in the collection:

    - iOS:

        - sort comparer: 3249

        - group extractor: 427

    - Android (a Moto X will hang for about 10 seconds with a debug build):

        - sort comparer: 12707
        - group extractor: 1015

    So ~4x the hit the amount of work!

    Removing the grouping on the Android build reduces the hit by half.

    Also, I noticed that:

    - making the sort comparer only return 0 has little to no effect on the performance hit.

    - clearing the descriptors' collection before adding a new one ends up in twice the amount of operations or so.

    - modifying any of the descriptor collections makes all the descriptors to be re-evaluated (the old descriptor about to be removed is also evaluated)

     

    Is there a preferred way to deal with descriptor? Is there a tutorial or an example showing best practices?

    Let me know if I can help in anyway, this is really a show stopper.

     

    Best regards,

    Jonathan

  5. Jonathan
    Jonathan avatar
    7 posts
    Member since:
    Nov 2016

    Posted 31 Jan in reply to Jonathan Link to this post

    Hi,

     

    I've set up a minimal test project that can be downloaded here (for the next 15 days): https://we.tl/JcFRUfNFpF

    It highlights the issues I've been describing.

     

    Best regards,

    Jonathan

  6. Jonathan
    Jonathan avatar
    7 posts
    Member since:
    Nov 2016

    Posted 01 Feb in reply to Jonathan Link to this post

    Hi,

     

    Further testing seems to indicate that the fastest way to refresh the list view (forcing it to re-evaluate the descriptors) is to assign the bound items source to a new instance, which would end up in something like this:

    Items = new ObservableCollection<Item>(Items);

     

    Using MVVM Light, I would have expected raising a property changed event to do the trick:

    RaisePropertyChanged(nameof(ViewModel.Items));

    The event is received when listening to changes, but the list view is not refreshed...

     

    I've updated the previous sample project with MVVM Light and Realm (download will work for the next 15 days): https://we.tl/RQ7Mk3YWtV

     

    Best regards,

    Jonathan

  7. Petar Marchev
    Admin
    Petar Marchev avatar
    996 posts

    Posted 02 Feb Link to this post

    Hello Jonathan,

    Thank you for the attached project. I was able to test your project, and create my own project too, only to confirm that this scenario is suspiciously slow in Android. I do not think that the hit count itself is a problem, because using the sorting from System.Linq gives me a higher hit count. However System.Linq sorting is super fast for this, and a larger, data set, which suggests that the performance in Android can be improved. I will log this so that we can investigate in detail.

    I created a feedback item which you can vote for and track its status. I have updated your Telerik points as a thank you for letting us know of this issue and helping us improve our product.

    At the moment we cannot suggest a better approach. Let us know if you have other questions.

    Regards,
    Petar Marchev
    Telerik by Progress
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
  8. Jonathan
    Jonathan avatar
    7 posts
    Member since:
    Nov 2016

    Posted 02 Feb in reply to Petar Marchev Link to this post

    Hi Petar,

     

    Thanks for creating a feedback item for the performance issue.

     

    Back to my previous question:

    Further testing seems to indicate that the fastest way to refresh the list view (forcing it to re-evaluate the descriptors) is to assign the bound items source to a new instance, which would end up in something like this:
    Items = new ObservableCollection<Item>(Items);

    Using MVVM Light, I would have expected raising a property changed event to do the trick:
    RaisePropertyChanged(nameof(ViewModel.Items));
    The event is received when listening to changes, but the list view is not refreshed...

     

    Best regards,

    Jonathan

  9. Petar Marchev
    Admin
    Petar Marchev avatar
    996 posts

    Posted 03 Feb Link to this post

    Hi Jonathan,

    I don't see a problem with the scenario you propose. When invoking the PropertyChanged notification, you are essentially saying that this property changed and anybody interested in this change should update accordingly. However, in reality your property value did not change, and the framework detects this, and since there has been no change - the PropertyChanged chain is not  triggered.

    Consider this custom view:
    public class CustomView : View
    {
     public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(
      nameof(ItemsSource),
      typeof(IEnumerable),
      typeof(CustomView),
      null,
      BindingMode.OneWay,
      null,
      OnItemsSourcePropertyChanged);
     
     public IEnumerable ItemsSource
     {
      get
      {
       return (IEnumerable)this.GetValue(ItemsSourceProperty);
      }
      set
      {
       this.SetValue(ItemsSourceProperty, value);
      }
     }
      
     private static void OnItemsSourcePropertyChanged(BindableObject bindable, object oldValue, object newValue)
     {
     }
    }

    You can now use it the same way you are using the listview:
    var customView = new CustomView
    {
     BindingContext = vm,
    };
     
    listView.SetBinding(RadListView.ItemsSourceProperty, new Binding(nameof(ViewModel.Source)));
    customView.SetBinding(CustomView.ItemsSourceProperty, new Binding(nameof(ViewModel.Source)));

    Place a break point in the OnItemsSourceChanged handler and see that the handler is not invoked upon raising the property changed notification.

    Even if the handler was invoked, this does not guarantee an update, because often we use a specific object to handle the items, something like a CustomItemsSourceProxy. And in the previously mentioned OnItemsSourcePropertyChanged handler, the code looks like this:
    private static void OnItemsSourcePropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
     RadVisual v = (RadVisual)bindable;
     v.itemsSourceProxy.ItemsSource = newValue;
    }

    So again here the proxy will not detect a change in its ItemsSource and no action will be triggered. I hope this explanation helps.

    Regards,
    Petar Marchev
    Telerik by Progress
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
  10. Jonathan
    Jonathan avatar
    7 posts
    Member since:
    Nov 2016

    Posted 03 Feb in reply to Petar Marchev Link to this post

    Hi Petar,

     

    Thank you very much for the clarification.

     

    Best regards,

    Jonathan

Back to Top