Grouped StackLayout In Xamarin Form
The benefit of StackLayout Binding Compare to ListView Binding:
- No need to worry about setting the height of stack layout (It automatically set the height as per content).
- You can use the Stack Layout inside ScrollView.
- As StackLayout does not have an inbuilt scrolling mechanism, So no need to worry about disabling the Scrolling.
Design Side
MainPage.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" xmlns:d="http://xamarin.com/schemas/2014/forms/design" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="TestMobileApp.MainPage"> <StackLayout Margin="10" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"> <ScrollView> <!-- Place new controls here --> <StackLayout BindableLayout.ItemsSource="{Binding Employees}"> <BindableLayout.ItemTemplate> <DataTemplate> <StackLayout> <!-- bind the header info--> <StackLayout> <Label Text="{Binding Header}" FontSize="15" FontAttributes="Bold" /> </StackLayout> <!-- bind the details--> <StackLayout BindableLayout.ItemsSource="{Binding .}"> <BindableLayout.ItemTemplate> <DataTemplate> <Label Text="{Binding FullName}" /> </DataTemplate> </BindableLayout.ItemTemplate> </StackLayout> <BoxView HeightRequest="1" BackgroundColor="Gray" /> </StackLayout> </DataTemplate> </BindableLayout.ItemTemplate> </StackLayout> </ScrollView> </StackLayout> </ContentPage>
Code Behind:
MainPage.Xaml.cs
using System.ComponentModel; using TestMobileApp.ViewModel; using Xamarin.Forms; namespace TestMobileApp { // Learn more about making custom code visible in the Xamarin.Forms previewer // by visiting https://aka.ms/xamarinforms-previewer [DesignTimeVisible(false)] public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); //Set the ViewModel Reference here this.BindingContext = new MainPageViewModel(); } } }
Note: Create The EmployeeHeaderInfo Class that inherit the EployeeDetails
Models:
EmployeeHeaderInfo.cs
using System.Collections.ObjectModel; namespace TestMobileApp.Model { public class EmployeeHeaderInfo : ObservableCollection<EmployeeDetails> { public string Header { get; set; } } }
EmployeeDetails.cs
namespace TestMobileApp.Model { public class EmployeeDetails { public string FirstName { get; set; } public string LastName { get; set; } public string FullName { get { return FirstName + ", " + LastName; } } } }
ViewModel:
MainPageViewModel.cs
using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; using TestMobileApp.Model; namespace TestMobileApp.ViewModel { public class MainPageViewModel : INotifyPropertyChanged { private ObservableCollection<EmployeeHeaderInfo> _employees = new ObservableCollection<EmployeeHeaderInfo>(); public ObservableCollection<EmployeeHeaderInfo> Employees { get => _employees; set { _employees = value; OnPropertyChanged(); } } public MainPageViewModel() { setData(); } private void setData() { //Static data added in Employees Object EmployeeHeaderInfo obj = new EmployeeHeaderInfo(); obj.Header = "A"; obj.Add(new EmployeeDetails { FirstName = "Angelina", LastName = "Johnson" }); obj.Add(new EmployeeDetails { FirstName = "Ann", LastName = "Williams" }); Employees.Add(obj); obj = new EmployeeHeaderInfo(); obj.Header = "C"; obj.Add(new EmployeeDetails { FirstName = "Cliff", LastName = "Jones" }); obj.Add(new EmployeeDetails { FirstName = "Cindy", LastName = "Knots" }); Employees.Add(obj); obj = new EmployeeHeaderInfo(); obj.Header = "E"; obj.Add(new EmployeeDetails { FirstName = "Erica", LastName = "Allen" }); obj.Add(new EmployeeDetails { FirstName = "Emily", LastName = "Kelvin" }); Employees.Add(obj); obj = new EmployeeHeaderInfo(); obj.Header = "G"; obj.Add(new EmployeeDetails { FirstName = "Gleen", LastName = "Menard" }); Employees.Add(obj); obj = new EmployeeHeaderInfo(); obj.Header = "I"; obj.Add(new EmployeeDetails { FirstName = "Ira", LastName = "Sawyer" }); Employees.Add(obj); obj = new EmployeeHeaderInfo(); obj.Header = "J"; obj.Add(new EmployeeDetails { FirstName = "Jennifer", LastName = "Tyler" }); obj.Add(new EmployeeDetails { FirstName = "John", LastName = "Webber" }); obj.Add(new EmployeeDetails { FirstName = "Jane", LastName = "White" }); obj.Add(new EmployeeDetails { FirstName = "Johny", LastName = "Michael" }); obj.Add(new EmployeeDetails { FirstName = "John", LastName = "Allister" }); Employees.Add(obj); obj = new EmployeeHeaderInfo(); obj.Header = "M"; obj.Add(new EmployeeDetails { FirstName = "Melvin", LastName = "Forbis" }); obj.Add(new EmployeeDetails { FirstName = "Margaret", LastName = "Adelman" }); obj.Add(new EmployeeDetails { FirstName = "Marie", LastName = "Broadbet" }); Employees.Add(obj); obj = new EmployeeHeaderInfo(); obj.Header = "P"; obj.Add(new EmployeeDetails { FirstName = "Penny", LastName = "Diaz" }); obj.Add(new EmployeeDetails { FirstName = "Paul", LastName = "Walker" }); obj.Add(new EmployeeDetails { FirstName = "Pete", LastName = "Packett" }); Employees.Add(obj); obj = new EmployeeHeaderInfo(); obj.Header = "R"; obj.Add(new EmployeeDetails { FirstName = "Rick", LastName = "Novak" }); obj.Add(new EmployeeDetails { FirstName = "Roger", LastName = "Lum" }); obj.Add(new EmployeeDetails { FirstName = "Ronald", LastName = "Barr" }); Employees.Add(obj); obj = new EmployeeHeaderInfo(); obj.Header = "Z"; obj.Add(new EmployeeDetails { FirstName = "Zarin", LastName = "Khan" }); Employees.Add(obj); } //OnPropertyChange Event handling public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }
I've been stuck on this problem for a number of days. Spent hours googling until I come across this post. Just want to say thank you. I didn't realise I could do BindableLayout.ItemsSource="{Binding .}" in the nested collection to get the currently being processed item.
ReplyDeleteThank you so much
Delete