I am building an application on Xamarin Forms using MVVM. The problem is that when I run the app, the data does not appear, the values are default. But if I change something in the Xaml file, in the name of the binding property, that value is updated and appears as it should be without restarting the application. I mention that I initialized BindingContext in ViewModel.
Here is the code:
ViewModel:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
using XamarinApp.Services;
using XamarinApp.Views.Month;
namespace XamarinApp.ViewModels.Month
{
public class DefaultScreenViewModel : BaseViewModel
{
private readonly IMonthService _monthService;
public decimal budget;
public decimal spendings;
public decimal economies;
public decimal percent;
public DefaultScreenViewModel(IMonthService monthService)
{
_monthService = monthService;
UpdateBudgetCommand = new Command(async () => await GoToUpdateBudget());
Title= "My Money";
}
public async void GetDefaultScreen()
{
try
{
var screen = await _monthService.GetDefaultScreen();
budget = screen.Budget;
economies = screen.Economies;
spendings = screen.Spendings;
percent = spendings/budget;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public decimal Budget
{
get => budget;
set => OnPropertyChanged(nameof(Budget));
}
public decimal Spendings
{
get => spendings;
set => OnPropertyChanged(nameof(Spendings));
}
public decimal Economies
{
get => economies;
set => OnPropertyChanged(nameof(Economies));
}
public decimal Percent
{
get => percent;
set => OnPropertyChanged(nameof(Percent));
}
private async Task GoToUpdateBudget()
=> await Shell.Current.GoToAsync(nameof(UpdateBudgetPage));
public ICommand UpdateBudgetCommand { get; }
}
}
The Xaml file:
<?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:month="clr-namespace:XamarinApp.ViewModels.Month"
x:Class="XamarinApp.Views.Month.DefaultScreenPage"
BackgroundColor="{StaticResource BackgroundColor}"
Title="{Binding Title}">
<ContentPage.Content>
<StackLayout>
<Frame Padding="30" Margin="30" BackgroundColor="{StaticResource Primary}">
<StackLayout>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<StackLayout Orientation="Vertical" FlexLayout.AlignSelf="Start" HorizontalOptions="FillAndExpand">
<Label Text="{Binding Spendings}" FontSize="Subtitle" VerticalOptions="FillAndExpand" />
<Label Text="Spendings" FontSize="Subtitle" VerticalOptions="FillAndExpand"/>
</StackLayout>
<StackLayout Orientation="Vertical" FlexLayout.AlignSelf="End" HorizontalOptions="End">
<Label Text="{Binding Budget}" FontSize="Subtitle" VerticalOptions="FillAndExpand" />
<Label Text="Budget" FontSize="Subtitle" VerticalOptions="FillAndExpand"/>
</StackLayout>
</StackLayout>
<ProgressBar Progress="{Binding Percent}" BackgroundColor="#69B811" ProgressColor="#750909" FlexLayout.AlignSelf="Center" Margin="10"/>
<Label/>
<Label Text="Current Month Savings: " />
<Label Text="{Binding Economies}"/>
</StackLayout>
</Frame>
<Button Text="Update your budget" Command="{Binding UpdateBudgetCommand}" Margin="20,40,20,0" CornerRadius="10"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
The Xaml.cs file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using XamarinApp.ViewModels.Month;
namespace XamarinApp.Views.Month
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class DefaultScreenPage : ContentPage
{
public readonly DefaultScreenViewModel _defaultScreenViewModel;
public DefaultScreenPage()
{
InitializeComponent();
_defaultScreenViewModel = Startup.Resolve<DefaultScreenViewModel>();
BindingContext = _defaultScreenViewModel;
}
protected override void OnAppearing()
{
_defaultScreenViewModel?.GetDefaultScreen();
}
}
}
And the BaseViewModel:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Xamarin.Forms;
using XamarinApp.Models;
using XamarinApp.Services;
namespace XamarinApp.ViewModels
{
public class BaseViewModel : INotifyPropertyChanged
{
public IDataStore<Item> DataStore => DependencyService.Get<IDataStore<Item>>();
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
}
}
CodePudding user response:
GetDefaultScreen
is setting the value of the public field
var screen = await _monthService.GetDefaultScreen();
budget = screen.Budget;
economies = screen.Economies;
spendings = screen.Spendings;
percent = spendings/budget;
instead, you need to set the value of the public properties
var screen = await _monthService.GetDefaultScreen();
Budget = screen.Budget;
Economies = screen.Economies;
Spendings = screen.Spendings;
Percent = spendings/budget;