Forum Xamarin.Forms

Announcement:

The Xamarin Forums have officially moved to the new Microsoft Q&A experience. Microsoft Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

To create new threads and ask questions head over to Microsoft Q&A for .NET and get involved today.

Xamarin Group ListView Filtered By SearchBar

Hello! I have a xamarin listview working with a search bar. It show everything or filters based on what you type. How can I get the listview to be grouped a well? I would like to group them by state. Members are pulling from a web service and I have a list of States. Thanks in advance.

ListView Xaml

                <ListView x:Name="ListViewMember"
                      ItemsSource="{Binding PublicMembersList}"
                      VerticalOptions="FillAndExpand"
                      HorizontalOptions="FillAndExpand"
                      HasUnevenRows="true"
                      RefreshCommand="{Binding LoadMembersCommand}"
                      IsPullToRefreshEnabled="True"
                      IsRefreshing="{Binding IsBusy, Mode=TwoWay}"
                      CachingStrategy="RecycleElement"
                      ItemSelected="OnMemberSelected"
                      SizeChanged="OnPageSizeChanged"
                      SeparatorVisibility="Default"
                      SeparatorColor="#000000">
                <ListView.Header>
                    <StackLayout>
                        <Label Text="{Binding PublicMembersList.Count, StringFormat='Active Members = {0}'}"></Label>
                    </StackLayout>
                </ListView.Header>
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <ContentView>
                                <Frame>
                                    <StackLayout x:Name="GridMemberList" Orientation="Horizontal">
                                        <BoxView Color="PaleGreen" HeightRequest="25"></BoxView>
                                        <Label Text="{Binding pMember}"></Label>
                                        <Label Text="{Binding Name}"></Label>
                                    </StackLayout>
                                </Frame>
                            </ContentView>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>

ListView Code Behind

        private void OnSearchChanged(object s, TextChangedEventArgs e)
    {

        ListViewMember.BeginRefresh();

        ViewModel.SearchMembersCommand.Execute(null);

        ListViewMember.ItemsSource = ViewModel.FilteredGroupList;

        ListViewMember.EndRefresh();

    }

View Model dealing with the Model

    public class MemberViewModel : BaseViewModel
{
    public int PublicMemberCount => PublicMembersList.Count;

    public ObservableCollection<MemberModel> FilteredGroupList;

    private ObservableCollection<MemberModel> LocalMembersList;

    public ObservableCollection<MemberModel> PublicMembersList
    {
        get
        {
            return LocalMembersList;
        }

        set
        {
            LocalMembersList = value;
            SetProperty(ref LocalMembersList, value);
        }
    }

    private String LocalMemberString;

    public String PublicMemberString
    {
        get
        {
            return LocalMemberString;
        }
        set
        {
            SetProperty(ref LocalMemberString, value);
        }
    }

    public Command LoadMembersCommand { get; set; }

    public Command SearchMembersCommand
    {
        get
        {
            return new Command(() => ExecuteSearchMembersCommand());
        }
    }

    public MemberViewModel()
    {

        Title = "Member Lookup";

        PublicMembersList = new ObservableCollection<MemberModel>();

        LoadMembersCommand = new Command(async () => await ExecuteLoadMembersCommand());

        if (PublicMembersList.Count == 0)
        {
            LoadMembersCommand.Execute(null);
        }

    }

    async Task ExecuteLoadMembersCommand()
    {
        if (IsBusy)
            return;

        IsBusy = true;

        try
        {


            PublicMembersList.Clear();

            var m = await DataSource.GetMembersAsync(true); 

            var d = Services.DataList.GetStates();

            var l = new ObservableCollection<StateModel>(d); 



            foreach (var i in m)
            {

                PublicMembersList.Add(i);
            }

            FilteredGroupList = new ObservableCollection<MemberModel>();

        }
        catch (Exception e)
        {
            Debug.WriteLine("Something - " + e);
        }
        finally
        {
            IsBusy = false;
        }

    }

    private void ExecuteSearchMembersCommand()
    {


        PublicMembersList.GroupBy(c => c.PhysicalState);

        if (string.IsNullOrWhiteSpace(PublicMemberString))
        {

            FilteredGroupList = new ObservableCollection<MemberModel>();

        }
        else
        {

            IEnumerable<MemberModel> f = PublicMembersList.Where(p => p.Name.Contains(PublicMemberString));

            FilteredGroupList = new ObservableCollection<MemberModel>(f);
        }

    }

}

Best Answer

  • AaronBlaylockAaronBlaylock USMember ✭✭
    Accepted Answer

    Got it working with this logic

    Create a group class that inherits an IEnumerable. It only needs one property which is what you want the group name to be.

    public class MemberModelGroup : ObservableCollection
    {
    public string Display{get;set;}
    }

    Change all lists to use IEnumerable. Then when you get your list of MemberModels from the server you need to place them into the correct MemberModelGroup. Each group is an element in UnfilteredList which is of type IEnumerable so you have a list of groups that you can filter using MemberModelGroup.Display.

    var listOfMemberModels = FromServer.GetMemberModels();
    foreach(var memberModel in listOfMemberModels)
    {
    var group = UnfilteredList.FirstOrDefault(el => el.Display == memberModel.StateName);
    if(group == null)
    {
    var newGroup = new MemberModelGroup{ Display = memberModel.StateName };
    newGroup.Add(memberModel);
    UnfilteredList.Add(newGroup);
    }
    else
    {
    group.Add(memberModel);
    }
    }

    Last step is to enable grouping and specify the Binding to use.

Answers

  • JoeMankeJoeManke USMember ✭✭✭✭✭

    To use grouping on a ListView, the ItemsSource must be a list of lists. Here are a couple guides to help you get started.

    https://xamarinhelp.com/xamarin-forms-listview-grouping/
    https://www.c-sharpcorner.com/article/grouping-in-listviews-with-xamarin-forms/

  • jezhjezh Member, Xamarin Team Xamurai
    edited November 2019

    Yes, when we try to use grouping on a ListView, the ItemsSource of listView should be a list of lists just as Joe said .

    I have created a simple demo according to your code, you can refer to it.

    The main code is as follows:

    class MemberModel

       public  class MemberModel
    {
        public string pMember { get; set; }
    
        public string Name { get; set; }
    }
    

    class GroupModel

     public class GroupModel: ObservableCollection<MemberModel>
    {
        public int count { get; set; }
    
        public GroupModel(List<MemberModel> list):base(list)
        {
    
        }
    }
    

    MainPage.xaml

      <StackLayout>
        <ListView
                HasUnevenRows="True"
                x:Name="listView" 
                IsGroupingEnabled = "True">
            <ListView.GroupHeaderTemplate >
                <DataTemplate>
                    <ViewCell Height="40">
                        <StackLayout Orientation="Horizontal"
                                 BackgroundColor="#3498DB"
                                 VerticalOptions="FillAndExpand">
                            <Label Text="{Binding count}"
                               VerticalOptions="Center" />
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.GroupHeaderTemplate>
    
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextCell Text="{Binding Name}" Detail="{Binding pMember}"></TextCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
    

    MainPage.xaml.cs

     public partial class MainPage : ContentPage
    {
        public ObservableCollection<MemberModel> innerItems { get; set; }
        public ObservableCollection<MemberModel> innerItems2 { get; set; }
        public ObservableCollection<MemberModel> innerItems3 { get; set; }
    
        public ObservableCollection<GroupModel> groupItems { get; set; }
    
        public MainPage()
        {
            InitializeComponent();
            listView.ItemsSource = getData();
        }
    
        public List<GroupModel> getData()
        {
    
            innerItems = new ObservableCollection<MemberModel> {
                new MemberModel {Name="Xander", pMember="1"},
                new MemberModel {Name="Rupert", pMember="2"},
                new MemberModel {Name="Tammy", pMember="3"},
                new MemberModel {Name="Blue", pMember="4"},
            };
    
            innerItems2 = new ObservableCollection<MemberModel> {
                new MemberModel {Name="Xander2", pMember="1"},
                new MemberModel {Name="Rupert2", pMember="2"},
                new MemberModel {Name="Tammy2", pMember="3"},
            };
            innerItems3 = new ObservableCollection<MemberModel> {
                new MemberModel {Name="Xander3", pMember="1"},
                new MemberModel {Name="Rupert3", pMember="2"},
    
            };
    
            List<GroupModel> sortedItems = new List<GroupModel>() {
                 new GroupModel(innerItems.ToList()){ count = innerItems.Count },
                 new GroupModel(innerItems2.ToList()){ count = innerItems2.Count },
                 new GroupModel(innerItems3.ToList()){ count = innerItems3.Count },
            };
    
            return sortedItems;
        }
    }
    

    The result is:

  • AaronBlaylockAaronBlaylock USMember ✭✭
    Accepted Answer

    Got it working with this logic

    Create a group class that inherits an IEnumerable. It only needs one property which is what you want the group name to be.

    public class MemberModelGroup : ObservableCollection
    {
    public string Display{get;set;}
    }

    Change all lists to use IEnumerable. Then when you get your list of MemberModels from the server you need to place them into the correct MemberModelGroup. Each group is an element in UnfilteredList which is of type IEnumerable so you have a list of groups that you can filter using MemberModelGroup.Display.

    var listOfMemberModels = FromServer.GetMemberModels();
    foreach(var memberModel in listOfMemberModels)
    {
    var group = UnfilteredList.FirstOrDefault(el => el.Display == memberModel.StateName);
    if(group == null)
    {
    var newGroup = new MemberModelGroup{ Display = memberModel.StateName };
    newGroup.Add(memberModel);
    UnfilteredList.Add(newGroup);
    }
    else
    {
    group.Add(memberModel);
    }
    }

    Last step is to enable grouping and specify the Binding to use.

Sign In or Register to comment.