I am trying to make a collectionView display the items in a collection I have created but that doe snot work at all. I have been looking around to understand the issue but I can't figure it out. Can someone help me out ? See below the XAML code for a custom control I created and following it the code behind in c#
'''
<StackLayout xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CustomControls.CustomControl.DataGrid2"
xmlns:local="clr-namespace:CustomControls.CustomControl"
HeightRequest="500"
WidthRequest="500"
Orientation="Horizontal">
<CollectionView x:Name="c2" ItemsLayout="VerticalList" x:DataType="local:DataGrid2" ItemsSource="{Binding Data2see}" >
<CollectionView.Header>
<StackLayout BackgroundColor="LightGray">
<Label Margin="10,0,0,0"
Text="Monkeys"
FontSize="Small"
FontAttributes="Bold" />
</StackLayout>
</CollectionView.Header>
<CollectionView.Footer>
<StackLayout BackgroundColor="LightGray">
<Label Margin="10,0,0,0"
Text="Friends of Xamarin Monkey"
FontSize="Small"
FontAttributes="Bold" />
</StackLayout>
</CollectionView.Footer>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="local:SomeDataExemple2">
<Grid Padding="10" ColumnDefinitions="100,100,100">
<Button Grid.Column="0" Text ="clickmenow2"/>
<Entry Grid.Column= "1" Text="{Binding Name}"/>
<Entry Grid.Column="2" Text="{Binding First_name}"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
namespace CustomControls.CustomControl
{ using System.Collections.Specialized;
using System.Collections.Generic;
using System.Collections;
using System.Collections.ObjectModel;
public partial class DataGrid2 : StackLayout
{
public DataGrid2()
{
Data2see = new Collection<SomeDataExemple2>();
Data2see.Add(new SomeDataExemple2("hamid", "britel"));
Data2see.Add(new SomeDataExemple2("mernissi", "wadie"));
Data2see.Add(new SomeDataExemple2("mhamed", "chraibi"));
Data2see.Add(new SomeDataExemple2("yazid", "alaoui"));
Data2see.Add(new SomeDataExemple2("amine", "gogole"));
Data2see.Add(new SomeDataExemple2("mehdi", "harras"));
InitializeComponent();
//c2.ItemsSource = Data;
}
public Collection<SomeDataExemple2> Data2see
{
get;
set;
}
}
public class SomeDataExemple2
{
public SomeDataExemple2(string name1, string name2)
{
Name = name1;
First_name = name2;
}
public string Name
{
get; set;
}
public string First_name
{
get;
set;
}
}}
.xaml code, you can use ContentPage instead of StackLayout
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp_test.MainPage"
xmlns:local="clr-namespace:MauiApp_test"
HeightRequest="500"
WidthRequest="500">
<CollectionView x:Name="c2" ItemsLayout="VerticalList" >
<CollectionView.Header>
<StackLayout BackgroundColor="LightGray">
<Label Margin="10,0,0,0"
Text="Monkeys"
FontSize="Small"
FontAttributes="Bold" />
</StackLayout>
</CollectionView.Header>
<CollectionView.Footer>
<StackLayout BackgroundColor="LightGray">
<Label Margin="10,0,0,0"
Text="Friends of Xamarin Monkey"
FontSize="Small"
FontAttributes="Bold" />
</StackLayout>
</CollectionView.Footer>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10" ColumnDefinitions="100,100,100">
<Button Grid.Column="0" Text ="clickmenow2"/>
<Entry Grid.Column= "1" Text="{Binding Name}"/>
<Entry Grid.Column="2" Text="{Binding First_name}"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
.xaml.cs
using System.Collections.ObjectModel;
namespace MauiApp_test;
public partial class MainPage : ContentPage
{
public Collection<SomeDataExemple2> Data2see
{
get;
set;
}
public MainPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
Data2see = new Collection<SomeDataExemple2>();
Data2see.Add(new SomeDataExemple2("hamid", "britel"));
Data2see.Add(new SomeDataExemple2("mernissi", "wadie"));
Data2see.Add(new SomeDataExemple2("mhamed", "chraibi"));
Data2see.Add(new SomeDataExemple2("yazid", "alaoui"));
Data2see.Add(new SomeDataExemple2("amine", "gogole"));
Data2see.Add(new SomeDataExemple2("mehdi", "harras"));
c2.ItemsSource = Data2see
.ToList();
}
public class SomeDataExemple2
{
public SomeDataExemple2(string name1, string name2)
{
Name = name1;
First_name = name2;
}
public string Name
{
get; set;
}
public string First_name
{
get;
set;
}
}
}
Related
I need to make a Multilevel Listview in Xamarin forms.
Currently, I am using this for my Single level Grouping in my Menu Page
http://www.compliancestudio.io/blog/xamarin-forms-expandable-listview
I need to update something like the attached image. I have tried some of the Solution but all in vain.
Anyone has any idea about this.
Thanks
You could do this with Expander of Xamarin Community Toolkit.
Install Xamarin Community Toolkit from NuGet: https://www.nuget.org/packages/Xamarin.CommunityToolkit/
Usage: xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
Xaml:
<ContentPage.BindingContext>
<viewmodels:RootViewModel />
</ContentPage.BindingContext>
<ContentPage.Content>
<ScrollView Margin="20">
<StackLayout BindableLayout.ItemsSource="{Binding roots}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<xct:Expander>
<xct:Expander.Header>
<Label Text="{Binding Root}"
FontAttributes="Bold"
FontSize="Large" />
</xct:Expander.Header>
<StackLayout BindableLayout.ItemsSource="{Binding Node}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<xct:Expander Padding="10">
<xct:Expander.Header>
<Label Text="{Binding Key.Node}" FontSize="Medium" />
</xct:Expander.Header>
<StackLayout BindableLayout.ItemsSource="{Binding Value}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label Text="{Binding SubRoot}" FontSize="Small" />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</xct:Expander>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</xct:Expander>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ScrollView>
</ContentPage.Content>
Model:
public class Roots
{
public string Root { get; set; }
public Dictionary<Nodes, List<SubRoots>> Node { get; set; }
}
public class Nodes
{
public string Node { get; set; }
}
public class SubRoots
{
public string SubRoot { get; set; }
}
ViewModel:
public class RootViewModel
{
public List<Roots> roots { get; private set; }
public RootViewModel()
{
CreateMonkeyCollection();
}
public void CreateMonkeyCollection()
{
List<SubRoots> subRoot = new List<SubRoots>() { new SubRoots() { SubRoot = "SubNode" } };
Dictionary<Nodes, List<SubRoots>> node = new Dictionary<Nodes, List<SubRoots>>();
node.Add(new Nodes() { Node="Nodo1"}, null);
node.Add(new Nodes() { Node="Nodo2"}, null);
node.Add(new Nodes() { Node="Nodo3"}, null);
node.Add(new Nodes() { Node="Nodo4"}, subRoot);
roots = new List<Roots>()
{
new Roots(){ Root="Root1", Node=node},
new Roots(){ Root="Root2", Node= null}
};
}
}
I have this kind of tap command in my carousel view:
<Image
x:Name="img_adPic"
HeightRequest="150"
Aspect="AspectFill"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Source="{Binding thumbnail}">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.TapCommand, Source={x:Reference page}}" CommandParameter="{Binding Path=BindingContext.CurrentIndex, Source={x:Reference page}}"/>
</Image.GestureRecognizers>
</Image>
I gave my contentpage a name :
<ContentPage x:Name="page" >
Now in my class:
public ICommand CarouselItemTapped{ get; set; }
public Page_MainMenuDetail()
{
InitializeComponent();
CarouselItemTapped = new Xamarin.Forms.Command((selectItem) =>
{
});
instance = this;
}
But no matter what I try, the command is NEVER fired. I think i did everything as in the tutorials, why is this not working?
Thank you
You could refer to the code below about how to use the TapCommand. The command would be triggled when you tap on the image.
Xaml:
<ContentPage.Content>
<AbsoluteLayout>
<CarouselView
x:Name="carouselView"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
IndicatorView="indicatorview">
<CarouselView.ItemTemplate>
<DataTemplate>
<Image
x:Name="img_adPic"
Aspect="AspectFill"
HeightRequest="150"
HorizontalOptions="FillAndExpand"
Source="{Binding .}"
VerticalOptions="FillAndExpand">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.TapCommand, Source={x:Reference page}}" />
</Image.GestureRecognizers>
</Image>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<IndicatorView
x:Name="indicatorview"
AbsoluteLayout.LayoutBounds=" 0.5,0.95,100,100"
AbsoluteLayout.LayoutFlags=" PositionProportional"
IndicatorColor=" LightGray"
IndicatorSize=" 10"
SelectedIndicatorColor=" Black" />
</AbsoluteLayout>
</ContentPage.Content>
Code behind:
public partial class Page2 : ContentPage
{
public ICommand CarouselItemTapped { get; set; }
public ICommand TapCommand { get; set; }
public Page2()
{
InitializeComponent();
var list = new List<string>
{
"pink.jpg",
"pink.jpg",
"pink.jpg",
"pink.jpg",
"pink.jpg",
};
carouselView.ItemsSource = list;
//CarouselItemTapped = new Xamarin.Forms.Command((selectItem) =>
//{
//});
//instance = this;
TapCommand = new Command(commandEvent);
this.BindingContext = this;
}
public void commandEvent()
{
}
}
Ok so when i run my app i just get a : System.AggregateException which i tried to spot with the debuguer
and it brings me to this function in the AgendaDatabase.cs file :
public AgendaDatabase(string dbPath)
{
database = new SQLiteAsyncConnection(dbPath);
database.CreateTableAsync<Agenda>().Wait();
}
Before targetting the System.AggregateException with the debugger i also had a SystemAggregateException for this function in AcceuilPage.xaml.cs:
protected override async void OnAppearing()
{
base.OnAppearing();
AgendaCollection.ItemsSource = await App.Database.GetAgendasAsync();
}
and when i execute one more it says : unhandled exception : SQLite.SQLiteException: no such table: Agenda which is weird cause the code is supposed to create it if it doesn't exist.
This is the tutorial i am following : https://learn.microsoft.com/en-us/xamarin/get-started/quickstarts/database?pivots=windows
Thanks for your help.
I've done the steps mutiple time, even listened to similar tutorial with no solution on how to fix it : heres the code :
AgendaDatabase.cs (in Database folder)
using System;
using System.Collections.Generic;
using System.Text;
using SQLite;
using Calculette.Models;
using System.Threading.Tasks;
namespace Calculette.Database
{
public class AgendaDatabase
{
readonly SQLiteAsyncConnection database;
public AgendaDatabase(string dbPath)
{
database = new SQLiteAsyncConnection(dbPath);
database.CreateTableAsync<Agenda>().Wait();
}
public Task<List<Agenda>> GetAgendasAsync()
{
return database.Table<Agenda>().ToListAsync();
}
public Task<Agenda> GetAgendaAsync(int id)
{
return database.Table<Agenda>()
.Where(i => i.ID == id)
.FirstOrDefaultAsync();
}
public Task<int> SaveAgendaAsync(Agenda agenda)
{
if (agenda.ID != 0)
{
return database.UpdateAsync(agenda);
}
else
{
return database.InsertAsync(agenda);
}
}
public Task<int> DeleteAgendaAsync(Agenda agenda)
{
return database.DeleteAsync(agenda);
}
}
}
Agenda.cs in the Models folder
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using SQLite;
using Calculette.Database;
namespace Calculette.Models
{
public class Agenda
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Topic { get; set; }
public string Duration { get; set; }
public DateTime Date { get; set; }
public ObservableCollection<Speaker> Speakers { get; set; }
public string Color { get; set; }
public string Name { get; set; }
public string Time { get; set; }
}
}
AcceuilPage.xaml in the Views folder
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Calculette"
xmlns:pv="clr-namespace:Xamarin.Forms.PancakeView;assembly=Xamarin.Forms.PancakeView"
x:Class="Calculette.MainPage"
BarBackgroundColor = "White"
BarTextColor="#008A00">
<ContentPage Icon="icontache.png" BackgroundColor="#F6F8F9">
<ContentPage.Content>
<!-- ScrollView nous permet d'avoir une page scrollable-->
<ScrollView Orientation="Vertical">
<CollectionView Grid.Row="2" Margin="25" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
SelectionMode="None" x:Name="AgendaCollection">
<CollectionView.Header>
<StackLayout Orientation="Horizontal" Spacing="220">
<Label Text="Agenda" TextColor="Black" FontSize="18"/>
<ImageButton Source="iconplus.png" HeightRequest="30" WidthRequest="30" Clicked="GoToNewFormPage"></ImageButton>
</StackLayout>
</CollectionView.Header>
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="20"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate >
<DataTemplate>
<pv:PancakeView HasShadow="True" BackgroundColor="White" VerticalOptions="StartAndExpand "
HorizontalOptions="FillAndExpand" >
<Grid VerticalOptions="StartAndExpand" HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<BoxView BackgroundColor="{Binding Color}" WidthRequest="3" HorizontalOptions="Start"
VerticalOptions="FillAndExpand"/>
<Expander Grid.Column="1">
<Expander.Header>
<Grid HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3.5*"/>
</Grid.ColumnDefinitions>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<Label Text="{Binding Date, StringFormat='{0:dd}'}" TextColor="#008A00" FontSize="27"
HorizontalOptions="Center"/>
<Label Text="{Binding Date, StringFormat='{0:MMMM}'}" TextColor="Black" FontSize="10"
HorizontalOptions="Center" Margin="0,-10,0,0" FontAttributes="Bold"/>
<ImageButton Source="iconplus.png" HorizontalOptions="Center" HeightRequest="30" WidthRequest="30" Clicked="GoToFormPage"></ImageButton>
</StackLayout>
<BoxView Grid.Column="1" BackgroundColor="#F2F4F8" WidthRequest="1" HorizontalOptions="Start"
VerticalOptions="FillAndExpand"/>
<StackLayout Grid.Column="2" HorizontalOptions="Start" VerticalOptions="Center" Margin="20">
<Label Text="{Binding Topic}" TextColor="#008A00" FontSize="15" FontAttributes="Bold"/>
<Label Text="{Binding Duration}" TextColor="#2F3246" FontSize="12" Margin="0,-10,0,0"/>
</StackLayout>
</Grid>
</Expander.Header>
<Grid HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3.5*"/>
</Grid.ColumnDefinitions>
<BoxView Grid.Column="1" BackgroundColor="#F2F4F8" WidthRequest="1" HorizontalOptions="Start"
VerticalOptions="FillAndExpand"/>
<StackLayout Grid.Column="2" Spacing="10">
<Label Text="Tâches" TextColor="Black" FontSize="15" Margin="20,0"/>
<StackLayout BindableLayout.ItemsSource="{Binding Speakers}" HorizontalOptions="Start" VerticalOptions="Center" Margin="20,0,0,20">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label TextColor="#2F3246" FontSize="12">
<Label.FormattedText>
<FormattedString>
<FormattedString.Spans>
<Span Text="{Binding Time}"/>
<Span Text=" - "/>
<Span Text="{Binding Name}" FontAttributes="Bold"/>
</FormattedString.Spans>
</FormattedString>
</Label.FormattedText>
</Label>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
</Grid>
</Expander>
</Grid>
</pv:PancakeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ScrollView>
</ContentPage.Content>
</ContentPage>
AcceuilPage.xaml.cs
using Calculette.ViewModel;
using Calculette.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.PancakeView;
namespace Calculette
{
public partial class MainPage : TabbedPage
{
public MainPage()
{
InitializeComponent();
this.BindingContext = this;
}
protected async void GoToFormPage(object sender, EventArgs e)
{
await Navigation.PushAsync(new Views.AgendaItemDetailPage());
}
protected async void GoToNewFormPage(object sender, EventArgs e)
{
await Navigation.PushAsync(new Views.NewFormPage());
}
protected override async void OnAppearing()
{
base.OnAppearing();
AgendaCollection.ItemsSource = await App.Database.GetAgendasAsync();
}
}
}
First of all, you must specify database model, because sqlite is not able to create table from your model.
namespace Calculette.Models
{
[Table("Agenda")]
public class Agenda
{
[PrimaryKey, AutoIncrement, Column("ID")]
public int ID { get; set; }
[Column("Topic")]
public string Topic { get; set; }
[Column("Duration")]
public string Duration { get; set; }
//public DateTime Date { get; set; }
//public ObservableCollection<Speaker> Speakers { get; set; }
[Column("Color")]
public string Color { get; set; }
[Column("Name")]
public string Name { get; set; }
[Column("Time")]
public string Time { get; set; }
}
}
Be aware that sqlite does not support
public DateTime Date { get; set; }
public ObservableCollection<Speaker> Speakers { get; set; }
I have a xamarin.forms app which have a listview showing item fetched from firebase. The issue I am facing is well asked question in SO ie; one of my item in listview (an item named location ) only updates when we scroll the listview. I tried to implement the iNotifyPropertychanged but still I am facing the same issue. What is the mistake I am doing here? Any help is appriciated.
My Listview Itemsource setting
var person = await firebaseHelper.GetPerson(EmployeeID);// getting data from firebase
LocationData = person.userdata.Where(x => DateTime.Parse(x.DateTime).ToString("yyyy-MM-dd") == StartDate.Date.ToString("yyyy-MM-dd") && x.Longitude != "initial").ToList();
ObservableCollection<UserLocationData> dynamicLocation = new ObservableCollection<UserLocationData>(LocationData);
TrackingListView.ItemsSource = dynamicLocation;
if (dynamicLocation.Count != 0)
{
foreach (UserLocationData Item in dynamicLocation)
{
Item.DateTime = DateTime.Parse(Item.DateTime).ToString("MMMM dd,yyyy hh:mm tt");
if (!string.IsNullOrEmpty(Item.Latitude) && !string.IsNullOrEmpty(Item.Longitude))
{
var Locations = await Geocoding.GetPlacemarksAsync(Convert.ToDouble(Item.Latitude), Convert.ToDouble(Item.Longitude));
var placemark = Locations?.FirstOrDefault();
if (placemark.Thoroughfare.ToString() != null)
{
var geocodeAddress =
placemark.Thoroughfare + '\n' +
placemark.SubAdminArea + '\n';
Item.Location = geocodeAddress.ToString();
}
else
{
Item.Location = Item.Latitude + "," + Item.Longitude;
}
}
}
My Datamodel
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string Name { get; set; }
public string PersonId { get; set; }
public string Address { get; set; }
public string PhoneNumber { get; set; }
public string EmailID { get; set; }
public string Password { get; set; }
public bool Status { get; set; }
public bool IsAdmin { get; set; }
public List<UserLocationData> userdata { get; set; }
}
public partial class UserLocationData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string Latitude { get; set; }
public string Longitude { get; set; }
public string DateTime { get; set; }
private string _location;
public string Location
{
get
{
return _location;
}
set
{
if (value != null)
{
_location = value;
NotifyPropertyChanged("Selected");
}
}
}
public bool ButtonOn { get; set; }
public bool ButtonOff { get; set; }
}
The Firebase GetPerson portion
public async Task<Person> GetPerson(string personId)
{
var allPersons = await GetAllPersons();
await firebase
.Child("Persons")
.OnceAsync<Person>();
return allPersons.Where(a => a.PersonId == personId).FirstOrDefault();
}
My XAML
<ListView x:Name="TrackingListView" ItemsSource="{Binding} "
HasUnevenRows="True"
IsVisible="False"
CachingStrategy="RecycleElement"
SeparatorVisibility="None"
SelectionMode="None"
BackgroundColor="Transparent"
Margin="10,10,10,10"
HorizontalOptions="FillAndExpand"
>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Frame
Padding="5"
Margin="0,5,0,5"
Opacity="0.9"
BackgroundColor="White"
BorderColor="LightBlue"
HasShadow="True"
CornerRadius="10"
ClassId="{Binding DateTime}"
HorizontalOptions="FillAndExpand">
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"></TapGestureRecognizer>
</Frame.GestureRecognizers>
<Grid Margin="0,10,0,0" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackLayout HorizontalOptions="FillAndExpand" Grid.Row="0" Grid.Column="0" Orientation="Horizontal">
<Image Source="clock.png" HorizontalOptions="Start" VerticalOptions="Start"
HeightRequest="20" Margin="10,0,5,0"
></Image>
<StackLayout Orientation="Vertical" Margin="0" HorizontalOptions="FillAndExpand">
<Label Text="Location fetched time" FontSize="12" HorizontalOptions="StartAndExpand" VerticalOptions="Center"
TextColor="Blue"
></Label>
<Label Text="{Binding DateTime}" HorizontalOptions="StartAndExpand" VerticalOptions="Center"
TextColor="Black" FontSize="Small"
></Label>
</StackLayout>
</StackLayout>
<StackLayout Grid.Column="1" Grid.Row="0" HorizontalOptions="EndAndExpand" VerticalOptions="FillAndExpand">
<Image Source="googlemap.png" HorizontalOptions="End" VerticalOptions="Center" HeightRequest="30" ></Image>
</StackLayout>
<StackLayout HorizontalOptions="FillAndExpand" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal">
<Image Source="currentlocation.png" HorizontalOptions="Start" VerticalOptions="Start"
HeightRequest="20" Margin="10,0,5,0"
></Image>
<StackLayout Orientation="Vertical" Margin="0" HorizontalOptions="FillAndExpand">
<Label Text="Location " FontSize="12" HorizontalOptions="StartAndExpand" VerticalOptions="Center"
TextColor="Blue"
></Label>
<Label Text="{Binding Location}" HorizontalOptions="StartAndExpand" VerticalOptions="Center"
TextColor="Black" FontSize="Small"
></Label>
</StackLayout>
</StackLayout>
<StackLayout Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="0" >
<Grid>
<StackLayout Orientation="Horizontal" Margin="0" IsVisible="{Binding ButtonOn}">
<Image Source="start.png" HorizontalOptions="Start"
HeightRequest="20" Margin="10,0,5,0"
VerticalOptions="Start">
</Image>
<Label Text="Location Tracking Started" VerticalOptions="Center" TextColor="Green" FontSize="12" ></Label>
</StackLayout>
<StackLayout Orientation="Horizontal" Margin="0" IsVisible="{Binding ButtonOff}">
<Image Source="stop.png" HorizontalOptions="Start"
HeightRequest="20" Margin="10,0,5,0"
VerticalOptions="Start">
</Image>
<Label Text="Location Tracking Stopped" VerticalOptions="Center" TextColor="Red" FontSize="12" ></Label>
</StackLayout>
</Grid>
</StackLayout>
</Grid>
</Frame>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
NotifyPropertyChanged("Selected"); it should be NotifyPropertyChanged("Location");
I am trying to implement listview grouping for my following JSON data.
JSON Sample:
{
"cbrainBibleBooksHB":[ {
"book":"2 John",
"cbrainBibleTOList":[
{
"bookName":"2 John",
"chapter":"1",
"pageUrl":"/edu-bible/9005/1/2-john-1"
},
{....}
]
},
{
"book":"3 John",
"cbrainBibleTOList":[
{
"bookName":"3 John",
"chapter":"1",
"pageUrl":"/edu-bible/9007/1/3-john-1"
},
{...}
]
}
]
}
I am trying to group the JSON data by its book name.
I tried like below:
Model:
public class BibleTestament
{
public List<CbrainBibleBooksHB> cbrainBibleBooksHB { get; set; }
}
public class CbrainBibleBooksHB : ObservableCollection<CbrainBibleTOList>
{
public string book { get; set; }
public List<CbrainBibleTOList> cbrainBibleTOList { get; set; }
}
public class CbrainBibleTOList
{
public string chapter { get; set; }
public string pageUrl { get; set; }
public string bookName { get; set; }
}
Viewmodel
HttpClient client = new HttpClient();
var Response = await client.GetAsync("rest api");
if (Response.IsSuccessStatusCode)
{
string response = await Response.Content.ReadAsStringAsync();
Debug.WriteLine("response:>>" + response);
BibleTestament bibleTestament = new BibleTestament();
if (response != "")
{
bibleTestament = JsonConvert.DeserializeObject<BibleTestament>(response.ToString());
}
AllItems = new ObservableCollection<CbrainBibleBooksHB>(bibleTestament.cbrainBibleBooksHB);
XAML
<ContentPage.Content>
<StackLayout>
<ListView
HasUnevenRows="True"
ItemsSource="{Binding AllItems,Mode=TwoWay}"
IsGroupingEnabled="True">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell>
<Label
Text="{Binding book}"
Font="Bold,20"
HorizontalOptions="CenterAndExpand"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
Margin="3"
TextColor="Black"
VerticalOptions="Center"/>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout
HorizontalOptions="StartAndExpand"
VerticalOptions="FillAndExpand"
Orientation="Horizontal">
<Label
Text="{Binding cbrainBibleTOList.chapter}"
Font="20"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
HorizontalOptions="CenterAndExpand"
TextColor="Black"
VerticalOptions="Center"/>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Footer>
<Label/>
</ListView.Footer>
</ListView>
</StackLayout>
</ContentPage.Content>
But no data is showing on the UI when running the project. Getting Binding: 'book' property not found on 'System.Object[]', target property: 'Xamarin.Forms.Label.Text' message on output box. It is very difficult to implement grouping for a listview in xamarin forms. Can anyone help me to do this? I have uploaded a sample project here.
You can use the latest BindableLayout of Xamarin.Forms version >=3.5 instead of using grouped Listview with less effort involved.
Update your Model class
public class CbrainBibleBooksHB
{
public string book { get; set; }
public List<CbrainBibleTOList> cbrainBibleTOList { get; set; }
}
XAML:
<ScrollView>
<FlexLayout
BindableLayout.ItemsSource="{Binding AllItems}"
Direction="Column"
AlignContent="Start">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0"
Text="{Binding book}"
HorizontalOptions="FillAndExpand"
BackgroundColor="LightBlue"/>
<StackLayout Grid.Row="1"
BindableLayout.ItemsSource="{Binding cbrainBibleTOList}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label Text="{Binding chapter}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.TapCommand, Source={x:Reference Name=ParentContentPage}}" CommandParameter="{Binding .}"/>
</Label.GestureRecognizers>
</Label>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</FlexLayout>
</ScrollView>
Note: Here ParentContentPage is the x:Name of parent content page which is used to give reference for command.
ViewModel:
class BibleTestamentViewModel : INotifyPropertyChanged
{
public ICommand TapCommand { get; private set; }
public BibleTestamentViewModel()
{
TapCommand = new Command(ChapterClickedClicked);
}
private void ChapterClickedClicked(object sender)
{
//check value inside sender
}
}
Output:
I tested your demo with static data and there are some issues in your case .
Firstly CbrainBibleBooksHB is a subclass of ObservableCollection ,so you don't need to set the property cbrainBibleTOList any more
public class CbrainBibleBooksHB : ObservableCollection<CbrainBibleTOList>
{
public string book { get; set; }
public List<CbrainBibleTOList> cbrainBibleTOList { get; set; }
}
Secondly , you set the wrong binding path of the label .
<Label
Text="{Binding chapter}"
...
/>
Following is my code ,because of I could not accsess to your url so I used the static data.
in xaml
...
<Label
Text="{Binding chapter}"
HeightRequest="30"
Font="20"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
HorizontalOptions="CenterAndExpand"
TextColor="Black"
VerticalOptions="Center"/>
...
in viewmodel
namespace TestamentSample
{
public class BibleTestamentViewModel
{
public ObservableCollection<CbrainBibleBooksHB> AllItems
{
get;set;
}
public BibleTestamentViewModel()
{
var cbrainBibleBooksHB = new CbrainBibleBooksHB() {book = "group1",};
cbrainBibleBooksHB.Add(new CbrainBibleTOList() { chapter = "1111" });
cbrainBibleBooksHB.Add(new CbrainBibleTOList() { chapter = "2222" });
cbrainBibleBooksHB.Add(new CbrainBibleTOList() { chapter = "3333" });
cbrainBibleBooksHB.Add(new CbrainBibleTOList() { chapter = "4444" });
cbrainBibleBooksHB.Add(new CbrainBibleTOList() { chapter = "5555" });
var cbrainBibleBooksHB2 = new CbrainBibleBooksHB() { book = "group2", };
cbrainBibleBooksHB2.Add(new CbrainBibleTOList() { chapter = "6666" });
cbrainBibleBooksHB2.Add(new CbrainBibleTOList() { chapter = "7777" });
cbrainBibleBooksHB2.Add(new CbrainBibleTOList() { chapter = "8888" });
cbrainBibleBooksHB2.Add(new CbrainBibleTOList() { chapter = "9999" });
cbrainBibleBooksHB2.Add(new CbrainBibleTOList() { chapter = "0000" });
AllItems = new ObservableCollection<CbrainBibleBooksHB>() {
cbrainBibleBooksHB,cbrainBibleBooksHB2
};
}
}
}
public class CbrainBibleBooksHB : ObservableCollection<CbrainBibleTOList>
{
public string book { get; set; }
}
You should make sure that the object which you download from remote url has the same level with my demo .
You can do this way aswell
<ListView ItemsSource="{Binding Itens}" SeparatorColor="#010d47" RowHeight="120" x:Name="lvEmpresaPending" SelectionMode="None">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Nome da Empresa:" FontAttributes="Bold" ></Label>
<Label Text="{Binding Main.Nome}"></Label>
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="CNPJ da Empresa:" FontAttributes="Bold"></Label>
<Label Text="{Binding Main.Cnpj}"></Label>
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Cargo: " FontAttributes="Bold"></Label>
<Label Text="{Binding Cargo}"></Label>
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Inicio" FontAttributes="Bold"></Label>
<Label Text="{Binding DataInicio}"></Label>
<Label Text="Término" FontAttributes="Bold"></Label>
<Label Text="{Binding DataFim}"></Label>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
public class ModelosPPP
{
public Empresa Main { get; set; }
public string DataInicio { get; set; }
public string DataFim { get; set; }
public string Cargo { get; set; }
public string Status { get; set; }
}
public class Empresa
{
public string Nome { get; set; }
public string Cnpj { get; set; }
}