How to group data list from Preferences in Xamarin - xamarin.forms

I have lists of Order information stored in Preferences as follows:
public class CartUser
{
public int IDProduct { get; set; }
public string NameProduct { get; set; }
public string SupplierID { get; set; }
}
I want to display a list of SupplierID groups, I think of the plan to use Group By
PageOne.xaml.cs
List<CartUser> cartUsers = new List<CartUser>();
var mycart = Preferences.Get("CartUserAdds", "_mycart");
var getcart = JsonConvert.DeserializeObject<List<CartUser>>(mycart).GroupBy(x => x.SupplierID);
cartUsers = (List<CartUser>)getcart;
BindableLayout.SetItemsSource(stdata, cartUsers);
However I get the error: System.InvalidCastException: 'Specified cast is not valid.' right line cartUsers = (List<CartUser>)getcart;
PageOne.xaml
<StackLayout x:Name="stdata">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout x:DataType="model:CartUser">
<Label Text="{Binding SupplierID}"/>
<Label Text="{Binding NameProduct}"/>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
The data I am taken from Preferences:
[{\"IDProduct\":1,\"NameProduct\":\"Name product 1\",\"SupplierID\":\"22379356\"},{\"IDProduct\":2,\"NameProduct\":\"Name product 2\",\"SupplierID\":\"22379356\"},{\"IDProduct\":3,\"NameProduct\":\"Name product 3\",\"SupplierID\":\"12336544\"}]
I want it to display like this
I read this article: How to Group List in Xamarin Forms?. However it sets the display in the ListView. I want it to show up in the StackLayout
Looking forward to a solution from everyone. Thank you!
Update using CollectionView
SupplierIDGrouping.cs
public class SupplierIDGrouping : ObservableCollection<CartUser>
{
public string SupplierID { get; private set; }
public SupplierIDGrouping(string supplierID)
: base()
{
SupplierID = supplierID;
}
public SupplierIDGrouping(string supplierID, IEnumerable<CartUser> source)
: base(source)
{
SupplierID = supplierID;
}
}
PageOne.xaml
<CollectionView ItemsSource="{Binding SupplierList}" IsGrouped="true">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding NameProduct}"/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
PageOne.xaml.cs
public ObservableCollection<SupplierIDGrouping> SupplierList { get; private set; } = new ObservableCollection<SupplierIDGrouping>();
List<CartUser> cartUsers = new List<CartUser>();
var mycart = Preferences.Get("CartUserAdds", "_mycart");
var getcart = JsonConvert.DeserializeObject<List<CartUser>>(mycart);
cartUsers = getcart;
foreach (var item in cartUsers)
{
if (!SupplierList.Any(supplierid => supplierid.SupplierID == item.SupplierID))
{
SupplierList.Add(new SupplierIDGrouping(item.SupplierID));
}
SupplierList.Single(supplierid => supplierid.SupplierID== item.SupplierID).Add(item);
}
BindingContext = this;
As a result, it still can't be grouped

We need to set the template for group header, try the code below .
<CollectionView ItemsSource="{Binding SupplierList}" IsGrouped="true">
<CollectionView.GroupHeaderTemplate>
<DataTemplate>
<Label Text="{Binding SupplierID}" FontAttributes="Bold"/>
</DataTemplate>
</CollectionView.GroupHeaderTemplate>
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding NameProduct}"/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>

Related

xamarin.forms second layer of bindable layout does not display binding

I am trying to create a tree similar to this :
I was able to create the first two layers, the parent being a scrollview (displaying the 7 items) the child being a bindable layout, displaying the sublayouts.
But the second sublayer is not binded to. The page just stays blank
<StackLayout BindableLayout.ItemsSource="{Binding dataPoints}" IsVisible="{Binding dataPointsVisible}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout Margin="20,0,0,0">
<StackLayout>
<Label Text="{Binding identifier}"/>
<Label Text="{Binding type}"/>
<StackLayout>
<Label FontAttributes="Bold" Text="see dataPointSettings ->"/>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
</StackLayout.GestureRecognizers>
</StackLayout>
<StackLayout BackgroundColor="Yellow"
HeightRequest="200"
BindableLayout.ItemsSource="{Binding dataPointSettings}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout BackgroundColor="Green">
<Label Text="{Binding alertingEmail}"/>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
<BoxView BackgroundColor="Black" HeightRequest="1"/>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
The "alertingEmail" is not displayed unfortunately.
How can I bind my views inside the second layer of bindable layouts?
You could refer to the code below:
Xaml: The same Xaml as yours.
Model:
public class DataPoints
{
public int identifier { get; set; }
public string type { get; set; }
public bool dataPointsVisible { get; set; }
public List<DataPointSettings> dataPointSettings { get; set; }
}
public class DataPointSettings
{
public string alertingEmail { get; set; }
}
ViewModel:
public class Page9ViewMode
{
public ObservableCollection<DataPoints> dataPoints { get; set; }
public Page9ViewMode()
{
dataPoints = new ObservableCollection<DataPoints>()
{
new DataPoints(){ dataPointsVisible=true, identifier=1, type="type1", dataPointSettings=new List<DataPointSettings>(){ new DataPointSettings() { alertingEmail="Email1-1"}, new DataPointSettings(){ alertingEmail="Email1-2" } } },
new DataPoints(){ dataPointsVisible=true, identifier=2, type="type2", dataPointSettings=new List<DataPointSettings>(){ new DataPointSettings() { alertingEmail="Email2-1"}, new DataPointSettings(){ alertingEmail="Email2-2" } } },
new DataPoints(){ dataPointsVisible=true, identifier=3, type="type3", dataPointSettings=new List<DataPointSettings>(){ new DataPointSettings() { alertingEmail="Email3-1"}, new DataPointSettings(){ alertingEmail="Email3-2" } } },
};
}
}
Screenshot:

CollectionView Grouping with Observables

Following this example to create a grouping for CollectionView, I notice that none of the properties are INotifyPropertyChanged, nor is the base class an ObservableCollection.
While the latter is easy to fix by changing List to ObservableCollection:
public class AnimalGroup : ObservableCollection<Animal>
{
public string Name { get; private set; }
public AnimalGroup(string name, ObservableCollection<Animal> animals) : base(animals)
{
Name = name;
}
private string _someOtherPropertyIWantToChangeAtRuntime = "hey";
public string SomeOtherPropertyIWantToChangeAtRuntime { get => _someOtherPropertyIWantToChangeAtRuntime, set => SetProperty(ref _someOtherPropertyIWantToChangeAtRuntime, value); }
}
It isn't clear how to make Name, or any other property (e.g. SomeOtherPropertyIWantToChangeAtRuntime), I want to associate with the group as an INotifyPropertyChanged. Treating it is as a normal class by adding the interface to base causes this warning:
Base interface 'INotifyPropertyChanged' is redundant because AnimalGroup inherits 'ObservableCollection'
Yet, there is nothing for the setter to call, such as SetProperty(ref _name, Value) and the existing PropertyChanged object is just for monitoring a group's collection changes. It isn't invokable, just handleable.
If I ignore the warning and implement INotifyPropertyChanged anyway (and name my event PropChanged to avoid colliding with ObservableCollection.PropertyChanged),
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();
PropChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
return true;
}
public event PropertyChangedEventHandler PropChanged;
and let my ViewModel manage the value of SomeOtherPropertyIWantToChangeAtRuntime, the bound <Label> never sees any changes.
<CollectionView ItemsSource="{Binding AnimalGroups}" HorizontalOptions="FillAndExpand">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label
Text="{Binding Name}"
HorizontalOptions="Start"
FontSize="24.44"
TextColor="Black"
FontAttributes="Bold"
Margin="0,0,0,10"/>
<Label
Text="{Binding SomeOtherPropertyIWantToChangeAtRuntime}" FontSize="15"
TextColor="Black"
Margin="0,0,0,0">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.FindGroupAndChangeTextCommand, Source{x:Reference thisPageName}" CommandParameter="{Binding Name}"/>
</Label.GestureRecognizers>
</Label>
...
ViewModel:
public ObservableCollection<AnimalGroup> AnimalGroups {get; private set;}
public ICommand FindGroupAndChangeTextCommand {get; private set;}
public void FindGroupAndChangeText(string name)
{
var group = AnimalGroups.FirstOrDefault(t => t.Name == name);
if (group != null)
group.SomeOtherPropertyIWantToChangeAtRuntime = DateTime.Now.ToString();
}
ViewModel()
{
AnimalGroups = LoadData(); // not shown
FindGroupAndChangeTextCommand = new Command(FindGroupAndChangeText);
}
The result is that the label remains "hey" (which is the default value) and never changes even though I can see that the above command fires and the code finds the group and sets the text.
Agree with Jason, ObservableCollection has inherited INotifyPropertyChanged interface , So you will get the warning
Base interface 'INotifyPropertyChanged' is redundant because AnimalGroup inherits 'ObservableCollection'
And please see following screenshot about ObservableCollection<T>.
If you want to change the item at the runtime like this GIF.
Based on your code. I add two properties in the Animal class. For achieve the change the text of properties at the runtime, we can achieve the INotifyPropertyChanged in Animal class. Here is AnimalGroup.cs
public class AnimalGroup : ObservableCollection<Animal>
{
public string Name { get; private set; }
public AnimalGroup(string name, ObservableCollection<Animal> animals) : base(animals)
{
Name = name;
}
}
public class Animal : INotifyPropertyChanged
{
string animalName;
public string AnimalName
{
set
{
if (animalName != value)
{
animalName = value;
OnPropertyChanged("AnimalName");
}
}
get
{
return animalName;
}
}
string animalArea;
public string AnimalArea
{
set
{
if (animalArea != value)
{
animalArea = value;
OnPropertyChanged("AnimalArea");
}
}
get
{
return animalArea;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
For testing click the command, I achieve the MyAnimalViewModel.cs like following code.
public class MyAnimalViewModel
{
public ObservableCollection<AnimalGroup> AnimalGroups { get; private set; } = new ObservableCollection<AnimalGroup>();
public ICommand FindGroupAndChangeTextCommand { protected set; get; }
public MyAnimalViewModel()
{
ObservableCollection<Animal> ts = new ObservableCollection<Animal>();
ts.Add(new Animal() { AnimalArea = "Asia", AnimalName = "cat" });
ts.Add(new Animal() { AnimalArea = "Asia", AnimalName = "dog" });
ObservableCollection<Animal> ts2 = new ObservableCollection<Animal>();
ts2.Add(new Animal() { AnimalArea = "Eourp", AnimalName = "keep" });
ts2.Add(new Animal() { AnimalArea = "Eourp", AnimalName = "gggg" });
AnimalGroups.Add(new AnimalGroup("Animal1", ts));
AnimalGroups.Add(new AnimalGroup("Animal2", ts2));
FindGroupAndChangeTextCommand = new Command<Animal>((key) =>
{
key.AnimalName = "testggggg";
});
}
}
I notice you want to achieve the group for CollectionView. Here is my edited layout.
<ContentPage.Content>
<CollectionView x:Name="MyCollectionView" ItemsSource="{Binding AnimalGroups}" IsGrouped="True" HorizontalOptions="FillAndExpand">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical"/>
</CollectionView.ItemsLayout>
<CollectionView.GroupHeaderTemplate>
<DataTemplate>
<Label Text="{Binding Name}"
BackgroundColor="LightGray"
FontSize="Large"
FontAttributes="Bold" >
</Label>
</DataTemplate>
</CollectionView.GroupHeaderTemplate>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label
Text="{Binding AnimalArea}"
HorizontalOptions="Start"
FontSize="24.44"
TextColor="Black"
FontAttributes="Bold"
Margin="0,0,0,10"/>
<Label
Text="{Binding AnimalName}" FontSize="15"
TextColor="Black"
Margin="0,0,0,0">
<Label.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1"
Command="{ Binding BindingContext.FindGroupAndChangeTextCommand, Source={x:Reference Name=MyCollectionView} }" CommandParameter="{Binding .}"
/>
</Label.GestureRecognizers>
</Label>
</StackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage.Content>
Here is layout background code.
public partial class Page2 : ContentPage
{
public Page2()
{
InitializeComponent();
this.BindingContext = new MyAnimalViewModel();
}
}

Xamarin Forms CollectionView Command not working

I have a collection view with the command binded, but for some reason when I select an item the action is never called in the viewmodel, heres my ViewModel code:
public class PlatillosViewModel : INotifyPropertyChanged
{
private INavigation Navigation;
public event PropertyChangedEventHandler PropertyChanged;
public List<PlatilloModel> Platillos { get; set; }
public List<GrupoModel> Grupos { get; set; }
public ICommand SelectedGroupCommand => new Command(SelectedGroup);
public PlatillosViewModel(INavigation navigation)
{
Navigation = navigation;
PlatillosRepository repository = new PlatillosRepository();
Platillos = repository.GetAll().ToList();
GrupoRepository grupoRepository = new GrupoRepository();
Grupos = grupoRepository.GetAll().ToList();
}
public ICommand SelectedPlatilloCommand => new Command<PlatilloModel>(async platillo =>
{
await Navigation.PushAsync(new PlatilloView());
});
void SelectedGroup()
{
PlatillosRepository platillosRepository = new PlatillosRepository();
//Platillos = platillosRepository.GetFilteredByGroup(grupoSeleccionado);
}
protected virtual void OnPropertyChanged(string property = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
And here is my Page:
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ComanderoMovil.Views.PlatillosView"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.UseSafeArea="true"
xmlns:behaviorsPack="clr-namespace:Xamarin.Forms.BehaviorsPack;assembly=Xamarin.Forms.BehaviorsPack">
<ContentPage.Content>
<StackLayout>
<SearchBar> </SearchBar>
<StackLayout Orientation="Horizontal">
<CollectionView ItemsSource="{Binding Grupos}"
HeightRequest="50"
ItemsLayout="HorizontalList"
SelectionMode="Single"
SelectedItem="{Binding SelectedGroupCommand, Mode=TwoWay}">
<CollectionView.ItemTemplate>
<DataTemplate>
<ContentView>
<Label Margin="2"
BackgroundColor="Black"
Text="{Binding nombre}"
TextColor="White"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
FontSize="Small"></Label>
</ContentView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
<ListView Grid.Column="2"
HasUnevenRows="True"
SeparatorVisibility="None"
ItemsSource="{Binding Platillos}">
<ListView.Behaviors>
<behaviorsPack:SelectedItemBehavior Command="{Binding SelectedPlatilloCommand}"/>
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ContentView Padding="2, 5, 5, 0">
<Frame OutlineColor="Black"
Padding="10"
HasShadow="False">
<StackLayout Orientation="Horizontal">
<Label Margin="10"
Text="{Binding clave_platillo}"
FontSize="Large"
HorizontalOptions="Start"></Label>
<Label Margin="10"
HorizontalTextAlignment="End"
Text="{Binding nombre}"></Label>
</StackLayout>
</Frame>
</ContentView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
I have tried adding the command to the items inside the collection view, replacing labels for buttons, but still doesn't work, I've also tried to use SelectionChangedCommand in the collection view, and still the same issue, the only way I can make it work is handling the item selection in the View, but I want to stay true to MVVM.
Here is my GrupoModel:
public class GrupoModel
{
public string clave_grupo { get; set; }
public int id_clasificacion { get; set; }
public int id_grupo { get; set; }
public string nombre { get; set; }
public bool pedirClave { get; set; }
public bool status { get; set; }
public int tipo { get; set; }
}
and here is an image of what im trying to do:
If you read the document:
When the SelectionMode property is set to Single, a single item in the
CollectionView can be selected. When an item is selected, the
SelectedItem property will be set to the value of the selected item.
When this property changes, the SelectionChangedCommand is executed
(with the value of the SelectionChangedCommandParameter being passed
to the ICommand), and the SelectionChanged event fires.
When you want to bind a Commond, you should bind to the SelectionChangedCommand instead of SelectedItem. Change your code like below and it will work:
<CollectionView
HeightRequest="50"
ItemsLayout="HorizontalList"
SelectionMode="Single"
SelectionChangedCommand="{Binding SelectedGroupCommand, Mode=TwoWay}"
>
The command should go in the class of GrupoModel instead of the PlatillosViewModel
public List<GrupoModel> Grupos { get; set; }
Should be "linked" to class GrupoModel that have properties and a commandwhich will listen, something like:
Class GrupoModel
{
public int Id { get; set; }
public string Foo { get; set; }
public ICommand SelectedGroupCommand => new Command(Completar);
private async void Completar()
{
await ViewModels.PlatillosViewModel.GetInstancia().SelectedGroup(this);
}
}
This way each element of Grupos will have a command to listen.
BTW: Shouldn't Grupos be an ObservableCollection?

Xamarin forms:Listview grouping issue

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; }
}

Binding data to a listview and hiding a field

I have the code below in the constructor of my .cs file of a page. I am able to pull
data without a problem
void InitData()
{
ArticlesService objArtServ = new ArticlesService();
Task<List<ArticlesModel>> dataRetrieved = objArtServ.GetValues("News");
//Bind headlines and Id to listview but hide Id
// datalist.itemsource = ?
}
Here is the service that returns the data for me without any problem.
public class ArticlesService
{
HttpClient client;
public async Task<List<ArticlesModel>> GetValues(string category){
client = new HttpClient();
var response = await client.GetStringAsync("http://testing-dev.com/Api/Articles/DefaultArticles/" + category);
var articlesModel = JsonConvert.DeserializeObject<List<ArticlesModel>>(response);
return articlesModel;
}
}
I have a grid on this main page and a list view which will list the headlines from my model and Id. The id will be hidden
and not displayed on the list.
Here is the code below
<StackLayout Grid.Column="1" Orientation="Vertical" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" Padding="0" Spacing="0">
<SearchBar x:Name="searchBar" Placeholder="Search" SearchCommandParameter="{Binding Source={x:Reference searchBar}, Path=Text}"/>
<StackLayout VerticalOptions="FillAndExpand">
<StackLayout VerticalOptions="FillAndExpand" Padding="1" BackgroundColor="Black">
<ListView x:Name="dataList" BackgroundColor="White">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label FontSize="20" VerticalOptions="CenterAndExpand" TextColor="Black" Text="{Binding HeadLines}"></Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</StackLayout>
</StackLayout>
Here is my model I am using
public class ArticlesModel
{
public Int32 Id { get; set; }
public string HeadLines { get; set; }
public string Url { get; set; }
public string Category { get; set; }
public string Summary { get; set; }
}
Questions
How do I bind only the HeadLines and Id to my listview such that my Id will be hidden but the headlines will show in the listview
On the click of each row in my listview, I want to be able to pass the associated Id so I can use the Id to query my list for other things.
How do I achieve this?
You don't need to bind the id if you're not displaying it. The ItemSelected event will pass you the complete ArticlesModel object for the row that was tapped, and you can easily get the ID from the model.
dataList.ItemSelected += (s,e) {
if (e.SelectedItem == null) return;
// item will be a pointer to the selected ArticlesModel object
var item = (ArticlesModel)e.SelectedItem;
// then you can use item.Id or any other property on ArticlesModel
};

Resources