Xamarin - Bind view model in xaml - c#

I'm binding grouped listview to view model data source via xaml.
But when the app is launched the list is empty even when I populate it from code behind in ctor. When I declare the ListView x:Name="myList" and populate it from code behind it works, but it's not "binded" to the view model directly.
<?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:viewModels="clr-namespace:Atc.Obedy.ViewModels;assembly=Atc.Obedy"
x:Class="Atc.Obedy.MainPage" Title="Jídelníček">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness"
iOS="20, 40, 20, 20"
Android="20, 20, 20, 20"
WinPhone="20, 20, 20, 20" />
</ContentPage.Padding>
<ContentPage.BindingContext>
<viewModels:MainPageViewModel />
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
Orientation="Vertical"
Spacing="15">
<ListView BindingContext="{ Binding MealsGroups }" GroupDisplayBinding="{ Binding DisplayName }" IsGroupingEnabled="true">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical">
<StackLayout Orientation="Horizontal">
<Label Text="{Binding MealTitle}" />
<Button Image="icon.png" Command="{ Binding OrderCommand }" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Code behind:
public partial class MainPage : ContentPage
{
protected IMealsRepository MealsRepository { get; }
public MainPage()
{
MealsRepository = IoC.Container.Get<IMealsRepository>();
var mealGroups = new ObservableCollection<MealsGroup>();
foreach (var meals in MealsRepository.GetMeals(DateTime.MinValue, DateTime.MaxValue).Where(x => x.DayOfOrder != null).GroupBy(x=>x.DayOfOrder))
{
mealGroups.Add(ProvideMealsGroup(meals.Key.Value, meals));
}
InitializeComponent();
var viewModel = BindingContext as MainPageViewModel;
viewModel.MealsGroups = mealGroups;
}
This is View Model:
public class MainPageViewModel : INotifyPropertyChanged, IViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<MealsGroup> _mealGroups = null;
public ObservableCollection<MealsGroup> MealsGroups
{
get { return _mealGroups; }
set
{
_mealGroups = value;
OnPropertyChanged(nameof(MealsGroups));
}
}
public ICommand OrderCommand { get; set; }
public MainPageViewModel()
{
OrderCommand = new Command(() =>
{
Debug.WriteLine("MealsGroups");
});
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And meal group
public class MealsGroup : List<MealViewModel>
{
public string DisplayName { get; set; }
public string ShortName { get; set; }
public event MealGroupOrderSwitched MealGroupOrderSwitched;
public ICommand OrderCommand { get; set; }
public MealsGroup()
{
OrderCommand = new Command(() =>
{
Debug.WriteLine("MealGroup");
});
}
public void AddMeal(Meal meal)
{
var model = new MealViewModel
{
IsOrdered = meal.IsOrdered,
MealTitle = meal.MealTitle,
MealId = meal.MealId.Value,
DayOfOrder = meal.DayOfOrder.Value,
IsOrderable = meal.IsOrderable,
IsSoup = meal.IsSoup
};
model.MealOrdered += meaId =>
{
for (var i = 0; i < Count; i++)
{
var mealViewModel = this[i];
if (mealViewModel.MealId != meaId && mealViewModel.IsOrderable)
mealViewModel.IsOrdered = false;
}
MealGroupOrderSwitched?.Invoke(this);
};
Add(model);
}
}
but the android app when launched has empty list. Even when I added items in code behind in ctor.

Solved by changing in xaml ListView property BindingContext={binding MealsGroups} to ItemsSource={binding MealsGroups}

Related

how to output data from one page to another from the collection of the selected item?

I have two XAML pages and one their common ViewModel page.I want to output data from one page to another from the collection of the selected item. It must be Label`s Text.
I have 2 problems
1)I can not bind text from label to the object field
2)If I bind Label`s Text to a variable.I can see data only on the current page. But if I go to another page and place the same label there, the information is not displayed.I do not understand why so because on the next page the same variable which already contains data
FIRST XAML PAGE
<?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="App1.TryPage">
<ContentPage.Content>
<StackLayout>
<CollectionView x:Name="AddCar" ItemsSource="{Binding Hearts}"
SelectionMode="None">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical"
Span="2" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="135" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<Frame CornerRadius="10" BorderColor="Black" Padding="0" >
<Button
CornerRadius="10" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="135" WidthRequest="150"
BackgroundColor="{Binding CustButtonColor}" ImageSource="{Binding Image}"
Command="{ Binding BindingContext.ChangeColor,
Source={x:Reference Name=AddCar} }" CommandParameter="{Binding .}"/>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Label x:Name="small12" FontSize="Large" HorizontalOptions="Center" VerticalOptions="Start" Text="{Binding tryHeart.TypeHeart}" />
<Button Text="Navigate" Command="{Binding navigateCommand }">
</StackLayout>
</ContentPage.Content>
</ContentPage>
CODE BEHIND
public partial class TryPage : ContentPage
{
public TryPage()
{
InitializeComponent();
BindingContext = new TryPageCS(this.Navigation);
}
}
VIEW MODEL PAGE
public class TryPageCS : INotifyPropertyChanged
{
public ObservableCollection<CircleColor> Hearts { get; set; }
public ICommand ChangeColor { protected set; get; }
public TryHeart tryHeart { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
INavigation Navigation { get; set; }
public Command navigateCommand { get; set; }
public async Task GotoPage2()
{
await Navigation.PushModalAsync(new MainPage());
}
public TryPageCS(INavigation navigation)
{
tryHeart = new TryHeart();
this.Navigation = navigation;
this.navigateCommand = new Command(async () => await GotoPage2());
Hearts = new ObservableCollection<CircleColor>();
Hearts.Add(new CircleColor() { Name = "one", Image = "heart", CustButtonColor = Color.White });
Hearts.Add(new CircleColor() { Name = "two", Image = "heart", CustButtonColor = Color.White });
Hearts.Add(new CircleColor() { Name = "three", Image = "heart", CustButtonColor = Color.White });
Hearts.Add(new CircleColor() { Name = "four", Image = "heart", CustButtonColor = Color.White });
var DefaultCars = new ObservableCollection<CircleColor>();
DefaultCars = Hearts;
ChangeColor = new Command<CircleColor>((key) =>
{
foreach (var item in Hearts)
{
item.CustButtonColor = Color.White;
item.Image = "heart";
}
var car = key as CircleColor;
car.CustButtonColor = Color.LightCoral;
tryHeart.TypeHeart = car.Name;
});
}
}
SECOND PAGE
<?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"
xmlns:controls="clr-namespace:App1"
x:Class="App1.MainPage">
<StackLayout>
<Label FontSize="Large" Text="{Binding tryHeart.TypeHeart}" />
</StackLayout>
</ContentPage>
CODE BEHIND
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new TryPageCS(this.Navigation);
}
}
Also I have a class
public class TryHeart : INotifyPropertyChanged
{
string typeHeart;
public string TypeHeart
{
set
{
if (typeHeart != value)
{
typeHeart = value;
OnPropertyChanged("TypeHeart");
}
}
get
{
return typeHeart;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I will explain why I need it. In my real project I have to collect information about the car from different pages. object of this class it will be my machine. Therefore I want to write down the collected data in object of a class and then on the last page to display data
On the SECOND XAML PAGE I write only THE SAME LABEL
<Label x:Name="small123" FontSize="Large" HorizontalOptions="Center" VerticalOptions="Start" Text="{Binding Name}" />
Please,help me with my 2 problems
1)Why I can not to write
<Label x:Name="small12" FontSize="Large" HorizontalOptions="Center" VerticalOptions="Start" Text="{Binding tryHeart.TypeHeart}" />
Information does not display
2)How I must display information from one page from collection view of selected item to another page
Navigation does not contain a definition for PushModalAsync
For question 1
TryHeart in the ViewModel is a private property in your case . You need to set it as public .
public TryHeart tryHeart {get;set;}
public TryPageCS()
{
//...
tryHeart = new TryHeart();
//...
}
For question 2
If you want to handle navigation logic in VM , you need to pass the current navigation from current page .
in ViewModel
Add a property
INavigation CurrentNavigation { get; set; }
public TryPageCS(INavigation navigation)
{
CurrentNavigation = navigation;
}
And now you can use the property in the method
await CurrentNavigation.PushModalAsync(new MainPage());
in ContentPage
Pass the Navigation as params
BindingContext = new TryPageCS(this.Navigation);

Multiselect checkbox in xamarin forms cross platform

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

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.Forms binding does not work

I try to rewrite my UWP C# app for Windows10 to Xamarin app using XAML. But Binding (for example here in ListView ItemSource=...) is not working for me and I don´t know why.
Visual Studio tells me, Cannot Resolve Symbol Recording due to unknown Data Context.
Here is my XAML (MainPage.xaml) for testing purpose:
<?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:XamarinTest;assembly=XamarinTest"
x:Class="XamarinTest.MainPage">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<ListView x:Name="listView" IsVisible="false" ItemsSource="{Binding Recording}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Horizontal">
<Image Source="Accept" WidthRequest="40" HeightRequest="40" />
<StackLayout Orientation="Vertical" HorizontalOptions="StartAndExpand">
<Label Text="TEST" HorizontalOptions="FillAndExpand" />
<Label Text="TEST" />
</StackLayout>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ContentPage>
Here is C# (MainPage.xaml.cs):
namespace XamarinTest
{
public partial class MainPage : ContentPage
{
public MainPage()
{
this.InitializeComponent();
this.AllTestViewModel = new RecordingViewModel();
this.BindingContext = AllTestViewModel;
}
public RecordingViewModel AllTestViewModel { get; set; }
}
}
And finally ViewModel (RecordingViewModel.cs):
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using XamarinTest.Model;
namespace XamarinTest.ViewModel
{
public class RecordingViewModel : INotifyPropertyChanged
{
public ObservableCollection<Recording> Recordings { get; } = new TrulyObservableCollection<Recording>();
public RecordingViewModel()
{
Recordings.Add(new RecordingTest2()
{
TestName = "Test 1",
TestNote = "Vytvoreni DB",
TestTime = new TimeSpan(0, 0, 0)
});
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public sealed class TrulyObservableCollection<T> : ObservableCollection<T>
where T : INotifyPropertyChanged
{
public TrulyObservableCollection()
{
CollectionChanged += FullObservableCollectionCollectionChanged;
}
public TrulyObservableCollection(IEnumerable<T> pItems) : this()
{
foreach (var item in pItems)
{
this.Add(item);
}
}
private void FullObservableCollectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (Object item in e.NewItems)
{
((INotifyPropertyChanged)item).PropertyChanged += ItemPropertyChanged;
}
}
if (e.OldItems != null)
{
foreach (Object item in e.OldItems)
{
((INotifyPropertyChanged)item).PropertyChanged -= ItemPropertyChanged;
}
}
}
private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, sender, sender, IndexOf((T)sender));
OnCollectionChanged(args);
}
}
}
Everything (models and viewmodels) are working in native UWP Windows 10 app. Only the Binding and making same view is problem in Xamarin. Could someone please help with binding?
Thx.
EDIT
Recording.cs is here:
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace XamarinTest.Model
{
public abstract class Recording : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string TestName { get; set; }
private TimeSpan _testTime;
private string _testNote;
private string _actualIco = "Play";
private bool _isActive = false;
private bool _enabled = true;
public double IcoOpacity { get; private set; } = 1.0;
public string ActualIco
{
get => _actualIco;
set
{
if (_actualIco == null) _actualIco = "Admin";
_actualIco = value;
NotifyPropertyChanged("ActualIco");
}
}
public bool IsActive
{
get => _isActive;
set
{
if (_isActive == value) return;
_isActive = value;
IcoOpacity = !value ? 1.0 : 0.3;
NotifyPropertyChanged("IsActive");
NotifyPropertyChanged("IcoOpacity");
}
}
public bool Enabled
{
get => _enabled;
set
{
if (_enabled == value) return;
_enabled = value;
NotifyPropertyChanged("Enabled");
}
}
public string TestNote
{
get => _testNote;
set
{
if (_testNote == value) return;
_testNote = value;
NotifyPropertyChanged("TestNote");
}
}
public TimeSpan TestTime
{
get => _testTime;
set
{
if (_testTime == value) return;
_testTime = value;
NotifyPropertyChanged("TestTime");
}
}
protected Recording()
{
TestName = "Unkonwn";
TestNote = "";
_testTime = new TimeSpan(0, 0, 0);
}
protected Recording(string testName, string testNote, TimeSpan testTime)
{
TestName = testName;
TestNote = testNote;
_testTime = testTime;
}
public string OneLineSummary => $"{TestName}, finished: "
+ TestTime;
private void NotifyPropertyChanged(string propertyName = "")
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public abstract bool playTest();
}
}
I tried add DataContext in XAML (postet in origin question), because of intellisence like this:
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:dvm="clr-namespace:XamarinTest.ViewModel"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
d:DataContext="{system:Type dvm:RecordingViewModel}"
and this to Grid:
<Label Text="{Binding Recordings[0].TestName}" Grid.Row="0" Grid.Column="2" />
IntelliSence is OK, but text doesn´t show in app.
Finally is working!
XAML should looks like code below.
Imporant is xmls:viewModel="..." and <ContentPage.BindingContext>...</>.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModel="clr-namespace:XamarinTest.ViewModel;assembly=XamarinTest"
x:Class="XamarinTest.MainPage"
>
<ContentPage.BindingContext>
<viewModel:RecordingViewModel/>
</ContentPage.BindingContext>
<ListView x:Name="listView" ItemsSource="{Binding Recordings}" Grid.Row="1" Grid.Column="1">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Horizontal">
<Image Source="Accept" WidthRequest="40" HeightRequest="40" />
<StackLayout Orientation="Vertical" HorizontalOptions="StartAndExpand">
<Label Text="{Binding TestName}" HorizontalOptions="FillAndExpand" />
<Label Text="{Binding TestNote}" />
</StackLayout>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ContentPage>
and MainPage.xaml.cs is okey
namespace XamarinTest
{
public partial class MainPage : ContentPage
{
public MainPage()
{
this.InitializeComponent();
this.AllTestViewModel = new RecordingViewModel();
this.BindingContext = AllTestViewModel;
}
public RecordingViewModel AllTestViewModel { get; set; }
}
}
looking at your ViewModel, it looks like there is no Recording member, but you do have a Recordings member.
EDIT
So you are adding your DataContext in the code behind so ignore the Xaml part.
Your View (MainPage.xaml) has a ViewModel(RecordingViewModel.cs). The ViewModel has a member called Recordings (a collection of type Recording). But in your Xaml, you are try to bind to Recording.
Change:
<ListView x:Name="listView" IsVisible="false" ItemsSource="{Binding Recording}">
to:
<ListView x:Name="listView" IsVisible="false" ItemsSource="{Binding Recordings}">
2nd EDIT
The only Labels in your example is the one inside of the ListView yes?
If so, you can access the Recordings children like TestNote by:

Categories

Resources