Home > Software design >  Xamarin, Label Text in xaml doesn't change on property changed WITH MVVM Helpers
Xamarin, Label Text in xaml doesn't change on property changed WITH MVVM Helpers

Time:12-07

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

CodePudding user response:

You are binding a the Text value of a Label to currentleve property of a LevelProgress object.

A property can only be bound if it is a BindableProperty. Thus, you will have to change the definition of currentlevel (and all other properties you want to bind to!) to make it a bindable.

Do something like this

public class LevelProgress : BindableObject
{

    ...

    public static readonly BindableProperty currentlevelProperty = 
        BindableProperty.Create("currentlevel", typeof(int), typeof(LevelProgress ), null);

    public int currentlevel
    {
        get { return (int)GetValue(currentlevelProperty ); }
        set { SetValue(currentlevelProperty , value); }
    }

    ...

}

Also, refer to the Bindable Property docu.

CodePudding user response:

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