Home > Enterprise >  How to create Listview with accordion in xamarin form?
How to create Listview with accordion in xamarin form?

Time:12-17

I am using xamarin.form. I want to create Listview with expand and collapse.

Example:

enter image description here

Please send me your suggestion.

CodePudding user response:

 <Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Padding="20">             
        <Grid.RowDefinitions>                       
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <control:FrameView Grid.Row="0" Text="Popular Music Instrument">
            <control:FrameView.ContainerContent>
                <StackLayout>
                    <Label Text="1. Piano/Keyboard" TextColor="Black"></Label>
                    <Label Text="2. Guitar" TextColor="Black"></Label>
                    <Label Text="3. Violin" TextColor="Black"></Label>
                    <Label Text="4. Drums" TextColor="Black"></Label>
                    <Label Text="5. Saxophone" TextColor="Black"></Label>
                </StackLayout>
            </control:FrameView.ContainerContent>
        </control:FrameView>
        
        <control:FrameView Grid.Row="1" Text="Gender">
            <control:FrameView.ContainerContent>
                <StackLayout>
                    <Label Text="1. Male" TextColor="Black"></Label>
                    <Label Text="2. Female" TextColor="Black"></Label>
                    <Label Text="3. Transgender" TextColor="Black"></Label>
                </StackLayout>
            </control:FrameView.ContainerContent>
        </control:FrameView>

        <control:FrameView Grid.Row="2" Text="Detail">
            <control:FrameView.ContainerContent>
                <StackLayout>
                    <Label Text="Lorem Ipsum is simply dummy text of the printing and typesetting industry. 
                           Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, 
                           when an unknown printer took a galley of type and scrambled it to make a type 
                           specimen book." TextColor="Black"></Label>
                </StackLayout>
            </control:FrameView.ContainerContent>
        </control:FrameView>
    </Grid>

Please review this link:

https://xamarinuidesigns.blogspot.com/2021/12/accordion-ui-designs-part-1.html

CodePudding user response:

We can create a complex Expandable ListView with a sub ListView in Xamarin.Forms.. This Expandable List allows user to Expand and Collapse items via a simple click.

Suppose we have a list of hotels and each hotel contains a list of rooms. One click on a hotel will display its rooms while a double click will collapse it.

We can create the following folders :

  • Models
  • Views
  • ViewModels

1.Create Models

Create a new Class Hotel.cs

public class Hotel
{
    public string Name { get; set; }

    public List<Room> Rooms { get; set; }

    public bool IsVisible { get; set; } = false;

    public Hotel()
    {
    }

    public Hotel(string name, List<Room> rooms)
    {
        Name = name;
        Rooms = rooms;
    }
}

Create a new Class Room.cs

    public class Room
    {
        public string RoomName { get; set; }
        public int TypeID { get; set; }

        public Room()
        {

        }

        public Room(string name, int typeID)
        {
            RoomName = name;
            TypeID = typeID;
        }
    }
  }

2.Create ViewModels

BaseViewModel.cs

public class BaseViewModel: INotifyPropertyChanged
{
    bool isBusy = false;
    public bool IsBusy
    {
        get { return isBusy; }
        set { SetProperty(ref isBusy, value); }
    }
    bool isEmpty = false;
    public bool IsEmpty
    {
        get { return isEmpty; }
        set
        {
            isEmpty = value;
            OnEmptyChanged(this, new PropertyChangedEventArgs("IsEmpty"));
        }
    }

    private void OnEmptyChanged(BaseViewModel baseViewModel, PropertyChangedEventArgs propertyChangedEventArgs)
    {
         CrossToastPopUp.Current.ShowToastMessage("No Data Found");

    }

    string busyText = string.Empty;
    string title = string.Empty;



    public string Title
    {
        get => title;
        set => SetProperty(ref title, value);
    }

    public string BusyText
    {
        get => busyText;
        set => SetProperty(ref busyText, value);
    }

    protected bool SetProperty<T>(ref T backingStore, T value,
        [CallerMemberName] string propertyName = "",
        Action onChanged = null)
    {
        if (EqualityComparer<T>.Default.Equals(backingStore, value))
            return false;

        backingStore = value;
        onChanged?.Invoke();
        OnPropertyChanged(propertyName);
        return true;
    }

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        var changed = PropertyChanged;
        if (changed == null)
            return;

        changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

HotelsGroupViewModel.cs

public class HotelsGroupViewModel: BaseViewModel
{
    private HotelViewModel _oldHotel;

    private ObservableCollection<HotelViewModel> items;
    public ObservableCollection<HotelViewModel> Items
    {
        get => items;

        set => SetProperty(ref items, value);
    }

    public Command LoadHotelsCommand { get; set; }
    public Command<HotelViewModel> RefreshItemsCommand { get; set; }

    public HotelsGroupViewModel()
    {
        items = new ObservableCollection<HotelViewModel>();
        Items = new ObservableCollection<HotelViewModel>();
        LoadHotelsCommand = new Command(async () => await ExecuteLoadItemsCommandAsync());
        RefreshItemsCommand = new Command<HotelViewModel>((item) => ExecuteRefreshItemsCommand(item));
    }

    public bool isExpanded = false;
    private void ExecuteRefreshItemsCommand(HotelViewModel item)
    {
        if (_oldHotel == item)
        {
            // click twice on the same item will hide it
            item.Expanded = !item.Expanded;
        }
        else
        {
            if (_oldHotel != null)
            {
                // hide previous selected item
                _oldHotel.Expanded = false;
            }
            // show selected item
            item.Expanded = true;
        }

        _oldHotel = item;
    }
    async System.Threading.Tasks.Task ExecuteLoadItemsCommandAsync()
    {
        try
        {
            if (IsBusy)
                return;
            IsBusy = true;
            Items.Clear();
            List<Room> Hotel1rooms = new List<Room>() { new Room("Jasmine", 1), new Room("Flower Suite", 2), new Room("narcissus", 1)
            };
            List<Room> Hotel2rooms = new List<Room>()
            {
                new Room("Princess", 1), new Room("Royale", 1), new Room("Queen", 1)
            };
            List<Room> Hotel3rooms = new List<Room>()
            {
                new Room("Marhaba", 1), new Room("Marhaba Salem", 1), new Room("Salem Royal", 1), new Room("Wedding Roome", 1), new Room("Wedding Suite", 2)
            };
            List<Hotel> items = new List<Hotel>() { new Hotel("Yasmine Hammamet", Hotel1rooms), new Hotel("El Mouradi Hammamet,", Hotel2rooms), new Hotel("Marhaba Royal Salem", Hotel3rooms) };

            if (items != null && items.Count > 0)
            {
                foreach (var hotel in items)
                    Items.Add(new HotelViewModel(hotel));
            }
            else { IsEmpty = true; }

        }
        catch (Exception ex)
        {
            IsBusy = false;
            Debug.WriteLine(ex);
        }
        finally
        {
            IsBusy = false;
        }
    }
}

HotelViewModel.cs

   public  class HotelViewModel: ObservableRangeCollection<RoomViewModel>, INotifyPropertyChanged
    {
        // It's a backup variable for storing CountryViewModel objects
        private ObservableRangeCollection<RoomViewModel> hotelRooms = new ObservableRangeCollection<RoomViewModel>();

        public HotelViewModel(Hotel hotel, bool expanded = false)
        {
            this.Hotel = hotel;
            this._expanded = expanded;

            foreach (Room room in hotel.Rooms)
            {
                hotelRooms.Add(new RoomViewModel(room));
            }
            if (expanded)
                this.AddRange(hotelRooms);

        }

        public HotelViewModel()
        {
        }

        private bool _expanded;
        public bool Expanded
        {
            get { return _expanded; }
            set
            {
                if (_expanded != value)
                {
                    _expanded = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("Expanded"));
                    OnPropertyChanged(new PropertyChangedEventArgs("StateIcon"));
                    if (_expanded)
                    {
                        this.AddRange(hotelRooms);
                    }
                    else
                    {
                        this.Clear();
                    }
                }
            }
        }

        public string StateIcon
        {
            get
            {
                if (Expanded)
                {
                    return "arrow_a.png";
                }
                else
                { return "arrow_b.png"; }
            }
        }
        public string Name { get { return Hotel.Name; } }
        public Hotel Hotel { get; set; }
    }

RoomViewModel.cs

public class RoomViewModel
{
    private Room _room;

    public RoomViewModel(Room room)
    {
        this._room = room;
    }

    public string RoomName { get { return _room.RoomName; } }
    public int TypeID { get { return _room.TypeID; } }

    public Room Room
    {
        get => _room;
    }
}

3.Create Page (Hotels)

Hotels.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Name="currentPage"
             xmlns:local="clr-namespace:ListViewWithSubListView.Views"
             x:Class="ListViewWithSubListView.Views.Hotels">
    <ContentPage.Content>
        <Grid >
          
            <StackLayout x:Name="hotelStack" Padding="1,0,1,0" >
                <ListView
                    x:Name="HotelsList"
                    BackgroundColor="White"
                    IsGroupingEnabled="True"
                    IsPullToRefreshEnabled="true"
                    IsRefreshing="{Binding IsBusy, Mode=OneWay}"
                    ItemsSource="{Binding Items}"
                    RefreshCommand="{Binding LoadHotelsCommand}"
                    >
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <ViewCell>
                                <StackLayout Orientation="Horizontal"  VerticalOptions="Center">
                                        <Label
                                            VerticalOptions="Center"
                                            FontAttributes="Bold"
                                            FontSize="Medium"
                                            Text="{Binding RoomName}"
                                            TextColor="Black"
                                            VerticalTextAlignment="Center" />                                  
                                </StackLayout>                                   
                            </ViewCell>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                    <ListView.GroupHeaderTemplate>
                        <DataTemplate>
                            <ViewCell>
                               <Grid >
                                   <Label
                                                FontAttributes="Bold"
                                                FontSize="Small"
                                                Text="{Binding Name}"
                                                TextColor="Gray"
                                                VerticalTextAlignment="Center" />
                                    <Image x:Name="ImgA" Source="{Binding StateIcon}"  Margin="0,0,5,0" HeightRequest="20" WidthRequest="20" HorizontalOptions="End"/>
                                    <Grid.GestureRecognizers>
                                        <TapGestureRecognizer Command="{Binding Source={x:Reference currentPage}, Path=BindingContext.RefreshItemsCommand}"  NumberOfTapsRequired="1" CommandParameter="{Binding .}"/>
                                    </Grid.GestureRecognizers>
                                </Grid>                               
                            </ViewCell>
                        </DataTemplate>
                    </ListView.GroupHeaderTemplate>
                </ListView>
            </StackLayout>
        </Grid>
    </ContentPage.Content>   
</ContentPage>

Hotels.xaml.cs

public partial class Hotels : ContentPage
{

    private HotelsGroupViewModel ViewModel
    {
        get { return (HotelsGroupViewModel)BindingContext; }
        set { BindingContext = value; }
    }

    private List<Hotels> ListHotel = new List<Hotels>();

    protected override void OnAppearing()
    {
        try
        {
            base.OnAppearing();

            if (ViewModel.Items.Count == 0)
            {
                ViewModel.LoadHotelsCommand.Execute(null);
            }           
        }
        catch (Exception Ex)
        {
            Debug.WriteLine(Ex.Message);
        }
    }

    public Hotels(HotelsGroupViewModel viewModel)
    {
        InitializeComponent();
        this.ViewModel = viewModel;           
    }   
}

And in App.xaml.cs

MainPage = new Hotels(new HotelsGroupViewModel());

Note:

Refer: https://www.c-sharpcorner.com/article/xamarin-forms-expandable-listview-with-a-sub-listview-mvvm-pattern/

  • Related