Why won't changes reflect in my Xamarin.Forms project or SQLite database - c#

UPDATE - Issue #1 is Solved, Issue#2 is still unsolved
You can view a very crude demonstration video of my issue at https://www.youtube.com/watch?v=5_6KJ0QJouM
I am building have a Xamarin.Forms app with an SQLite database using the MVVM design pattern and C#
When try to Save a record to the database from a View the update/save does not appear to be saving to the SQLite database or reflect in other Views.
I know the database Save method does work as I have created some dummy data when the application first loads (in App.xaml.cs) using the DeveloperData.cs file.
I have two issues.
(SOLVED) Issue 1 - Data not Saving to Database
when I call the Save command from the MerchandiserEditPage.xaml, which uses the MerchandiserEditPageViewModel.cs ViewModel, the record does not appear to save.
Issue 2 - Changes Reflecting in other Views
Once the updated data is saved to the database, how can I reflect that change in other views? After I Save a record from the MerchandiserEditPage that View is "Popped" off the stack and the user is returned to the MerchandiserProfileView. I want the updated data to be reflected in all other views on the stack. But this doesn't appear to be happening? (I tested this using hardcoded data and the same issue occurred, so problem is not directly related to issue 1)
There are many files in my project, that can be viewed/downloaded from my GitHub repository but I will concentrate on the following in this question.
MerchandiserEditPage.xaml (View)
MerchandiserProfilePage.xaml (View)
MerchandiserDatabase.cs (Database Functions)x
MerchandiserEditPageViewModel.cs x
View my GitHub repository for the full project.
MerchandiserDatabase.cs (Database Functions)
using SQLite;
namespace MobileApp.Database
{
public class MerchandiserDatabase
{
private static SQLiteConnection database = DependencyService.Get<IDatabaseConnection>().DbConnection();
private readonly static object collisionLock = new object();
public MerchandiserDatabase()
{
database.CreateTable<Models.Merchandiser>();
}
public static void SaveMerchandiser(Models.Merchandiser merchandiser)
{
lock (collisionLock)
{
if (merchandiser.Id != 0)
{
database.Update(merchandiser);
}
else
{
database.Insert(merchandiser);
}
}
}
}
}
MerchandiserEditPageViewModel.cs (ViewModel) UPDATED
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace MobileApp.ViewModels
{
public class MerchandiserEditPageViewModel : BaseViewModel
{
public string PageTitle { get; } = "Edit Merchandiser Profile";
public Command SaveCommand { get; set; }
private string name;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged();
}
}
private string phone;
public string Phone
{
get { return phone; }
set
{
phone = value;
OnPropertyChanged();
}
}
private string email;
public string Email
{
get { return email; }
set
{
email = value;
OnPropertyChanged();
}
}
public MerchandiserEditPageViewModel(Models.Merchandiser selectedMerchandiser)
{
Name = selectedMerchandiser.Name;
Phone = selectedMerchandiser.Phone;
Email = selectedMerchandiser.Email;
SaveCommand = new Command( async ()=> {
selectedMerchandiser.Name = this.Name;
selectedMerchandiser.Phone = this.Phone;
selectedMerchandiser.Email = this.Email;
Database.MerchandiserDatabase.SaveMerchandiser(selectedMerchandiser);
await Application.Current.MainPage.Navigation.PopModalAsync();
});
}
}
}
MerchandiserEditPage.xaml (View)
<?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="MobileApp.Views.MerchandiserEditPage">
<ContentPage.Content>
<StackLayout>
<!--Page Heading-->
<StackLayout Spacing="0">
<Label Text="{Binding PageTitle}"
Style="{StaticResource PageTitle}"/>
<BoxView HeightRequest="1" Color="LightGray" />
</StackLayout>
<!-- Merchandiser Profile -->
<StackLayout Margin="10">
<Label Text="Name"/>
<Entry Text="{Binding Name}"/>
<Label Text="Phone"/>
<Entry Text="{Binding Phone}"/>
<Label Text="Email"/>
<Entry Text="{Binding Email}"/>
<StackLayout Orientation="Horizontal"
HorizontalOptions="Center">
<Button Text="Cancel"
Clicked="CancelButton_Clicked"/>
<Button Text="Save"
Command="{Binding SaveCommand}"/>
</StackLayout>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
MerchandiserEditPage.xaml.cs (View - Code Behind)
public partial class MerchandiserEditPage : ContentPage
{
Models.Merchandiser SelectedMerchandiser { get; set; }
public MerchandiserEditPage (Models.Merchandiser selectedMerchandiser)
{
InitializeComponent ();
SelectedMerchandiser = selectedMerchandiser;
this.BindingContext = new ViewModels.MerchandiserEditPageViewModel(selectedMerchandiser);
}
async private void CancelButton_Clicked(object sender, EventArgs e)
{
await Navigation.PopModalAsync();
}
}
MerchandiserProfilePage.xaml (View - 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"
x:Class="MobileApp.Views.MerchandiserProfilePage"
NavigationPage.HasNavigationBar="False">
<ContentPage.Content>
<StackLayout>
<!--Page Heading-->
<StackLayout Spacing="0">
<Label Text="{Binding PageTitle}"
Style="{StaticResource PageTitle}"/>
<BoxView HeightRequest="1" Color="LightGray" />
</StackLayout>
<!-- Merchandiser Profile -->
<StackLayout Margin="10">
<Label Text="Name"/>
<Entry Text="{Binding Name}"
IsEnabled="False"/>
<Label Text="Phone"/>
<Entry Text="{Binding Phone}"
IsEnabled="False"/>
<Label Text="Email"/>
<Entry Text="{Binding Email}"
IsEnabled="False"/>
<StackLayout Orientation="Horizontal"
HorizontalOptions="Center">
<Button Text="Back"
Clicked="BackButton_Clicked"/>
<Button Text="Edit"
Clicked="EditButton_Clicked"/>
</StackLayout>
<Button Text="Delete"
Command="{Binding DeleteCommand}"/>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
MerchandiserProfilePage.xaml.cs - (View - Code Behind)
public partial class MerchandiserProfilePage : ContentPage
{
private Models.Merchandiser SelectedMerchandister { get; set; }
public MerchandiserProfilePage (Models.Merchandiser selectedMerchandiser)
{
InitializeComponent ();
SelectedMerchandister = selectedMerchandiser;
this.BindingContext = new ViewModels.MerchandiserProfilePageViewModel(selectedMerchandiser);
}
async private void BackButton_Clicked(object sender, EventArgs e)
{
await Navigation.PopModalAsync();
}
async private void EditButton_Clicked(object sender, EventArgs e)
{
await Navigation.PushModalAsync(new Views.MerchandiserEditPage(SelectedMerchandister));
}
}
MerchandiserProfilePageViewModel.cs (ViewModel)
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace MobileApp.ViewModels
{
public class MerchandiserProfilePageViewModel : BaseViewModel
{
public string PageTitle { get; } = "Merchandiser Profile";
public Command DeleteCommand { get; }
private string name;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged();
}
}
private string phone;
public string Phone
{
get { return phone; }
set
{
phone = value;
OnPropertyChanged();
}
}
private string email;
public string Email
{
get { return email; }
set
{
email = value;
OnPropertyChanged();
}
}
public MerchandiserProfilePageViewModel(Models.Merchandiser selectedMerchandiser)
{
Name = selectedMerchandiser.Name;
Phone = selectedMerchandiser.Phone;
Email = selectedMerchandiser.Email;
DeleteCommand = new Command( async()=> {
bool deleteConfirmed = await Application.Current.MainPage.DisplayAlert("Confirm Delete",$"Are you sure you want to delete {selectedMerchandiser.Name} as a Merchandiser?","Yes","No");
if (deleteConfirmed)
{
// TODO: Delete Merchandiser
await Application.Current.MainPage.Navigation.PopModalAsync();
}
});
}
}
}

you have a hardcoded set of data in your VM instead of loading it from the db
public MerchandisersPageViewModel()
{
//Merchandisers = new ObservableCollection<Models.Merchandiser>(Database.MerchandiserDatabase.GetMerchandisers());
Merchandisers = new ObservableCollection<Models.Merchandiser>()
{
new Models.Merchandiser { Id=1, Name="Barney Rubble", Phone="021 321 654", Email="barney#rubble.com"},
new Models.Merchandiser { Id=2, Name="Frank Grimes", Phone="022 456 789", Email="grimey#homersfriend.com"},
new Models.Merchandiser { Id=3, Name="Perry Platypus", Phone="023 789 456", Email="perry#agentp.com"},
};
}
Update:
in MerchandiserProfilePageViewModel, get rid of the properties for Name, Phone and EMail
then in MerchandiserProfilePage.xaml change the bindings
<Entry Text="{Binding SelectedMerchandiser.Name}" IsEnabled="False"/>

Related

Xamarin.Forms update Entry value from Xamarin.Essentials.Contacts selection using MVVM

I'm trying to update the value of an Entry control using Xamarin.Essentials.Contacts.
I have a PhoneNumber Entry that's empty when opening the page but there's a button below it that says 'Select Contact' and I'm retrieving the phone number and am trying to set the value of the PhoneNumber Entry to that phone number.
I can't seem to be able to do it and I don't really know how to do it.
When I select the contact I get the number but it doesn't update the Entry value.
Here's my code
XAML
<StackLayout Orientation="Horizontal"
Padding="16,12"
Spacing="12">
<Frame HorizontalOptions="FillAndExpand"
Padding="6, 1"
Margin="5, 0"
CornerRadius="8"
BackgroundColor="#212121">
<Entry x:Name="PhoneNumberEntry"
Text="{Binding PhoneNumberEntry, Mode=TwoWay}"
Placeholder="Don't add '+1'"
Keyboard="Numeric"
MaxLength="10"
Style="{StaticResource EntryStyle}" />
</Frame>
</StackLayout>
<StackLayout Orientation="Horizontal"
Padding="16,12"
Spacing="12">
<Button x:Name="ContactButton"
Text="Or select contact"
Command="{Binding SelectContactCommand}"
BackgroundColor="Red"
HorizontalOptions="FillAndExpand" />
</StackLayout>
ViewModel
public class InviteViewModel : BaseViewModel
{
public InviteViewModel()
{
SelectContactCommand = new Command(async () => await ExecuteSelectContactCommand());
Info = new StringBuilder();
}
public string PhoneNumberEntry { get; set; }
public Command SelectContactCommand { get; set; }
public StringBuilder Info { get; set; }
private async Task ExecuteSelectContactCommand()
{
try
{
var contact = await Contacts.PickContactAsync();
if (contact == null)
{
return;
}
Info.AppendLine(contact.Phones.FirstOrDefault()?.PhoneNumber ?? string.Empty);
var phoneNumber = Info.ToString().Replace("(", "").Replace(")", "").Replace(" ", "").Replace("-", "").TrimEnd();
PhoneNumberEntry = phoneNumber;
}
catch (Exception ex)
{
}
}
}
Like #Jason and #AppPack said, it was only a matter of using OnPropertyChanged() in the setter of PhoneNumberEntry
private string phoneNumberEntry;
public string PhoneNumberEntry
{
get { return phoneNumberEntry; }
set
{
phoneNumberEntry = value;
OnPropertyChanged("PhoneNumberEntry");
}
}

How to navigate between page with datas

I hope someone could help me understand how it could works..
I have a project in Xamarin Forms
I have a page1 with code below
Page1.xaml
<StackLayout BindableLayout.ItemsSource="{Binding List1}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<AbsoluteLayout>
<Button Text="{Binding NameP} Clicked="Button_Clicked"/>
</AbsoluteLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
Page1.cs
using System.Collections.Generic;
using Xamarin.Forms;
namespace app.Views
{
public partial class Page1 : ContentPage
{
public Page1()
{
InitializeComponent();
ListPrograms1 = new List<Programmes1>();
{
ListPrograms1.Add(new Programmes1() { NameP = "Tomato", Detail = "xxxxx" });
ListPrograms1.Add(new Programmes1() { NameP = "Pepperoni", Detail = "yyyyy" });
}
BindingContext = this;
}
public List<Programmes1> ListPrograms1
{
get; set;
}
public class Programmes1
{
public string NameP { get; set; }
public string Detail { get; set; }
}
public async void Button_Clicked(object sender, System.EventArgs e)
{
await Navigation.PushAsync(new Page2());
}
}
}
and in the second page
Page2.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"
x:Class="app.Views.Page2">
<StackLayout VerticalOptions="Start" HorizontalOptions="Center">
<Label Text={Binding NameP}" />
<Label Text={Binding Detail}" />
</StackLayout>
</ContentPage>
So when I click on the button, I want to go to Page2 and pass datas from the previous page or another Modelpage.
What is the good way when you have lists of datas and want to use them in several pages ?
Do you recommend use a db ?
My problem is similar as a ListView ( when you click on item , you can go to another detailpage) but here I use a bindable layout.
As Jason's reply, you can pass data to another pages using constructors.
For the first page, you can find the current select item in Button_click event.
private async void Button_Clicked(object sender, EventArgs e)
{
Button btn = (Button)sender;
Programmes1 data = ListPrograms1.Find(s => s.NameP == btn.Text);
await Navigation.PushAsync(new Page26(data));
}
Then modify the second page constructors that having one parameter.
public partial class Page26 : ContentPage
{
public Programmes1 Model { get; set; }
public Page26(Programmes1 model)
{
InitializeComponent();
Model = model;
Console.WriteLine(Model.NameP);
this.BindingContext = this;
}
}
<StackLayout HorizontalOptions="Center" VerticalOptions="Start">
<Label Text="{Binding Model.NameP}" />
<Label Text="{Binding Model.Detail}" />
</StackLayout>
Please note: you need to bind ListPrograms1 to StackLayout BindableLayout.ItemsSource, not List1, because I don't find where is List1.

Progress Bar Not Updating Xamarin Forms MVVM

I know this has been asked before but I've spent ages and nothing has helped.
I'm trying to update a progress bar from a ViewModel however it will not update.
Recipe.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="FitnessScript.Views.Recipes">
<ContentPage.Content>
<StackLayout>
<Label Text="Please Enter Ingredients and Requirements!"
HorizontalOptions="Center"
VerticalOptions="Start" HorizontalTextAlignment="Center" TextType="Text"
Margin="0,20,0,0"
FontSize="25"/>
<Label Text="Enter Ingredients" Margin="5"/>
<Entry x:Name="Ingredients"
Text="{Binding Ingredients}"
Placeholder="Ingredients"
PlaceholderColor="LightGray" />
<Label Text="Enter Calories" Margin="5"/>
<Entry x:Name="Calories"
Text="{Binding Calories}"
Placeholder="Calories"
PlaceholderColor="LightGray" />
<Button x:Name="RecipeSearchBtn"
Text="Find Recipes"
Command="{Binding RequestRecipeCommand}" />
<ProgressBar x:Name="ProgressB"
Progress="{Binding ProgressValue}"
ProgressColor="Purple"
IsVisible="True"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Recipes.xmal.cs
namespace FitnessScript.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Recipes : ContentPage
{
RecipeSearchViewModel recipeSearchViewModel;
public Recipes()
{
recipeSearchViewModel = new RecipeSearchViewModel();
InitializeComponent();
BindingContext = recipeSearchViewModel;
}
}
}
RecipeSearchViewModel
namespace FitnessScript.ViewModels
{
public class RecipeSearchViewModel : BaseViewModel
{
private static readonly IRecipeService _recipeService = new RecipeService();
private readonly BackgroundWorker worker;
#region Getters/Setters
string _ingredients;
public string Ingredients
{
get { return _ingredients; }
set
{
_ingredients = value;
OnPropertyChanged("Ingredients");
}
}
int _calories;
public int Calories
{
get { return _calories; }
set
{
_calories = value;
OnPropertyChanged("Calories");
}
}
float _progressValue;
public float ProgressValue
{
get { return _progressValue; }
set
{
_progressValue = value;
OnPropertyChanged("ProgressValue");
}
}
#endregion
public RecipeSearchViewModel()
{
this.worker = new BackgroundWorker();
}
public Command RequestRecipeCommand
{
get
{
return new Command(async () => await RequestRecipe());
}
}
private async Task RequestRecipe()
{
await Task.Run(() =>
{
Device.BeginInvokeOnMainThread(() =>
{ ProgressValue = 1; }
);
});
List<string> ingredientsList = await _recipeService.GetRecipe(Ingredients, Calories);
App.Current.MainPage.DisplayAlert("Success", $"{Ingredients}, {Calories}", "Close");
}
}
}
I Have tired many different alternatives, such as setting ProgressValue to Double and Decimal, forcing the UI thread, with and without adding a parameter to OnPropertyChange(). I've attempted background works too, just nothing sadly.
I'm debugging using a S10+ via USB as I prefer it to emulation.
The overall aim is to press the RecipeSearchBtn, do the logic, and update the progress bar along with it, however for debugging purposes I just want to change the progress to 100% when the button command executes
Any help would be appreaciated, thanks
Also I have tried the Activity Indicator however similar issues, it never showed while debugging though my phone when setting the visibility ect to true through binding IsBool
About binding ActivityIndicator isvisible, I do one sample that you can take a look:
Please take a look the following code, ActivityIndicator display firstly, clicking button to load data, setting ActivityIndicator isVisible and IsRunning as false.
<StackLayout>
<Button
x:Name="btn1"
Command="{Binding command1}"
Text="load data" />
<ActivityIndicator
HeightRequest="50"
IsRunning="{Binding isvisible}"
IsVisible="{Binding isvisible}"
WidthRequest="50"
Color="Red" />
<ListView ItemsSource="{Binding students}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding name}" />
<Label Text="{Binding age}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
public partial class Page2 : ContentPage
{
public Page2()
{
InitializeComponent();
this.BindingContext = new studentviewmodel();
}
}
public class studentviewmodel:ViewModelBase
{
public ObservableCollection<studentmodel> students { get; set; }
public Command command1 { get; set; }
private bool _isvisible;
public bool isvisible
{
get { return _isvisible; }
set
{
_isvisible = value;
RaisePropertyChanged("isvisible");
}
}
public studentviewmodel()
{
command1 = new Command(loaddata);
isvisible = true;
students = new ObservableCollection<studentmodel>();
}
private async void loaddata()
{
//call service to do other something.
await Task.Delay(5000);
students.Add(new studentmodel() { name = "cherry", age = 29 });
students.Add(new studentmodel() { name = "barry", age = 30 });
students.Add(new studentmodel() { name = "annine", age = 15 });
isvisible = false;
}
}
public class studentmodel
{
public string name { get; set; }
public int age { get; set; }
}
The ViewModelBase is the class that implementing INotifyPropertyChanged, to notify data changed.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The screenshot:

Passing parameter from view-model to page and display it

I'm stuck with passing parameter from view model to page. On view model page i have list of properties which i increase by button , after button click sum is displyed on same page below that i collected that many of smth, my goal is to send this sum collected on this view model page to new page which i want to be responsible for displaying this sum . I'm stuck with passing parameter, it just don't update the value, it looks like the binding is okey becouse app don't throw exception that object has no reference. I'm begginer in xamarin and for any explanation or just direction which i can follow to achive this I would be very appreciated. Thank you in advance :)
ListViewModel code:
public class PersonListViewModel : INotifyPropertyChanged
{
public ObservableCollection<PersonViewModel> Persons { get; set; }
PersonViewModel selectedPerson;
double _sumcollected;
public double SumCollected
{
get => _sumcollected;
set
{
if (_sumcollected != value)
{
_sumcollected = value;
OnPropertyChanged("SumCollected");
}
}
}
public INavigation Navigation { get; set; }
public PersonListViewModel()
{
Persons = new ObservableCollection<PersonViewModel>
{
new PersonViewModel()
{
Name="Test", Surname="Test", Description= "TEsT", Background = "bgtest6.jpg", ProgressCounter =0.1, SavedClicked=0,Weight=1
},
new PersonViewModel()
{
Name="Test", Surname="Test", Description= "TEsT",Background = "bgtest6.jpg", ProgressCounter =0.1, SavedClicked=0,Weight=30
},
new PersonViewModel()
{
Name="Test", Surname="Test", Description= "TEsT",Background = "bgtest6.jpg", ProgressCounter =0.2, SavedClicked=0,Weight=100
},
new PersonViewModel()
{
Name="Test", Surname="Test", Description= "TEsT",Background = "bgtest6.jpg", ProgressCounter =0.3, SavedClicked=0,Weight=27
},
};
NavigateCommand = new Command<PersonViewModel>(NavigatationSolved);
IncreaseProgressCommand = new Command<PersonViewModel>(IncreaseProgress);
GotoCounterCommand = new Command<PersonListViewModel>(GotoNumbersPage);
NavigateSumPageCommand = new Command<PersonListViewModel>(NavigateSumPage);
}
private void NavigateSumPage(object obj)
{
Debug.WriteLine("Navigate to sum page ");
PersonListViewModel personListModel = obj as PersonListViewModel;
Navigation.PushAsync(new SumPage(personListModel));
}
//Passing SumCollected not working
private void GotoNumbersPage(object numbers)
{
PersonListViewModel personList = numbers as PersonListViewModel;
Navigation.PushAsync(new CounterPage(personList));
Debug.WriteLine("Next Page ?");
}
private void IncreaseProgress(object sender)
{
PersonViewModel person = sender as PersonViewModel;
if(person.ProgressCounter >= 1)
{
person.ProgressCounter -= person.ProgressCounter;
Application.Current.MainPage.DisplayAlert("Alert!", "Message after one progress bar", "GO!");
}
else
{
person.ProgressCounter += .2;
}
//Navigation.PushPopupAsync(new GratulationAlertPage());
person.SavedClicked += 1;
Debug.WriteLine("Saved Clicked");
SumCollected += 1;
SumCollected += person.Weight;
Debug.WriteLine("New SumCollected value");
}
}
ListViewModelPage code:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CommandDemo.Views.PersonListPage"
>
<NavigationPage.TitleView>
<StackLayout Orientation="Horizontal">
<Button Text="Numbers"
Command="{Binding Path=BindingContext.GotoCounterCommand}"
CommandParameter="{Binding .}"/>
</StackLayout>
</NavigationPage.TitleView>
<ContentPage.Content>
<StackLayout Padding="10"
Margin="10">
<ListView x:Name="personList"
ItemsSource="{Binding Persons}"
HasUnevenRows="True"
>
<!--SelectedItem="{Binding SelectedPerson}"-->
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1"
Command="{Binding Source={x:Reference personList},Path=BindingContext.NavigateCommand}"
CommandParameter="{Binding .}"/>
</StackLayout.GestureRecognizers>
<Label Text="{Binding Name}"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
HorizontalOptions="Center"
VerticalOptions="Center"
Margin="5,5,5,5"/>
<ProgressBar Progress="{Binding ProgressCounter}"/>
<Button Text="Add Progress"
Command="{Binding Source={x:Reference personList},Path=BindingContext.IncreaseProgressCommand}"
CommandParameter="{Binding .}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Label Text="{Binding SumCollected}"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
VerticalOptions="Center"
HorizontalOptions="Center"/>
<Button Text="Numbers"
Command="{Binding NavigateSumPageCommand}"
CommandParameter="{Binding .}"/>
</StackLayout>
</ContentPage.Content>
SumViewModel code:
public class CounterViewModel : INotifyPropertyChanged
{
private PersonListViewModel _personListView;
public PersonListViewModel PersonList
{
get => _personListView;
set
{
if (_personListView != value)
{
_personListView = value;
OnPropertyChanged("PersonList");
}
}
}
PersonViewModel _personView;
public PersonViewModel PersonView
{
get => _personView;
set
{
if (_personView != value)
{
_personView = value;
OnPropertyChanged("PersonView");
}
}
}
public double SumCollected
{
get => PersonList.SumCollected;
set
{
if (PersonList.SumCollected != value)
{
PersonList.SumCollected = value;
OnPropertyChanged("SumCollected");
}
}
}
private double _collected;
public double Collected
{
get => _collected;
set
{
if (_collected != value)
{
_collected = value;
OnPropertyChanged("Collected");
}
}
}
public CounterViewModel()
{
PersonList = new PersonListViewModel();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Page where i want to display sum collected from list page:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CommandDemo.Views.SumPage">
<ContentPage.Content>
<StackLayout>
<Label Text="{Binding PersonList.SumCollected}"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
Sum page code behind:
public partial class SumPage : ContentPage
{
public SumPage (PersonListViewModel personListModel)
{
InitializeComponent ();
BindingContext = new CounterViewModel();
}
}
You need to receive the object that you are passing in your viewmodel.
public CounterViewModel(PersonListViewModel personList)
{
PersonList = personList;
}

Xamarin remove item from list ObservableCollection and listview form

I have a simple app on Xamarin (a to-do list) which purpose is to dynamically create and remove items from a list. I am using ObservableCollection for the list. I spent a ton of time researching about this but I could not get it working.
Right now my app can add items to the list and display it in the main form. Now I want it to delete the corresponding items from the list with a click of a button.
Here is my code:
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App3"
x:Class="App3.MainPage">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Add" Clicked="addnewitem"/>
</ContentPage.ToolbarItems>
<ContentPage.BindingContext>
<local:viewmod/>
</ContentPage.BindingContext>
<StackLayout>
<Editor x:Name="txtboxNAME"></Editor>
<ListView ItemsSource="{Binding Tasks}" HasUnevenRows="True" x:Name="itemListView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame>
<StackLayout>
<Editor Text="{Binding Taskname}"/>
<Switch/>
<Button Text="Delete" CommandParameter="{Binding ItemName}" Clicked="DeleteClicked">
</Button>
</StackLayout>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
MainPage.xaml.cs (The code behind the MainPage form)
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace App3
{
public partial class MainPage : ContentPage
{
static int itemid = 0;
public MainPage()
{
InitializeComponent();
BindingContext = new viewmod();
}
private void addnewitem(object sender, EventArgs e)
{
var vm = BindingContext as viewmod;
string itemnameval = "item_" + itemid.ToString();
vm.AddItems(this.txtboxNAME.Text, itemnameval);
itemid++;
}
private void DeleteClicked(object sender, EventArgs e) // Item should be deleted from the list
{
// This does not work
var itemsender = (Xamarin.Forms.Button)sender;
var item = itemsender?.BindingContext as Task;
var vm = BindingContext as viewmod;
vm?.RemoveCommand.Execute(item);
//vm.Tasks.Remove(item); // conversion error
// This does not work either. "allItems" is not defined.
TaskClass listitem = (from itm in allItems
where itm.ItemName == item.CommandParameter.ToString()
select itm).FirstOrDefault<TaskClass>();
allItems.Remove(listitem);
}
}
}
TaskClass.cs
namespace App3
{
class TaskClass
{
public string Taskname { get; set; }
public string ItemName { get; set; }
}
}
viewmod.cs
using System.Collections.ObjectModel;
using Xamarin.Forms;
namespace App3
{
class viewmod
{
public ObservableCollection<TaskClass> Tasks { get; set; } = new ObservableCollection<TaskClass>();
public viewmod()
{
}
public void AddItems(string taskn, string taskid)
{
Tasks.Add(new TaskClass { Taskname = $"{taskn}", ItemName=$"{taskid}" });
}
public void DelItem(TaskClass task)
{
Tasks.Remove(task);
}
public Command<TaskClass> RemoveCommand
{
get
{
return new Command<TaskClass>((task) =>
{
Tasks.Remove(task);
});
}
}
}
}
modify your XAML first = the "." syntax passes the entire bound object
<Button Text="Delete" CommandParameter="{Binding .}" Clicked="DeleteClicked" />
then in your code behind
private void DeleteClicked(object sender, EventArgs e)
{
var itemsender = (Xamarin.Forms.Button)sender;
var item = (TaskClass)itemsender?.CommandParameter;
// it would be much cleaner to keep a ref to your VM in your page
// rather than continually casting it from BindingContext
var vm = BindingContext as viewmod;
vm.Tasks.Remove(item);
}

Categories

Resources