I wanted to try toggling a series of StackLayout's IsVisible property using a list of boolean. To better elaborate, here are my codes:
View
<ContentPage.Content>
<StackLayout>
<StackLayout IsVisible="{Binding pageVisible[0]}">
<Label Text="Page 1"/>
<Button Text="Next"
Command="{Binding ToNextCommand}"
CommandParameter="{Binding currentPage}"/>
</StackLayout>
<StackLayout IsVisible="{Binding pageVisible[1]}">
<Label Text="Page 2"/>
<Button Text="Next"
Command="{Binding ToNextCommand}"
CommandParameter="{Binding currentPage}"/>
</StackLayout>
<StackLayout IsVisible="{Binding pageVisible[2]}">
<Label Text="Page 3"/>
<Button Text="Next"
Command="{Binding ToNextCommand}"
CommandParameter="{Binding currentPage}"/>
</StackLayout>
</StackLayout>
</ContentPage.Content>
View Model
private List<bool> _pageVisible;
public List<bool> pageVisible
{
get => _pageVisible;
set
{
_pageVisible = value;
OnPropertyChanged();
}
}
public ProfileCompletionViewModel()
{
pageVisible = new List<bool>();
pageVisible.Add(true); //to make the first stacklayout visible
for (var i = 0; i < 2; i++)
{
pageVisible.Add(false);
}
}
public ICommand ToNextCommand { get; }
private int _currentPage = 0;
public int currentPage
{
get => _currentPage;
set => SetProperty(ref _currentPage, value);
}
private async Task ToNextFunc()
{
for (var i = 0; i < 3; i++)
pageVisible[i] = false; //Set all to false
pageVisible[_currentPage + 1] = true;
currentPage += 1;
}
}
The list is updating well when I clicked 'Next' (I found out using breakpoints). But the View is not updating despite having OnPropertyChange() on the List. Is there something wrong in my codes? T_T
Thank you in advance
I see, I just tried if it will work with a minimal line of code. I
guess I need to give each a separate property.
Yes, you can define several separate variables for each property,
but you can also create a model for the item of the list and implement interface INotifyPropertyChanged .
I create a demo to simulate this function, it works properly on my side. You can refer to the following code:
1.create a class Item for the list item.
Item.cs
public class Item: INotifyPropertyChanged
{
private bool _pageVisible;
public bool PageVisible
{
set { SetProperty(ref _pageVisible, value); }
get { return _pageVisible; }
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
MyViewModel.cs
public class MyViewModel: INotifyPropertyChanged
{
public List<Item> pageVisibleList { get; set; }
public MyViewModel()
{
pageVisibleList = new List<Item>();
pageVisibleList.Add( new Item { PageVisible = true}); //to make the first stacklayout visible
for (var i = 0; i < 2; i++)
{
pageVisibleList.Add(new Item { PageVisible = false });
}
}
public ICommand ToNextCommand => new Command(ToNextFunction);
private void ToNextFunction()
{
for (var i = 0; i < 3; i++)
pageVisibleList[i].PageVisible = false; //Set all to false
if (_currentPage < 2)
{
pageVisibleList[_currentPage + 1].PageVisible= true;
}
else {
_currentPage = 0;
pageVisibleList[0].PageVisible = true;
}
currentPage += 1;
}
private int _currentPage = 0;
public int currentPage
{
get => _currentPage;
set => SetProperty(ref _currentPage, value);
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
TestPage.xaml.cs
<?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:app1109="clr-namespace:App1109"
x:Class="App1109.TestPage">
<ContentPage.BindingContext>
<app1109:MyViewModel></app1109:MyViewModel>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<StackLayout IsVisible="{Binding pageVisibleList[0].PageVisible}">
<Label Text="Page 1"/>
<Button Text="Next --> Button 1"
Command="{Binding ToNextCommand}"
/>
</StackLayout>
<StackLayout IsVisible="{Binding pageVisibleList[1].PageVisible}">
<Label Text="Page 2"/>
<Button Text="Next --> Button 2"
Command="{Binding ToNextCommand}"
/>
</StackLayout>
<StackLayout IsVisible="{Binding pageVisibleList[2].PageVisible}">
<Label Text="Page 3"/>
<Button Text="Next --> Button 3"
Command="{Binding ToNextCommand}"
/>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Related
I have a Xamarin form where I have a list and two buttons. What I am seeing is that, depending where the buttons are, the model loads differently. Here is my Xaml code:
<?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="MyApp.Views.RewardsPage"
Title="{Binding Title}"
xmlns:local="clr-namespace:MyApp.ViewModels"
xmlns:model="clr-namespace:MyApp.Models" x:DataType="local:RewardsViewModel"
x:Name="BrowseItemsPage">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="650" />
</Grid.RowDefinitions>
<CollectionView x:Name="ItemsListView"
ItemsSource="{Binding Items}"
SelectionMode="None" Grid.Row="1" >
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="10" x:DataType="model:RewardModel">
<Label Text="{Binding id, StringFormat='ID: {0}'}"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemTextStyle}"
FontSize="16" />
<!--other labels removed for brevity-->
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.Footer>
<StackLayout Orientation="Horizontal">
<Button Text="Previous Month" Command="{Binding PreviousMonthCommand}" HorizontalOptions="FillAndExpand"></Button>
<Button Text="Next Month" Command="{Binding NextMonthCommand}" HorizontalOptions="FillAndExpand"></Button>
</StackLayout>
</CollectionView.Footer>
</CollectionView>
</Grid>
</ContentPage>
The code works fine here. But, if I move the StackLayout from the CollectionView.Footer to its own grid row, like this:
</CollectionView>
<!--other labels removed for brevity-->
</CollectionView>
<StackLayout Orientation="Horizontal" Grid.Row="2">
<Button Text="Previous Month" Command="{Binding PreviousMonthCommand}" HorizontalOptions="FillAndExpand"></Button>
<Button Text="Next Month" Command="{Binding NextMonthCommand}" HorizontalOptions="FillAndExpand"></Button>
</StackLayout>
Then the code in my RewardsViewModel executes in a different order. Here is my RewardsViewModel code (simplified):
[QueryProperty(nameof(CurrentMonth), nameof(CurrentMonth))]
[QueryProperty(nameof(CurrentYear), nameof(CurrentYear))]
public class RewardsViewModel: BaseViewModel
{
public ObservableCollection<RewardModel> Items { get; }
private List<MonthModel> months;
private int _current_year;
private int _current_month;
public Command PreviousMonthCommand { get; }
public Command NextMonthCommand { get; }
public RewardsViewModel()
{
Items = new ObservableCollection<RewardModel>();
PreviousMonthCommand = new Command(OnPrevious, ValidatePrevious);
NextMonthCommand = new Command(OnNext, ValidateNext);
}
public int CurrentYear
{
get
{
return _current_year ;
}
set
{
_current_year = value;
LoadItems();
}
}
public int CurrentMonth
{
get
{
return _current_month;
}
set
{
_current_month= value;
LoadItems();
}
}
public void LoadItems()
{
IsBusy = true;
//do stuff
}
private bool ValidatePrevious()
{
//do stuff to validate and return true or false
}
private bool ValidateNext()
{
//do stuff to validate and return true or false
}
private void OnPrevious()
{
//do stuyff
}
private void OnNext()
{
//do stuff
}
}
Depending on where the buttons reside in the Xaml page, the load events change:
When the buttons are within CollectionView, and the page loads, first the constructor loads then the Query Parameter setters load (CurrentMonth set then CurrentYear set)
When the buttons are outside the CollectionView, first the constructor loads then ValidatePrevious method is called and then ValidateNext method is called.
Why does the placement of the buttons in the Xaml file change the order of operations in my ViewModel? And, how do I ensure that the Query Parameter setters are called first, regardless of where the buttons reside?
Edit:
This is the code, from the previous page, that loads this page, passing in the Query Parameters:
async void OnItemSelected(MonthModel item)
{
if (item == null)
return;
await Shell.Current.GoToAsync($"{nameof(RewardsPage)}?{nameof(RewardsViewModel.CurrentYear)}={CurrentYear}&CurrentMonth={SelectedItem.month}");
}
Edit: Adding Base Class:
public class BaseViewModel : INotifyPropertyChanged
{
bool isBusy = false;
public bool IsBusy
{
get { return isBusy; }
set { SetProperty(ref isBusy, value); }
}
string title = string.Empty;
public string Title
{
get { return title; }
set { SetProperty(ref title, value); }
}
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName] string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Below is the code I'm using for checkbox in xamarin forms, but here I'm able to select only one item, I wanted to select multiple items from the checkbox. To the checkbox the data is binded from the database. Please help me
Checkforms.xaml.cs
public partial class Checkforms : ContentPage
{
private ObservableCollection<HelperModel> statusRecords;
string[] statusList;
public Checkforms()
{
InitializeComponent();
GetUserRoles();
}
public async void GetUserRoles()
{
HttpClient client = new HttpClient();
var response = await client.GetStringAsync("http://**********/api/Masters/getRoles");
var details = JsonConvert.DeserializeObject<List<HelperModel>>(response);
ListView1.ItemsSource = details;
}
private async void ListView1_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem == null) return;
var statusData = e.SelectedItem as HelperModel;
((ListView)sender).SelectedItem = null;
HttpClient client = new HttpClient();
var response = await client.GetStringAsync("http://********/api/Masters/getRoles");
var details = JsonConvert.DeserializeObject<List<HelperModel>>(response);
ListView1.ItemsSource = details;
var item = details.Where(x => x.name == statusData.name).FirstOrDefault();
if (item != null)
item.IsSelected = !item.IsSelected;
}
}
Checkforms.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="Checkbox_listview.Checkforms"
xmlns:lv="clr-namespace:Xamarin.Forms.MultiSelectListView;assembly=Xamarin.Forms.MultiSelectListView" Padding="0,20,0,0">
<ContentPage.Content>
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<!-- Place new controls here -->
<ListView x:Name="ListView1" ItemSelected="ListView1_ItemSelected" lv:MultiSelect.Enable="true">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout HorizontalOptions="FillAndExpand"
Orientation="Horizontal" Padding="10 ">
<Label Text="{Binding name}" HorizontalOptions="StartAndExpand"/>
<Image Source="select.png" IsVisible="{Binding IsSelected}"
VerticalOptions="Center" HeightRequest="40"
WidthRequest="40"/>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
HelperModel.cs
public class HelperModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isSelected = false;
public string name { get; set; }
public bool IsSelected
{
get { return isSelected; }
set
{
isSelected = value;
OnPropertyChanged("IsSelected");
}
}
//OnProperty changed method
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I'm trying to select multiple items from the checkbox after binding it from the database, from here only one item is selected at a time. please help how to select multiple items
Thanks in advance
You can try use CollectionView to replace the listview like following code. CollectionView have SelectionMode, you can set it to Multiple
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<!-- Place new controls here -->
<CollectionView x:Name="ListView1" ItemsSource="{Binding StatusRecords}" SelectionMode="Multiple"
SelectionChanged="ListView1_SelectionChanged">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout HorizontalOptions="FillAndExpand" Orientation="Horizontal" Padding="10 ">
<Label Text="{Binding name}" HorizontalOptions="StartAndExpand"/>
<Image Source="select.png" IsVisible="{Binding IsSelected}" VerticalOptions="Center" HeightRequest="40" WidthRequest="40"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
Here is running GIF.
=========Update===============
Do you want to Multiple pre-selection result?
If so, you should add the Property in your ViewModel. Note: No matter what is your model, please set the type of ObservableCollection to object
ObservableCollection<object> selectedHelperModels;
public ObservableCollection<object> SelectedHelperModels
{
get
{
return selectedHelperModels;
}
set
{
if (selectedHelperModels != value)
{
selectedHelperModels = value;
OnPropertyChanged("SelectedHelperModels");
}
}
}
Then If the IsSelected was selected to true. I will add it to the SelectedHelperModels.
public MyHelperViewModel()
{
StatusRecords = new ObservableCollection<HelperModel>();
StatusRecords.Add(new HelperModel() { IsSelected=false, name="test1" });
StatusRecords.Add(new HelperModel() { IsSelected = true, name = "test2" });
StatusRecords.Add(new HelperModel() { IsSelected = true, name = "test3" });
StatusRecords.Add(new HelperModel() { IsSelected = true, name = "test4" });
StatusRecords.Add(new HelperModel() { IsSelected = false, name = "test5" });
StatusRecords.Add(new HelperModel() { IsSelected = false, name = "test6" });
SelectedHelperModels = new ObservableCollection<object>();
foreach (var item in StatusRecords)
{
if (item.IsSelected)
{
SelectedHelperModels.Add(item);
}
}
}
In the foreground xaml. Add the SelectedItems="{Binding SelectedHelperModels}" in the CollectionView.
<CollectionView x:Name="ListView1" ItemsSource="{Binding StatusRecords}" SelectedItems="{Binding SelectedHelperModels}" SelectionMode="Multiple"
SelectionChanged="ListView1_SelectionChanged">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout HorizontalOptions="FillAndExpand" Orientation="Horizontal" Padding="10 ">
<Label Text="{Binding name}" HorizontalOptions="StartAndExpand"/>
<Image Source="{Binding IsSelected, Converter={StaticResource imageToBool}}" IsVisible="{Binding IsSelected} " VerticalOptions="Center" HeightRequest="40" WidthRequest="40"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
As you comment, you lack of the ListView1_SelectionChanged event. just add it in the layout background code.
public partial class MainPage : ContentPage
{
MyHelperViewModel myHelperViewModel;
public MainPage()
{
InitializeComponent();
myHelperViewModel= new MyHelperViewModel();
this.BindingContext = myHelperViewModel;
}
private void ListView1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
}
}
=========Update2============
Do you want to achieve the result like following GIF?
If so, I found the SelectionChanged event cannot achieve it easliy, and it cannot meet the MVVM requirement, So I add a TapGestureRecognizer for StackLayout in the CollectionView.
Here is code.
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<CollectionView x:Name="ListView1"
ItemsSource="{Binding StatusRecords}"
SelectedItems="{Binding SelectedHelperModels}"
SelectionMode="Multiple"
>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout HorizontalOptions="FillAndExpand" Orientation="Horizontal" Padding="10">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.ChangeCommand, Source={x:Reference Name=ListView1}}"
CommandParameter="{Binding .}"
/>
</StackLayout.GestureRecognizers>
<Label Text="{Binding name}" HorizontalOptions="StartAndExpand"/>
<Image Source="{Binding IsSelected, Converter={StaticResource imageToBool},Mode=TwoWay}" IsVisible="{Binding IsSelected, Mode=TwoWay}" VerticalOptions="Center" HeightRequest="40" WidthRequest="40"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
Here is ViewModel.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows.Input;
using Xamarin.Forms;
namespace SelectMutiPlyDemo
{
public class MyHelperViewModel: INotifyPropertyChanged
{
public ObservableCollection<HelperModel> StatusRecords { get; set; }
public ICommand ChangeCommand { protected set; get; }
ObservableCollection<object> selectedHelperModels;
public ObservableCollection<object> SelectedHelperModels
{
get
{
return selectedHelperModels;
}
set
{
if (selectedHelperModels != value)
{
selectedHelperModels = value;
OnPropertyChanged("SelectedHelperModels");
}
}
}
public MyHelperViewModel()
{
StatusRecords = new ObservableCollection<HelperModel>();
StatusRecords.Add(new HelperModel() { IsSelected=false, name="test1" });
StatusRecords.Add(new HelperModel() { IsSelected = true, name = "test2" });
StatusRecords.Add(new HelperModel() { IsSelected = true, name = "test3" });
StatusRecords.Add(new HelperModel() { IsSelected = true, name = "test4" });
StatusRecords.Add(new HelperModel() { IsSelected = false, name = "test5" });
StatusRecords.Add(new HelperModel() { IsSelected = false, name = "test6" });
SelectedHelperModels = new ObservableCollection<object>();
foreach (var item in StatusRecords)
{
if (item.IsSelected)
{
SelectedHelperModels.Add(item);
}
}
ChangeCommand=new Command<HelperModel>((key) =>
{
if (SelectedHelperModels.Contains<object>(key))
{
SelectedHelperModels.Remove(key);
}
else
{
SelectedHelperModels.Add(key);
}
key.IsSelected = !key.IsSelected;
});
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
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:
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;
}
I'm fairy new to Xamarin, and I trying to do a switching List. So when button is pressed, it switches to another list. I have CustomLists class which wraps all this lists and exposes ChosenList property, which gives access to list currenly being displayed. When entry in List is deleted Command property gets called
public ICommand DeleteTest
{
get { return new Command<TaskRecord>((s) => OnDelete(s)); }
}
void OnDelete(TaskRecord task)
{
List.Remove(task);
IsUnfinishedChanged_ = !task.IsFinished;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ChosenList"));
System.Diagnostics.Debug.Print("Deleting..");
}
When I delete entry in a first list (those shown at the programm start) it works fine. But in a second, ListView doesn't update for some reason
Here's my XAML code
<ListView ItemsSource="{Binding ChosenList}" IsPullToRefreshEnabled="True"
Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" SeparatorColor="DimGray">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem Text="Delete" IsDestructive="true"
Command="{Binding Source={x:Reference MainGrid},
Path=BindingContext.DeleteTest}"
CommandParameter="{Binding .}"/>
</ViewCell.ContextActions>
<StackLayout Padding="15,0" VerticalOptions="Center">
<Label Text="{Binding Path=Name}" FontSize="Large" Font="Arial"
FontAttributes="Bold" VerticalOptions="Center"/>
<Label Text="{Binding Path=ShortDescr}" FontSize="Micro" Font="Arial"
VerticalOptions="Center"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Edit: ChosenList is wrapper for ListView to bind to
public List<TaskRecord> ChosenList
{
get
{
return (IsAll_ ? List : Unfinished);
}
}
Edit 2: Putting the whole code up here
private List<TaskRecord> List { get; set; }
private List<TaskRecord> UnfinishedCache_;
private List<TaskRecord> Unfinished
{
get
{
if (IsUnfinishedChanged_)
{
UnfinishedCache_ = new List<TaskRecord>();
foreach (TaskRecord task in List)
{
if (!task.IsFinished) UnfinishedCache_.Add(task);
}
IsUnfinishedChanged_ = false;
}
return UnfinishedCache_;
}
set { UnfinishedCache_ = value; }
}
private bool IsUnfinishedChanged_=true;
private bool IsAll_;
public ICommand ListChangeCommand
{
get { return new Command<string>((s)=>OnListSwitch(s)); }
}
void OnListSwitch(string senderText)
{
if (senderText == "All" && !IsAll_)
{
IsAll_ = true;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ChosenList"));
}
else if (senderText == "Unfinished" && IsAll_)
{
IsAll_ = false;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ChosenList"));
}
}
public ICommand DeleteTest
{
get { return new Command<TaskRecord>((s) => OnDelete(s)); }
}
void OnDelete(TaskRecord task)
{
List.Remove(task);
IsUnfinishedChanged_ = !task.IsFinished;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ChosenList"));
System.Diagnostics.Debug.Print("Deleting..");
}
public event PropertyChangedEventHandler PropertyChanged;
public List<TaskRecord> ChosenList
{
get
{
return (IsAll_ ? List : Unfinished);
}
}