Prism Xamarin - Data binding not updating - xamarin.forms

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?

Related

Unhandled Exception: System.InvalidCastException: Specified cast is not valid. when trying to save data to my database xamarin forms

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

How to delete items from a collection view in xamarin.forms?

NOTE: this didn't work : SQLite Error: Cannot delete WhereListIterator`1: it has no PK
(Adding a table name made no change)
I have an AcceuilPage which basically do this : https://github.com/devcrux/Xamarin.Forms-Expander but i added a delete icon and when i press it i want to be able to delete all the content for this date ( also the date) from this page.
So i added a delete function to delete it from database but when i expand for a date and press delete i get this error : Exception:
System.NotSupportedException: Cannot delete Object: it has no PK which bring me to await App.Database.DeleteAgendaAsync(agenda); in :
async void DeleteButtonClicked(object sender, EventArgs e)
{
var agenda = (Agenda)AgendaCollection.SelectedItem;
await App.Database.DeleteAgendaAsync(agenda);
await Navigation.PopAsync();
}
i also tried for the error line this : await App.Database.DeleteAgendaAsync(agenda); but this didn't even do anything when i pressed it ( no error and nothing happening).
I saw that this was a working fine for a listview but im struggling to do it in a collectionview, feels to me like it doesn't know if i am currently selecting something. ( With listview, when u click an item, the item view change color).
What am i doing wrong, im also starting to think that using a listview would be more simple.
My 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"/>
<StackLayout Orientation="Horizontal">
<ImageButton Source="iconplus.png" HeightRequest="30" WidthRequest="30" Clicked="GoToNewFormPage"></ImageButton>
<ImageButton Source="iconmoins.png" HeightRequest="30" WidthRequest="30"></ImageButton>
</StackLayout>
</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 x:Name="topicLayout" 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"/>
<ImageButton Source="iconmoins.png" HeightRequest="30" WidthRequest="30" Clicked="DeleteButtonClicked"></ImageButton>
</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>
My 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();
BindingContext = new Agenda();
}
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());
}
async void DeleteButtonClicked(object sender, EventArgs e)
{
var agenda = (Agenda)BindingContext;
await App.Database.DeleteAgendaAsync(agenda);
await Navigation.PopAsync();
}
protected override async void OnAppearing()
{
base.OnAppearing();
AgendaCollection.ItemsSource = await App.Database.GetAgendasAsync();
}
}
}
AgendaDatabase.cs
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
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using SQLite;
using Calculette.Database;
namespace Calculette.Models
{
[Table("Agenda")]
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; }
}
}
When we touch on the imagebutton/button , the click event triggers , but it does not mean we select an item in CollectionView ,the button click event covers the click/tap event on item of Collectionview itself, so in this scenario , AgendaCollection.SelectedItem is null ,then the error happens .
For the second approach you used: var agenda = (Agenda)BindingContext; , you set BindingContext in constructor : BindingContext = new Agenda(); , BindingContext is a brand new object which is not the one you are selecting .
To solve the prorblem , you could get the imagebutton's BindingContext and then manipulate the logic .
async void DeleteButtonClicked(object sender, EventArgs e)
{
ImageButton button = sender as ImageButton;
var agenda = button.BindingContext as Agenda;
await App.Database.DeleteAgendaAsync(agenda);
await Navigation.PopAsync();
}
Update
public ObservableCollection<Agenda> GetAgendasAsync()
{
List<Agenda>list = database.Table<Agenda>().ToListAsync().Result;
ObservableCollection<Agenda> result = new ObservableCollection<Agenda>(list);
return result;
}

Xamarin Forms TemplateBinding to Command In BasePage

I have a BasePage created using ControlTemplates that contains a loading overlay for each child to use - This overlay has a "cancel" button on it, but for some reason I can't get ICommands to execute when I tap the buttons. Clicked events work fine but I'd like to understand what the problem is with Commands.
I researched the issue and found I should be binding using Command="{TemplateBinding MyCommand}" since my content is within a ControlTemplate but still no luck, however I am also binding the Text property and this is working fine, so I'm a bit confused.
Here's a cut down version of what I've hacked together.
Here's my BasePage XAML with the button in question:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage x:Name="this" NavigationPage.HasNavigationBar="False" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:helpers="clr-namespace:ShoppingListNEW.MarkupExtensions" xmlns:views="clr-namespace:ShoppingListNEW.Views" x:Class="ShoppingListNEW.Pages.BasePage">
<ContentPage.ControlTemplate>
<ControlTemplate>
<Grid Padding="0" RowSpacing="0" ColumnSpacing="0">
<StackLayout>
<StackLayout.Padding>
<OnPlatform x:TypeArguments="Thickness" iOS="0,30,0,0"/>
</StackLayout.Padding>
<ScrollView VerticalOptions="FillAndExpand" >
<StackLayout RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width}" RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height}" Orientation="Vertical">
<ContentPresenter />
</StackLayout>
</ScrollView>
</StackLayout>
<AbsoluteLayout IsVisible="False" Grid.Row="0" Grid.Column="0" x:Name="loading" BackgroundColor="#85000000" AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1">
<ActivityIndicator Color="Lime" Scale="2" IsRunning="true" IsEnabled="true" IsVisible="true" AbsoluteLayout.LayoutFlags="PositionProportional" AbsoluteLayout.LayoutBounds="0.5,0.4" />
<Button IsVisible="True" Text="{TemplateBinding MyTextLabel}" AbsoluteLayout.LayoutFlags="PositionProportional" AbsoluteLayout.LayoutBounds="0.5,0.5" Command="{TemplateBinding MyCommand}" />
</AbsoluteLayout>
</Grid>
</ControlTemplate>
</ContentPage.ControlTemplate>
</ContentPage>
And here's the C# for that BasePage:
using System;
using System.Windows.Input;
using ShoppingListNEW.Views;
using Xamarin.Forms;
namespace ShoppingListNEW.Pages
{
public partial class BasePage : ContentPage
{
public string MyTextLabel { get; set; } = "This Works";
public ICommand MyCommand { get; set; }
public BasePage()
{
InitializeComponent();
MyCommand = new Command(() =>
{
Console.Write("But this doesn't work :(");
});
}
public void ShowHideLoading(bool showhide, bool allowCancel = false)
{
var loadingLayout = (AbsoluteLayout)GetTemplateChild("loading");
var cancelButton = loadingLayout.FindByName<Button>("btnCancel");
cancelButton.IsVisible = allowCancel;
loadingLayout.IsVisible = showhide;
}
}
}
Any ideas?
On your ControlTemplate binding, please use the following code:
<Button
AbsoluteLayout.LayoutBounds="0.5,0.5"
AbsoluteLayout.LayoutFlags="PositionProportional"
Command="{TemplateBinding BindingContext.MyCommand}"
IsVisible="True"
Text="{TemplateBinding BindingContext.MyTextLabel}" />
I use your code to create simple that you can take a look, please don't set AbsoluteLayout IsVisible="False".
<ContentPage.ControlTemplate>
<ControlTemplate>
<Grid Padding="0">
<StackLayout>
<ScrollView VerticalOptions="FillAndExpand">
<StackLayout
Orientation="Vertical"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Height}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Width}">
<ContentPresenter />
</StackLayout>
</ScrollView>
</StackLayout>
<AbsoluteLayout
x:Name="loading"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
BackgroundColor="#85000000">
<ActivityIndicator
AbsoluteLayout.LayoutBounds="0.5,0.4"
AbsoluteLayout.LayoutFlags="PositionProportional"
IsEnabled="true"
IsRunning="true"
IsVisible="true"
Scale="2"
Color="Lime" />
<Button
AbsoluteLayout.LayoutBounds="0.5,0.5"
AbsoluteLayout.LayoutFlags="PositionProportional"
Command="{TemplateBinding BindingContext.MyCommand}"
IsVisible="True"
Text="{TemplateBinding BindingContext.MyTextLabel}" />
</AbsoluteLayout>
</Grid>
</ControlTemplate>
</ContentPage.ControlTemplate>
public partial class Page19 : ContentPage
{
public Page19()
{
InitializeComponent();
this.BindingContext = new Viewmodel1();
}
}
public class Viewmodel1:ViewModelBase
{
private string _MyTextLabel;
public string MyTextLabel
{
get { return _MyTextLabel; }
set
{
_MyTextLabel = value;
RaisePropertyChanged("MyTextLabel");
}
}
public RelayCommand MyCommand { get; set; }
public Viewmodel1()
{
MyCommand = new RelayCommand(method);
MyTextLabel = "this is test";
}
private void method()
{
Console.WriteLine("this is test!");
}
}
Problem solved. Thanks to Mikolaj Kieres for the answer, it was a simple case of initialising the Command earlier so I moved it up above the InitializeComponent and that kicked it into life. Thanks!

My Application is not working after added custom behavior, cant understand why

I have created a custom stepper Behavior, and added that behavior to a stepper in my xaml, but for some reason after adding the behavior the application doesn't compile, and i get this error:
Position 82:87. No property, bindable property, or event found for 'ValueChangedCommand', or mismatching type between value and property. (ComanderoMovil)
here is my code of the behavior:
using System;
using System.Windows.Input;
using Xamarin.Forms;
namespace ComanderoMovil.Behaviors
{
public class StepperQuantityChangedBehavior : Behavior<Stepper>
{
public static readonly BindableProperty StepperValueChangedProperty =
BindableProperty.Create("ValueChangedCommand", typeof(ICommand), typeof(StepperQuantityChangedBehavior), null);
public ICommand ValueChangedCommand
{
get
{
return (ICommand)GetValue(StepperValueChangedProperty);
}
set
{
SetValue(StepperValueChangedProperty, value);
}
}
protected override void OnAttachedTo(Stepper bindable)
{
base.OnAttachedTo(bindable);
bindable.ValueChanged += Bindable_ValueChanged;
}
protected override void OnDetachingFrom(Stepper bindable)
{
base.OnDetachingFrom(bindable);
bindable.ValueChanged -= Bindable_ValueChanged;
}
private void Bindable_ValueChanged(object sender, ValueChangedEventArgs e)
{
if (ValueChangedCommand == null)
{
return;
}
var stepper = sender as Stepper;
var prueba = e.NewValue;
if (ValueChangedCommand.CanExecute(prueba))
{
ValueChangedCommand.Execute(prueba);
}
}
}
}
and here is my code of the xaml where I add the behavior:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ComanderoMovil.Views.DishView"
xmlns:converterPack="clr-namespace:Xamarin.Forms.ConvertersPack;assembly=Xamarin.Forms.ConvertersPack"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.UseSafeArea="true"
xmlns:local="clr-namespace:ComanderoMovil.Behaviors"
x:Name="DishSelectedPage">
<ContentPage.ToolbarItems>
<ToolbarItem Icon="shopping_cart" Text="Search" Command="{Binding ShowCartCommand}" />
</ContentPage.ToolbarItems>
<ContentPage.Resources>
<ResourceDictionary>
<converterPack:CurrencyConverter x:Key="CurrencyConverter"></converterPack:CurrencyConverter>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<ScrollView>
<StackLayout>
<Label Text="{Binding Dish.Name}"
FontSize="Title"
HorizontalOptions="Center"
FontAttributes="Bold"></Label>
<Label Text="Precio"
FontSize="Subtitle"
HorizontalOptions="Center"
FontAttributes="Bold"></Label>
<Label Text="{Binding Dish.Price1, Converter={StaticResource CurrencyConverter}}"
FontSize="Subtitle"
HorizontalOptions="Center"></Label>
<Label Text="Modificadores"
FontAttributes="Bold"
FontSize="Large"
HorizontalOptions="Center"></Label>
<ListView ItemsSource="{Binding DishesMods}"
x:Name="ModsListView"
HasUnevenRows="True"
SeparatorVisibility="Default"
SeparatorColor="Black"
IsGroupingEnabled="True"
HeightRequest="{Binding ListHeight}">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell Height="30">
<StackLayout VerticalOptions="FillAndExpand"
Padding="10"
BackgroundColor="DimGray">
<Label Text="{Binding Key}"
TextColor="White"
VerticalOptions="Center"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="20">
<StackLayout Orientation="Horizontal">
<CheckBox Color="#102536">
<CheckBox.Behaviors>
<local:CheckBoxModChangedState ItemCheckedCommand="{Binding BindingContext.SelectedModCommand, Source={Reference DishSelectedPage}}"></local:CheckBoxModChangedState>
</CheckBox.Behaviors>
</CheckBox>
<Label Text="{Binding Name}"
VerticalOptions="Center"></Label>
<Label Text="Precio:"
VerticalOptions="Center"></Label>
<Label Text="{Binding Price}"
VerticalOptions="Center"></Label>
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Cantidad: "></Label>
<Label Text="1"></Label>
</StackLayout>
<StackLayout>
<Stepper HeightRequest="40"
WidthRequest="40">
<Stepper.Behaviors>
<local:StepperQuantityChangedBehavior ValueChangedCommand="{Binding BindingContext.ModQuantityCommand, Source={Reference DishSelectedPage}}"></local:StepperQuantityChangedBehavior>
</Stepper.Behaviors>
</Stepper>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Footer>
<ContentView>
<Frame HasShadow="False"
Padding="50">
<Button Padding="20"
Text="Agregar Orden"
TextColor="White"
BackgroundColor="#102536"
Command="{Binding BindingContext.AddOrderCommand, Source={Reference DishSelectedPage}}"></Button>
</Frame>
</ContentView>
</ListView.Footer>
</ListView>
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>
the funny thing is that, I have added custom behavior for other controls, like the checkbox, and it works without a problem, but only with this new behavior I am having trouble.
Anyone know's what is happening?
public static readonly BindableProperty StepperValueChangedProperty
public ICommand ValueChangedCommand
the issue is on the lines,as the error message said
event found for 'ValueChangedCommand', or mismatching type between
value and property. (ComanderoMovil)
you should change StepperValueChangedProperty to ValueChangedCommandProperty to keep the name consistent with ValueChangedCommand
change
public static readonly BindableProperty StepperValueChangedProperty =
BindableProperty.Create("ValueChangedCommand", typeof(ICommand), typeof(StepperQuantityChangedBehavior), null);
public ICommand ValueChangedCommand
{
get
{
return (ICommand)GetValue(StepperValueChangedProperty);
}
set
{
SetValue(StepperValueChangedProperty, value);
}
}
to
public static readonly BindableProperty ValueChangedCommandProperty =
BindableProperty.Create("ValueChangedCommand", typeof(ICommand), typeof(StepperQuantityChangedBehavior), null);
public ICommand ValueChangedCommand
{
get
{
return (ICommand)GetValue(ValueChangedCommandProperty);
}
set
{
SetValue(ValueChangedCommandProperty, value);
}
}

How to expand and Collapse ListView in Xamarin Forms

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>

Resources