I'm new to Xamarin Forms and I understand that there are many useful controls. I'm looking for a control that can expand to show data in a grid like in the example below.
Update
Model:
public class Phone
{
public string mobile { get; set; }
public string home { get; set; }
public string office { get; set; }
}
public class Contact
{
public string id { get; set; }
public string name { get; set; }
public string email { get; set; }
public string address { get; set; }
public string gender { get; set; }
public Phone phone { get; set; }
}
public class ContactList
{
public List<Contact> contacts { get; set; }
}
XAML:
<Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Margin="10" Text="Display Json Data" FontSize="25" />
<ListView x:Name="listviewConacts" Grid.Row="1" HorizontalOptions="FillAndExpand" HasUnevenRows="True" ItemSelected="listviewContacts_ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid HorizontalOptions="FillAndExpand" Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Text="{Binding name}" HorizontalOptions="StartAndExpand" Grid.Row="0" TextColor="Blue" FontAttributes="Bold"/>
<Label Text="{Binding email}" HorizontalOptions="StartAndExpand" Grid.Row="1" TextColor="Orange" FontAttributes="Bold"/>
<Label Text="{Binding phone.mobile}" HorizontalOptions="StartAndExpand" Grid.Row="2" TextColor="Gray" FontAttributes="Bold"/>
<BoxView HeightRequest="2" Margin="0,10,10,0" BackgroundColor="Gray" Grid.Row="3" HorizontalOptions="FillAndExpand" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
<ActivityIndicator x:Name="ProgressLoader" IsRunning="True"/>
</Grid>
Based on the model and XAML above, how can I achieve an expandable and collapsible ListView such as the one in image above?
In ListView to show the data just take one GridLayout in ViewCell. Take two rows with height auto in GridLayout. In first row show header and button and in second row just add that item relatesd data and bind one isvisible property to that second row. On click of that up arrow just inverse the value of isvisible property.
That is if the property of isvisible is true then it will show the 2 row and if isvisible property is false then will just show you that header.
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding HeaderText}" Grid.Row="0"
Grid.Column="0" />
<Button Text="Show" Grid.Row="0" Grid.Column="1"
Clicked="LableVisibleButton"/>
<Label
Grid.Row="1" Grid.Grid.ColumnSpan="2"
FormattedText="{Binding FormattedText}" IsVisible="
{Binding LabelVisible}"/>
</ViewCell>
I've done it in notepad so haven't tested it. Moreover I generally don't do xaml. However, this should work. I just added 2 buttons on top of your grid, bound to the same command that toggles a boolean to say wether your grid should be visible or not.
Your ViewModel:
namespace XamlSamples.Models
{
public class Phone
{
public string mobile { get; set; }
public string home { get; set; }
public string office { get; set; }
}
public class Contact
{
public string id { get; set; }
public string name { get; set; }
public string email { get; set; }
public string address { get; set; }
public string gender { get; set; }
public Phone phone { get; set; }
public bool IsCollapsed { get; private set; }
public ICommand ToggleCollapseCommand { get; }
public Contact() => ToggleCollapseCommand = new Command(_ => IsCollapsed = !IsCollapsed);
}
public class ContactList
{
public List<Contact> contacts { get; set; }
}
public class InvertBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => !(bool)value;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => !(bool)value;
}
}
Your view:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:models="clr-namespace:XamlSamples.Models;assembly=XamlSamples"
x:Class="XamlSamples.CollapsableListView">
<ContentPage.Resources>
<ResourceDictionary>
<models:InvertBoolConverter x:Key="invertBoolConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Margin="10" Text="Display Json Data" FontSize="25" />
<ListView x:Name="listviewConacts" Grid.Row="1" HorizontalOptions="FillAndExpand" HasUnevenRows="True" ItemSelected="listviewContacts_ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical">
<Button Text="Tap to Uncollapse" Command="{Binding ToggleCollapseCommand}" IsVisible="{Binding IsCollapsed}"/>
<Button Text="Tap to Collapse" Command="{Binding ToggleCollapseCommand}" IsVisible="{Binding IsCollapsed, Converter={StaticResource invertBoolConverter}}"/>
<Grid HorizontalOptions="FillAndExpand" Padding="10" IsVisible="{Binding IsCollapsed, Converter={StaticResource invertBoolConverter}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Text="{Binding name}" HorizontalOptions="StartAndExpand" Grid.Row="0" TextColor="Blue" FontAttributes="Bold"/>
<Label Text="{Binding email}" HorizontalOptions="StartAndExpand" Grid.Row="1" TextColor="Orange" FontAttributes="Bold"/>
<Label Text="{Binding phone.mobile}" HorizontalOptions="StartAndExpand" Grid.Row="2" TextColor="Gray" FontAttributes="Bold"/>
<BoxView HeightRequest="2" Margin="0,10,10,0" BackgroundColor="Gray" Grid.Row="3" HorizontalOptions="FillAndExpand" />
</Grid>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
<ActivityIndicator x:Name="ProgressLoader" IsRunning="True"/>
</Grid>
Related
Ok so heres the basic of my app : On opening im in the AcceuilPage.xaml, i click the + icon to add a new item to the collection.
Then it brings me to the NewFormPage.xaml which i use to fill a data form, then when i click save i call the SaveButtonClicked method from NewFormPage.xaml.cs BUT my app just crash and i get: Unhandled Exception: System.InvalidCastException: Specified cast is not valid. But i have no clue why ?
Thanks for your help !
Also, i tried this to see if data were created and everything was working fine :
private void CreateSampleData()
{
base.OnAppearing();
ObservableCollection<Agenda> list = new ObservableCollection<Agenda>();
list.Add(new Agenda { Topic = "Journée au chantier", Duration = "07:30 UTC - 11:30 UTC", Color = "#B96CBD", Date = new DateTime(2020, 3, 23) });
list.Add(new Agenda { Topic = "Journée au chantier", Duration = "07:30 UTC - 11:30 UTC", Color = "#B96CBD", Date = new DateTime(2020, 3, 23) });
AgendaCollection.ItemsSource = list;
}
Heres the code :
AgendaDatabase.cs in the 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();
}
// Get all agenda
public Task<List<Agenda>> GetAgendasAsync()
{
return database.Table<Agenda>().ToListAsync();
}
// Get specific agenda
public Task<Agenda> GetAgendaAsync(int id)
{
return database.Table<Agenda>()
.Where(i => i.ID == id)
.FirstOrDefaultAsync();
}
// Insert new agenda (save)
public Task<int> SaveAgendaAsync(Agenda agenda)
{
if (agenda.ID != 0)
{
return database.UpdateAsync(agenda);
}
else
{
return database.InsertAsync(agenda);
}
}
//Delete specific agenda
public Task<int> DeleteAgendaAsync(Agenda agenda)
{
return database.DeleteAsync(agenda);
}
}
}
Agenda.cs in 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; }
}
}
NewFormPage.xaml in the views folder
<ContentPage.Content>
<StackLayout>
<StackLayout >
<Label Text="Date de calcul:" FontAttributes="Bold" FontFamily="ROBOTO" TextColor="#000000"></Label>
<DatePicker x:Name="Datepicker" Date="{Binding Date}" FontFamily="ROBOTO" Format="yyyy-MM-dd" ></DatePicker>
</StackLayout>
<StackLayout >
<Label Text="Description" FontAttributes="Bold" FontFamily="ROBOTO" TextColor="#000000"></Label>
<Entry x:Name="Description" Text="{Binding Topic}"/>
</StackLayout>
<StackLayout>
<Label Text="Durée quotidienne" FontAttributes="Bold" FontFamily="ROBOTO" TextColor="#000000"></Label>
<Entry x:Name="Duree" Placeholder="HH:MM:SS" Text="{Binding Duration}"/>
</StackLayout>
<StackLayout>
<Label Text="Niveau de pression dB(A)" FontAttributes="Bold" FontFamily="ROBOTO" TextColor="#000000"></Label>
<Entry x:Name="Pression" Text="{Binding Pression}"/>
</StackLayout>
<Editor Text="{Binding Commentary}"
x:Name="Commentary"
AutoSize="TextChanges"
Placeholder="Commentaires" />
<StackLayout>
<Button Text="Save" x:Name="SaveButton" TextColor="#008A00" BackgroundColor="#FFFFFF" BorderWidth="1" BorderColor="#1A961A" Clicked="SaveButtonClicked"></Button>
<ActivityIndicator IsRunning="{Binding IsBusy}"/>
</StackLayout>
</StackLayout>
</ContentPage.Content>
NewFormPage.xaml.cs in the views folder
using Calculette.Models;
using Calculette.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Calculette.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class NewFormPage : ContentPage
{
public NewFormPage ()
{
InitializeComponent ();
BindingContext = new FormViewModel();
}
async void SaveButtonClicked(object sender, EventArgs e)
{
var agenda = (Agenda)BindingContext;
//agenda.Date = DateTime.UtcNow;
await App.Database.SaveAgendaAsync(agenda);
await Navigation.PopAsync();
}
}
}
AcceuilPage.xaml
<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"> <!--ItemsSource="{Binding AngedaCollection}" -->
<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>
AcceuilPage.xaml.ca
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();
}
}
here, BindingContext is declared as a FormViewModel
public NewFormPage ()
{
InitializeComponent ();
BindingContext = new FormViewModel();
}
but here you are trying to cast BindingContext as an Agenda
async void SaveButtonClicked(object sender, EventArgs e)
{
var agenda = (Agenda)BindingContext;
//agenda.Date = DateTime.UtcNow;
await App.Database.SaveAgendaAsync(agenda);
await Navigation.PopAsync();
}
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 experimenting with Prism and xamarin but it seems I even don't get my property updated and I don't understand why. I navigate to this page with and the title should be updated but instead it is the empty string. Can someone explain why it isn't updating? Thanks!
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:prism="http://prismlibrary.com"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="CardGame.Mobile.Views.GamePage">
<StackLayout Padding="10, 20">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="2*" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Title, Mode=TwoWay}"
FontSize="Large"
HorizontalOptions="FillAndExpand"
Grid.Column="0"
Grid.Row="0"/>
<Image Source="AC.png"
Grid.Column="0"
Grid.Row="1"
Scale="0.2"
Aspect="AspectFit" />
<Image Source="AC.png"
Grid.Column="0"
Grid.Row="2"
HorizontalOptions="Fill"
VerticalOptions="Fill"/>
</Grid>
</StackLayout>
</ContentPage>
My MVVM
public class GamePageViewModel : BindableBase, INavigationAware
{
private SingleGameManager _gameManager;
public string PlayerName { get; set; }
private string _title;
public string Title
{
get { return _title; }
set
{
SetProperty(ref _title, value);
RaisePropertyChanged();
}
}
public GamePageViewModel()
{
}
public void OnNavigatedFrom(INavigationParameters parameters)
{
}
public void OnNavigatedTo(INavigationParameters parameters)
{
PlayerName = parameters.GetValue<string>("PlayerName");
_gameManager = new SingleGameManager(PlayerName);
_gameManager.StartRound();
Title = $"Welcome {PlayerName}! Round {_gameManager.Round}!";
}
}
Like you can see on my property I call the RaisePropertyChanged but it doens't execute I guess?
Have no clue as to how to handle this scenario dynamically after(2)
1) the app call the REST Json web service
2) Deserialize the Json response
3) Bind the Data to ListView
below is the working example.
Problem : I need to add a button to each row of record. Example , How to add a button next to Johnny Depp?? Add EventHandler for this button?
This means I need to dynamically generate the XAML? How to bind ?
here to code to handle the Returned Json Result:
-- Model:
public class Phone
{
public string mobile { get; set; }
public string home { get; set; }
public string office { get; set; }
}
public class Contact
{
public string id { get; set; }
public string name { get; set; }
public string email { get; set; }
public string address { get; set; }
public string gender { get; set; }
public Phone phone { get; set; }
}
public class ContactList
{
public List<Contact> contacts { get; set; }
}
---- Call REST
var client = new System.Net.Http.HttpClient();
var response = await client.GetAsync("http://Rest api");
string contactsJson = response.Content.ReadAsStringAsync().Result;
ContactList ObjContactList = new ContactList();
if (contactsJson != "")
{
ObjContactList = JsonConvert.DeserializeObject<ContactList>(contactsJson);
}
listviewConacts.ItemsSource = ObjContactList.contacts;
------XAML:
<Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Margin="10" Text="Display Json Data" FontSize="25" />
<ListView x:Name="listviewConacts" Grid.Row="1" HorizontalOptions="FillAndExpand" HasUnevenRows="True" ItemSelected="listviewContacts_ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid HorizontalOptions="FillAndExpand" Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Text="{Binding name}" HorizontalOptions="StartAndExpand" Grid.Row="0" TextColor="Blue" FontAttributes="Bold"/>
<Label Text="{Binding email}" HorizontalOptions="StartAndExpand" Grid.Row="1" TextColor="Orange" FontAttributes="Bold"/>
<Label Text="{Binding phone.mobile}" HorizontalOptions="StartAndExpand" Grid.Row="2" TextColor="Gray" FontAttributes="Bold"/>
<BoxView HeightRequest="2" Margin="0,10,10,0" BackgroundColor="Gray" Grid.Row="3" HorizontalOptions="FillAndExpand" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
<ActivityIndicator x:Name="ProgressLoader" IsRunning="True"/>
</Grid>
Thanks
You created custom cell already, add button to it with Command and CommandParameter. Inside your viewcell grid add
<Button Text="Toggle" Command="{Binding Source={x:Reference YourPage}, Path=BindingContext.TapCommand}" CommandParameter="{Binding}"/>
Then in your model
public Command TapCommand
{
get
{
return new Command( (obj) =>
{
//obj here will be your data record
});
}
}