Searching in grouped ListView in Xamarin.Forms

Background

I followed this guide to create a grouped ListView in Xamarin.Forms. Doing so, the following Property is given which is bound as the ItemSource of the ListView:

DevicesGrouped = new ObservableCollection<Grouping<string, Device>>(sorted);

Everything works just fine, including the grouping functionality.

To search (and thereby filter) the entries in the list, I have tried to implement this pattern:

this.ItemsSource = DevicesGrouped
        .Where (x => x.Name.ToLower ()
        .Contains (filter.ToLower ()));

The issue

The problem is, that I do not have access to the properties of the Device as the objects reside in Groupings. I can only access the Key of the DevicesGrouped which, in this case, is a Manufacturer, where I want to search for the Name. The problem in searching for the Key is also, that the ListView scrolls to the position of the Key and thus hides the elements in the ListView behind the grouping header.

My question is thus, how would one access the properties of the ObservableCollection which is Grouped?

I have tried keeping a List which I use when I filter, but doing so, the app crashes, since Groupings are enabled on the ListView itself.

Thanks for the help in advance.

Best Answer

Answers

  • KimNieblingKimNiebling DKMember
    edited June 2015

    I found a somewhat suitable solution for my own question:

    _groupedDeviceList.ItemsSource = DevicesGrouped.Where(o => o.Any(p => p.Name.ToLower().Contains(filter.ToLower())));

    Where _groupedDeviceList is the ListView.

    Ofcourse, if anyone else has a better solution, I am all ears.

    There is although an issue concerning the layout of the elements in the ListView itself. At first, once the ListView has been filtered, the first element in the ListView is placed behind the SearchBar. Once I scroll up, I see the element.

    Any ideas?

  • KimNieblingKimNiebling DKMember

    @DanielLuberda Yeah, your solution would indeed also work just as fine, but might as well use LINQ to reduce nesting as I wrote in my update.

    I although unfortunately found an issue concerning the layout of the elements in the ListView itself. At first, once the ListView has been filtered (after you search) the first element in the ListView is placed behind the SearchBar. Once I scroll up, I see the element. I think this is due to the Grouping.

    Any idea?

  • hvaughanhvaughan USMember ✭✭✭
    edited October 26

    @KimNiebling said:
    I found a somewhat suitable solution for my own question:

    _groupedDeviceList.ItemsSource = DevicesGrouped.Where(o => o.Any(p => p.Name.ToLower().Contains(filter.ToLower())));

    Where _groupedDeviceList is the ListView.

    Ofcourse, if anyone else has a better solution, I am all ears.

    There is although an issue concerning the layout of the elements in the ListView itself. At first, once the ListView has been filtered, the first element in the ListView is placed behind the SearchBar. Once I scroll up, I see the element.

    Any ideas?

    The LINQ code above will actually include all items in a group if at least one of the items in that group has a name that contains the filter value. So if you have:

    Group 1 Object

    • Item 1 Object (Name = "Blah")
    • Item 2 Object (Name = "Bleh")

    And you search for the letter e, you would get back both Item 1 and Item 2 because at least one item in Group 1 came back true.

    For me, I only wanted items that matched so I used 2 foreach loops and created a new grouped groups collection (I am sure there is some LINQ way to do this):

            List<ListViewGroupedItems<BaseClaimGroup>> newGroupedGroups = new 
            List<ListViewGroupedItems<BaseClaimGroup>>();
    
            foreach(ListViewGroupedItems<BaseClaimGroup> group in ViewModel.OriginalGroupedGroups) {
                ListViewGroupedItems<BaseClaimGroup> filteredGroup = new ListViewGroupedItems<BaseClaimGroup> {
                    ShortName = group.ShortName,
                    LongName  = group.LongName
                };
    
                foreach(BaseClaimGroup costCenter in group) {
                    if(costCenter.Name.ToLowerInvariant().Contains(filter.ToLowerInvariant())) {
                        filteredGroup.Add(costCenter);
                    }
                }
    
                if(filteredGroup.IsNotNullOrEmpty()) {
                    newGroupedGroups.Add(filteredGroup);
                }
            }
    
Sign In or Register to comment.