I am creating a component that displays information about how many more trips a user needs to complete in order to receive a discount.
<!--DiscountProgressCard.xaml-->
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyProject.DiscountProgressCard"
x:Name="this">
<Border BindingContext="{x:Reference this}"> <!--Border styling cut for brevity -->
<VerticalStackLayout>
<ProgressBar ProgressColor="Green" Progress="{Binding Progress}" />
<Label>
<Label.Text>
<MultiBinding StringFormat="{}{0} out of {1} trips completed.">
<Binding Path="CompletedTrips" />
<Binding Path="TripsNecessaryForDiscount" />
</MultiBinding>
</Label.Text>
</Label>
</VerticalStackLayout>
</Border>
</ContentView>
The values CompletedTrips
and TripsNecessaryForDiscount
are bindable properties. Progress
is meant to be derived from these properties, and utilized solely within this component.
public partial class DiscountProgressCard : ContentView
{
public DiscountProgressCard()
{
InitializeComponent();
}
//Parameter - The number of trips the user has already completed.
public int CompletedTrips
{
get { return (int)GetValue(CompletedTripsProperty); }
set { SetValue(CompletedTripsProperty, value); }
}
public static readonly BindableProperty CompletedTripsProperty = BindableProperty.Create(
nameof(CompletedTrips),
typeof(int),
typeof(DiscountProgressCard),
0);
//Parameter - The number of trips the user must complete to receive a discount.
public int TripsNecessaryForDiscount
{
get { return (int)GetValue(TripsNecessaryForDiscountProperty); }
set { SetValue(TripsNecessaryForDiscountProperty, value); }
}
public static readonly BindableProperty TripsNecessaryForDiscountProperty = BindableProperty.Create(
nameof(TripsNecessaryForDiscount),
typeof(int),
typeof(DiscountProgressCard),
999);
//Progress should be derived from CompletedTrips and TripsNecessaryForDiscount.
protected decimal Progress {
get { return Decimal.Divide(CompletedTrips, TripsNecessaryForDiscount); }
}
When the component is rendered, and values are provided to the parameters CompletedTrips
and TripsNecessaryForDiscount
, those parameters are displayed correctly. However, the getter of Progress
is only run once, using the default values of CompletedTrips
and TripsNecessaryForDiscount
. As a result, the progress bar is always empty.
<!--MyPage.xaml-->
<ContentPage>
<!--...cut for brevity...-->
<components:DiscountProgressCard CompletedTrips=200" TripsNecessaryForDiscount="400" />
</ContentPage>
Why is Progress
not being recalculated with the provided parameter values? And how do I go about fixing it?
Progress
is not updated, because there is no notification that something has changed. You'll need to raise a notification that Progress
should be updated whenever the CompletedTrips
updates:
public static readonly BindableProperty CompletedTripsProperty = BindableProperty.Create(
nameof(CompletedTrips),
typeof(int),
typeof(DiscountProgressCard),
0,
propertyChanged: OnProgressChanged);
private static void OnProgressChanged(BindableObject bindable, object oldValue, object newValue)
{
((DiscountProgressCard)bindable).OnPropertyChanged(nameof(Progress));
}
Apart from that, the Progress
property should probably be public.