Xamarin, Label Text in xaml doesn't change on property changed WITH MVVM Helpers - c#

In my Xaml the values only update when, i go into the xaml and do this for example:
{Binding use.currentlevel}->{Binding use.currentleve}->{Binding use.currentlevel}
but not when the use variable is updated upon launch and aqustion of data from the database, i cant figure out why.
P.S. i set the bindingcontext in xaml file.
AboutPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="INWORK.Views.AboutPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:control="clr-namespace:ProgressRingControl.Forms.Plugin;assembly=ProgressRing.Forms.Plugin"
xmlns:vm="clr-namespace:INWORK.ViewModels"
Title="{Binding Title}"
BackgroundImage="MainBackground.png">
<ContentPage.BindingContext>
<vm:AboutViewModel />
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<Color x:Key="Accent">#96d1ff</Color>
<Color x:Key="Muscular">#E76F51</Color>
<Color x:Key="Cardio">#429EA6</Color>
</ResourceDictionary>
</ContentPage.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="3*" />
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
<RowDefinition Height="1.15*" />
</Grid.RowDefinitions>
<Ellipse
Grid.Row="1"
Fill="Gray"
HeightRequest="160"
HorizontalOptions="Center"
Stroke="#FFFF9900"
VerticalOptions="Center"
WidthRequest="160" />
<control:ProgressRing
Grid.Row="1"
HeightRequest="100"
Progress="{Binding use.muscularprogress}"
RingProgressColor="{StaticResource Muscular}"
RingThickness="20"
Scale="1"
WidthRequest="100"
class="pro" />
<control:ProgressRing
Grid.Row="1"
HeightRequest="100"
Progress="{Binding use.cardioprogress}"
RingProgressColor="{StaticResource Cardio}"
RingThickness="20"
Scale="0.85"
class="pro" />
<StackLayout Grid.Row="1" VerticalOptions="Center">
<StackLayout Orientation="Horizontal" HorizontalOptions="Center">
<Label
x:Name="Level"
FontAttributes="Bold"
FontSize="20"
HorizontalOptions="CenterAndExpand"
Text="Level "
TextColor="Black" />
<Label
FontAttributes="Bold"
FontSize="20"
HorizontalOptions="CenterAndExpand"
Text="{Binding use.currentlevel}"
TextColor="Black" />
<Button Command="{Binding GoInfoCommand}"></Button>
</StackLayout>
<Label
x:Name="Totalprocent"
FontAttributes="Bold"
FontSize="20"
HorizontalOptions="CenterAndExpand"
Text="0%"
TextColor="Black" />
</StackLayout>
<Grid Grid.Row="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<StackLayout Grid.Column="0">
<Label
Padding="2"
FontAttributes="Bold"
FontSize="20"
HorizontalOptions="Center"
Text="Muscular"
TextColor="{StaticResource Muscular}" />
<StackLayout HorizontalOptions="Center" Orientation="Horizontal">
<Label
FontAttributes="Bold"
FontSize="20"
Text="{Binding use.muscularprogress}"
TextColor="Black" />
<Label
FontAttributes="Bold"
FontSize="20"
Text="%"
TextColor="Black" />
</StackLayout>
</StackLayout>
<StackLayout Grid.Column="2">
<Label
x:Name="easier"
FontAttributes="Bold"
FontSize="20"
HorizontalOptions="Center"
Text="Cardio"
TextColor="{StaticResource Cardio}" />
<StackLayout HorizontalOptions="Center" Orientation="Horizontal">
<Label
FontAttributes="Bold"
FontSize="20"
Text="{Binding use.cardioprogress}"
TextColor="Black" />
<Label
FontAttributes="Bold"
FontSize="20"
Text="%"
TextColor="Black" />
</StackLayout>
</StackLayout>
</Grid>
</Grid>
</ContentPage>
LevelProgress.cs Model
using SQLite;
using System;
using System.Collections.Generic;
using System.Text;
namespace INWORK.Models
{
public class LevelProgress
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int currentlevel { get; set; }
public bool pushups;
public bool squats;
public bool pullups;
public bool splitsquats;
public bool stepups;
public bool tricepdips;
public bool legraises;
//Cardio section
public bool running;
public bool intervals;
public double muscularprogress { get; set; }
public double cardioprogress { get; set; }
}
}
Service for accsessing local database
using INWORK.Models;
using SQLite;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Essentials;
namespace INWORK.Services
{
internal class DataStorage
{
private static SQLiteAsyncConnection db;
private static async Task Init()
{
if (db != null)
return;
var databasePath = Path.Combine(FileSystem.AppDataDirectory, "test2.db");
db = new SQLiteAsyncConnection(databasePath);
await db.CreateTableAsync<LevelProgress>();
await db.CreateTableAsync<Overview>();
}
public static async Task FirstCreation()
{
await Init();
LevelProgress LevelProgress = new LevelProgress()
{
currentlevel = 1,
cardioprogress = 0,
muscularprogress = 0,
pushups = false,
squats = false,
pullups = false,
splitsquats = false,
stepups = false,
tricepdips = false,
legraises = false
};
await db.InsertAsync(LevelProgress);
}
public static async Task EditProgress(LevelProgress usehere)
{
await Init();
await db.UpdateAsync(new LevelProgress()
{
Id = 1,
currentlevel = usehere.currentlevel,
muscularprogress = usehere.muscularprogress,
pushups = usehere.pushups,
squats = usehere.squats,
pullups = usehere.pullups,
splitsquats = usehere.splitsquats,
stepups = usehere.stepups,
tricepdips = usehere.tricepdips,
legraises = usehere.legraises,
cardioprogress = usehere.cardioprogress,
running = usehere.running,
intervals = usehere.intervals
});
}
public static async Task FinishWorkout()
{
}
public static async Task<LevelProgress> GetProgress()
{
await Init();
var levelProgress = await db.Table<LevelProgress>().FirstOrDefaultAsync();
//var levelProgress = await db.Table<LevelProgress>().ToListAsync();
return levelProgress;
}
public static async Task AddWorkout(string _Workout_type, int _Result, DateTime _Date)
{
await Init();
Overview Overview = new Overview()
{
Workout_type = _Workout_type,
Result = _Result,
Date = _Date
};
await db.InsertAsync(Overview);
}
public static async Task<IEnumerable<Overview>> GetOverview(string type)
{
await Init();
IEnumerable<Overview> overview;
if (type == "Running" || type == "Intervals")
{
overview = await db.Table<Overview>().Where(v => v.Workout_type == "Running" || v.Workout_type == "Intervals").ToListAsync();
}
else
{
overview = await db.Table<Overview>().Where(v => v.Workout_type != "Running" || v.Workout_type != "Intervals").ToListAsync();
}
return overview;
}
}
}
AboutViewModel
using INWORK.Models;
using INWORK.Services;
using MvvmHelpers;
using System;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace INWORK.ViewModels
{
public class AboutViewModel : ViewModelBase
{
public ICommand GoInfoCommand { get; set; }
public AboutViewModel()
{
Title = "About";
OpenWebCommand = new Command(async () => await Browser.OpenAsync("https://aka.ms/xamarin-quickstart"));
//Command = "{Binding OpenWebCommand}
Task.Run(async () => await Loadup());
//use.currentlevel = use.currentlevel;
}
private LevelProgress pp;
private LevelProgress _use;
public LevelProgress use
{
get => _use;
set
{
_use = value;
OnPropertyChanged();
}
}
public async Task Loadup()
{
_use = new LevelProgress();
var temps = await DataStorage.GetProgress();
use = temps;
//await ProgressTracker.AddWorkout("Ŗunning",2, DateTime.Today);
if (use.currentlevel == 0)
{
await DataStorage.FirstCreation();
Loadup();
}
}
public ICommand OpenWebCommand { get; }
}
}

Yes,if we want to update the UI after we change the field(muscularprogress,cardioprogress) in object use, we need to make class LevelProgress implement interface INotifyPropertyChanged.
Since you have has base class ViewModelBase, we can do like this:
public class LevelProgress: ViewModelBase
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int currentlevel { get; set; }
public bool pushups;
public bool squats;
public bool pullups;
public bool splitsquats;
public bool stepups;
public bool tricepdips;
public bool legraises;
//Cardio section
public bool running;
public bool intervals;
//public double muscularprogress { get; set; }
private double _muscularprogress;
public double muscularprogress
{
get => _muscularprogress;
set { SetProperty(ref _muscularprogress, value); }
}
//public double cardioprogress { get; set; }
private double _cardioprogress;
public double cardioprogress
{
get => _cardioprogress;
set { SetProperty(ref _cardioprogress, value); }
}
}
Note:
As a test , I created a fake object with special value for it and assign it's value for use at the beginning,after that we change it's value, and the UI could refresh automatically.
private void test(object obj)
{
use.muscularprogress = 98.8;
use.cardioprogress = 12.9;
}

Related

How can you bind a Label to a function result in Xamarin.Forms

I'm trying to bind a Label to the result of the GetPlayCount() function call. The other bindings, for Name and Category, are working as expected, but there is no output for the third label
XAML:
<ListView ItemsSource="{Binding Games}"
HasUnevenRows="true"
HeightRequest="200"
SeparatorVisibility="Default">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid Margin="0" Padding="0" RowSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Margin="0" Text="{Binding Name}"/>
<Label Grid.Column="1" Margin="0" Text="{Binding Category}"/>
<!--This following Label is the one not binding -->
<Label Grid.Column="2" Margin="0" Text="{Binding GetPlayCount}" />
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Code Behind:
public partial class CollectionPage : ContentPage
{
CollectionViewModel collectionView = new CollectionViewModel();
public CollectionPage()
{
InitializeComponent();
BindingContext = collectionView;
}
}
ViewModel:
public class CollectionViewModel : INotifyPropertyChanged
{
private ObservableCollection<Game> games;
public ObservableCollection<Game> Games
{
get { return games; }
set
{
games = value;
OnPropertyChanged("Games");
}
}
public CollectionViewModel()
{
GetGames();
}
public async void GetGames()
{
var restService = new RestService();
Games = new ObservableCollection<Game>(await restService.GetGamesAsync());
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Model:
public class Game
{
public string Name { get; set; }
public string Category { get; set; }
public async Task<int> GetPlayCount()
{
return (await new RestService().GetGamesAsync()).Where(result => result.Name == this.Name).Count();
}
}
You can bind only to the property. You may call that function from the property getter. However it is not possible to bind to the function. The app doesn't know when your function is updated, so binding wouldn't make much sense. For the property you can call PropertyChanged to signal that the property has a new value.
I will talk with code:
[NotMapped]
public decimal TotalPrice { get =>GetTotalPrice(); }
private decimal GetTotalPrice()
{
decimal result = 0;
foreach(var dpo in DetailPurchaseOrder)
{
result = result + dpo.GetTotalPurchasePrice();
}
return result;
}

ObservableCollection not loading data correctly

Just a bit of background of what I am trying to do, I have a syncfusion listview that updates the data from the database with a REST api call. I get weekly data. There is another button on the view that should updates the list with monthly data.
For weekly, I have 1 record in the db and list gets populated with data. After clicking on the "view all" button, the db gives the data and list receives 13 records but the view update just one of it. Looks like the list has limited to show just one. Here is the code:
private ObservableCollection<TransactionInformationDto> listItems;
public ObservableCollection<TransactionChartData> ChartData { get; set; }
public ObservableCollection<TransactionInformationDto> TransactionList
{
get { return listItems; }
set { listItems = value; OnPropertyChanged(nameof(TransactionList)); }
}
public ICommand GetTransactions => new Command(async () =>
{
IsBusy = true;
TransactionList.Clear();
var data = await GetAllTransactions();
foreach(var item in data)
{
TransactionList.Add(item);
}
IsBusy = false;
});
public Command<object> ItemTappedCommand
{
get
{
return this.itemTappedCommand ?? (this.itemTappedCommand = new Command<object>(ShowTransactionInformation));
}
}
private void ShowTransactionInformation(object item)
{
var list = item as Syncfusion.ListView.XForms.ItemTappedEventArgs;
var transaction = (TransactionInformationDto)list.ItemData;
Navigation.PushAsync(new TransactionInfoPage(transaction));
}
#endregion
#region Constructor
public DashboardPageViewModel(INavigation navigation)
{
LoadTransactionDetails();
Navigation = navigation;
}
#endregion
#region Properties
public double TotalBalance
{
get
{
return totalBalance;
}
set
{
this.totalBalance = value;
this.OnPropertyChanged();
}
}
public INavigation Navigation { get; }
#endregion
#region Methods
private async Task<ObservableCollection<TransactionInformationDto>> GetAllTransactions()
{
var retrievalInformation = await App.Database.GetUserRetrievalInformation();
return new ObservableCollection<TransactionInformationDto>(await DependencyService.Get<IGetInformation>().GetAllUserTransactions(retrievalInformation));
}
private void LoadTransactionDetails()
{
var userTransactions = new List<TransactionInformationDto>();
Task.Run(async () => {
var retrievalInformation = await App.Database.GetUserRetrievalInformation();
var userBalance = await DependencyService.Get<IGetInformation>().GetUserBalanceInformation(retrievalInformation);
TotalBalance = userBalance.CurrentBalance;
userTransactions = await DependencyService.Get<IGetInformation>().GetTransactionData(retrievalInformation);
});
Thread.Sleep(1000);
WeekData(userTransactions);
}
private void WeekData(List<TransactionInformationDto> transactionInformation)
{
TransactionList = new ObservableCollection<TransactionInformationDto>();
var data = new ObservableCollection<TransactionInformationDto>(transactionInformation.OrderByDescending(x =>x.TimeStamp));
foreach(var item in data)
{
TransactionList.Add(item);
}
days = new string[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
ChartData = new ObservableCollection<TransactionChartData>();
UpdateChartData(days);
}
XAML
<Grid Grid.Row="1">
<Label Margin="16,26,16,16"
Text="TRANSACTIONS"
TextColor="{StaticResource Gray-800}"
FontSize="12"
LineHeight="{OnPlatform Android=1.5, Default=-1}"
HorizontalOptions="Start" />
<buttons:SfButton Margin="11,26,11,16"
BorderWidth="0"
TextColor="{StaticResource Gray-600}"
BackgroundColor="{StaticResource Transparent}"
WidthRequest="72"
HeightRequest="18"
Command="{Binding GetTransactions}"
CornerRadius="4"
HorizontalOptions="End">
<Label Text="VIEW ALL"
TextColor="{DynamicResource Link}"
FontSize="12"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
LineHeight="{OnPlatform Android=1.5, Default=-1}"
/>
</buttons:SfButton>
</Grid>
<listView:SfListView Grid.Row="2"
x:Name="_transactionList"
IsScrollBarVisible="False"
ItemSpacing="0"
ItemsSource="{Binding TransactionList}"
SelectionBackgroundColor="{StaticResource TappedBackgroundColor}"
TapCommand="{Binding ItemTappedCommand}"
AutoFitMode="Height"
BackgroundColor="White">
<listView:SfListView.ItemTemplate>
<DataTemplate>
<Grid RowSpacing="0" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!--Profile pic-->
<border:SfBorder Grid.RowSpan="3"
Margin="16"
WidthRequest="40"
HeightRequest="40"
CornerRadius="20"
BorderWidth="0"
VerticalOptions="Center">
<Image Aspect="Fill"
Source="receipt">
</Image>
</border:SfBorder>
<!-- Name -->
<Label Grid.Column="1"
Margin="0,15,0,4"
HorizontalOptions="Start"
Text="{Binding ReceiverName}"
LineHeight="{OnPlatform Android=1.5, Default=-1}" />
<!-- Transaction Title -->
<Label Grid.Row="1"
Grid.Column="1"
Margin="0,0,0,16"
HorizontalOptions="Start"
Text="{Binding TransactionMessage}"
TextColor="{StaticResource Gray-700}"
FontSize="12"
LineHeight="{OnPlatform Android=1.5, Default=-1}" />
<!-- Amount -->
<Label Grid.Column="1"
Margin="0,16,16,4"
HorizontalOptions="End"
TextColor="{Binding IsReceived, Converter={x:StaticResource BooleanToColorConverter}, ConverterParameter=5}"
LineHeight="{OnPlatform Android=1.5, Default=-1}">
<Label.FormattedText>
<FormattedString>
<Span Text="{Binding IsReceived, Converter={StaticResource BooleanToStringConverter}, ConverterParameter=2}" />
<Span Text=" $" />
<Span Text="{Binding TransactionAmount}" />
</FormattedString>
</Label.FormattedText>
</Label>
<!-- Date -->
<Label Grid.Row="1"
Grid.Column="1"
Margin="0,0,16,16"
HorizontalOptions="End"
Text="{Binding TimeStamp, StringFormat='{}{0:dd MMM yyyy}'}"
TextColor="{StaticResource Gray-700}"
FontSize="12"
LineHeight="{OnPlatform Android=1.5, Default=-1}" />
<!-- Seperator -->
<BoxView Grid.Row="2" Grid.ColumnSpan="2" Style="{StaticResource SeparatorStyle}" />
</Grid>
</DataTemplate>
</listView:SfListView.ItemTemplate>
</listView:SfListView>
<controls:Popup Grid.Row="2" Grid.RowSpan="1" IsBusy="{Binding IsBusy}" IsEnabled="{Binding IsBusy}" LoadingMessage="Loading the list.." />
</Grid>`
Any help would be appreciated.
don't ever use Thread.Sleep(). It blocks the UI (UI hangs / doesn't respond) if it's run on the UI Thread.
1.1. Trying to fix something by adding Thread.Sleep() and Task.Delay() is a step more towards hell. Don't enter this path!
You don't need to use ObservableCollection everywhere. If you don't need Observability, use IList or IEnumerable or an array.
Task.Run() crates a new thread. You usually don't need this. + You have issues in databinding, because you need to ensure the binding is executed on the UI thread, to be able to update the UI component.
Don't load data in the constructor. Trigger the loading in and lifecycle event like PageAppearing() (https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/app-lifecycle)
Updated code
private ObservableCollection<TransactionInformationDto> listItems;
public ObservableCollection<TransactionChartData> ChartData { get; set; }
public ObservableCollection<TransactionInformationDto> TransactionList
{
get { return listItems; }
set { listItems = value; OnPropertyChanged(nameof(TransactionList)); }
}
public ICommand GetTransactions => new Command(async () =>
{
IsBusy = true;
TransactionList.Clear();
var data = await GetAllTransactions();
TransactionList = new ObservableCollection<TransactionInformationDto>(data);
IsBusy = false;
});
public Command<object> ItemTappedCommand
{
get
{
return this.itemTappedCommand ?? (this.itemTappedCommand = new Command<object>(ShowTransactionInformation));
}
}
private void ShowTransactionInformation(object item)
{
var list = item as Syncfusion.ListView.XForms.ItemTappedEventArgs;
var transaction = (TransactionInformationDto)list.ItemData;
Navigation.PushAsync(new TransactionInfoPage(transaction));
}
#endregion
#region Constructor
public DashboardPageViewModel(INavigation navigation)
{
// LoadTransactionDetails(); // <<< see 4.
Navigation = navigation;
}
#endregion
#region Properties
public double TotalBalance
{
get
{
return totalBalance;
}
set
{
this.totalBalance = value;
this.OnPropertyChanged();
}
}
public INavigation Navigation { get; }
#endregion
#region Methods
private async Task<IEnumerable<TransactionInformationDto>> GetAllTransactions() // see 2.
{
var retrievalInformation = await App.Database.GetUserRetrievalInformation();
return await DependencyService.Get<IGetInformation>().GetAllUserTransactions(retrievalInformation);
}
private async Task LoadTransactionDetails()
{
var userTransactions = new List<TransactionInformationDto>();
// see 3.
var retrievalInformation = await App.Database.GetUserRetrievalInformation();
var userBalance = await DependencyService.Get<IGetInformation>().GetUserBalanceInformation(retrievalInformation);
TotalBalance = userBalance.CurrentBalance;
userTransactions = await DependencyService.Get<IGetInformation>().GetTransactionData(retrievalInformation);
// see. 1. & 1.1.
WeekData(userTransactions);
}
private void WeekData(List<TransactionInformationDto> transactionInformation)
{
TransactionList = new ObservableCollection<TransactionInformationDto>(transactionInformation.OrderByDescending(x =>x.TimeStamp));
days = new string[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
ChartData = new ObservableCollection<TransactionChartData>();
UpdateChartData(days);
}
The Task.Run method will run on a separate thread that tries to update the UI. The layout will be updated when adding the items in the main thread. UI threads get interrupted which restricts the layouts in the application.
https://learn.microsoft.com/en-us/xamarin/ios/user-interface/ios-ui/ui-thread#background-thread-example
https://forums.xamarin.com/discussion/comment/96756

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:

Xamarin.Forms.Skeleton - What make animation and BackgroundColor of the plugin doesn't appears?

I have implemented the Skeleton plugin in my xamarin.form app. It's working fine in my listview page. Then I try it in my listView item detail, the animation and skeleton backgroundColor doesn't work. The function working fine and no error in my build.
This is what I've tried:
my xaml layout :
<ScrollView BackgroundColor="White">
<StackLayout
Margin="15"
extension:Skeleton.IsParent="True"
extension:Skeleton.IsBusy="{Binding IsLoadBusy}"
extension:Skeleton.BackgroundColor="{StaticResource GrayColor}"
extension:Skeleton.Animation="Fade"
extension:Skeleton.AnimationInterval="600">
<Image
x:Name="ImgSelfie"
HeightRequest="200" WidthRequest="200" BackgroundColor="White"
Source="selfie"
extension:Skeleton.IsBusy="{Binding IsLoadBusy}"
extension:Skeleton.Animation="Fade"
extension:Skeleton.BackgroundColor="{StaticResource DarkGrayColor}"/>
<Label Text="Location :" FontAttributes="Bold"/>
<Editor
Text="{Binding Attend.AddressDetail}" x:Name="EdtLocation" IsEnabled="False" AutoSize="TextChanges"
extension:Skeleton.IsBusy="{Binding IsLoadBusy}"
extension:Skeleton.Animation="Fade"
extension:Skeleton.BackgroundColor="{StaticResource DarkGrayColor}"/>
<Label Text="Time :" FontAttributes="Bold"/>
<Entry
Text="{Binding Attend.Created}" x:Name="EntTime" IsEnabled="False"
extension:Skeleton.IsBusy="{Binding IsLoadBusy}"
extension:Skeleton.Animation="Fade"
extension:Skeleton.BackgroundColor="{StaticResource DarkGrayColor}" />
<Label Text="Action :" FontAttributes="Bold"/>
<Label
Text="{Binding Attend.Activity}" x:Name="LblAction" FontSize="Medium" TextColor="Black"
extension:Skeleton.IsBusy="{Binding IsLoadBusy}"
extension:Skeleton.Animation="Fade"
extension:Skeleton.BackgroundColor="{StaticResource DarkGrayColor}"/>
<Label Text="Noted :" FontAttributes="Bold"/>
<Editor
Text="{Binding Attend.Note}" x:Name="EntNote" IsEnabled="False" AutoSize="TextChanges"
extension:Skeleton.IsBusy="{Binding IsLoadBusy}"
extension:Skeleton.Animation="Fade"
extension:Skeleton.BackgroundColor="{StaticResource DarkGrayColor}"/>
<StackLayout VerticalOptions="EndAndExpand">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<material:MaterialButton
mvx:Bi.nd="Command OnCancelButtonCommand" Text="Confirm"
ButtonType="Outlined" BorderColor="Black" BackgroundColor="White"
TextColor="Black" PressedBackgroundColor="Gray" BorderWidth="2" WidthRequest="25" Padding="15"/>
</Grid>
</StackLayout>
</StackLayout>
</ScrollView>
and the view model :
public async Task PerformShimmerAsyncTask(string id)
{
this.Attend = new Attendance
{
//Image = null,
AddressDetail = "x",
Created = DateTime.Now,
Activity = "x",
Note = "x"
};
this.IsLoadBusy = true;
await Task.Delay(2500);
this.IsLoadBusy = false;
//await GetItem(id);
this.Attend = new Attendance
{
//Image = "selfie.png",
AddressDetail = "asdasdasda",
Created = DateTime.Now,
Activity = "sadasdasdasfacf",
Note = "asuuusfasfa"
};
}
following based on this example.
Please help and correct if my question is not yet clear.
I use your code and it works well on my side, I can show you my sample project.
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
myViewModel vm = new myViewModel();
this.BindingContext = vm;
vm.PerformShimmerAsyncTask("123");
}
}
public class myViewModel : BaseViewModel
{
private MyModel _attend;
private bool _isLoadBusy = false;
public MyModel Attend
{
get { return _attend; }
set { SetProperty(ref _attend, value); }
}
public bool IsLoadBusy
{
get { return _isLoadBusy; }
set
{
_isLoadBusy = value;
OnPropertyChanged();
}
}
public async Task PerformShimmerAsyncTask(string id)
{
this.Attend = new MyModel
{
AddressDetail = "x",
Created = DateTime.Now,
Activity = "x",
Note = "x"
};
this.IsLoadBusy = true;
await Task.Delay(10000);
this.IsLoadBusy = false;
this.Attend = new MyModel
{
AddressDetail = "asdasdasda",
Created = DateTime.Now,
Activity = "sadasdasdasfacf",
Note = "asuuusfasfa"
};
}
}
public class MyModel
{
public string AddressDetail { get; set; }
public DateTime Created { get; set; }
public string Activity { get; set; }
public string Note { get; set; }
}
And the code in Xaml is same as yours. You should make sure that you have set the right bindingContext and call PerformShimmerAsyncTask correctly.
I uploaded my sample project here. Let me know if it works for you.

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