Reputation: 972
I am working on a MAUI application in MVVM, and I am getting binding errors (when running the app and navigating to the page in question) which I do not understand. Here is what I am doing:
In the page I am using the ProgressBar control and binding its Progress property to the property of an object defined in the ViewModel which is the BindingContext of the page:
<tt:ProgressBar Progress="{Binding SessionProgression.ProgressionRatio}" />
The ViewModel property definition:
[ObservableProperty]
private TimeProgression _sessionProgression = new TimeProgression();
And the object in question's class definition (the rest of the class updates the property value and calls OnPropertyChanged("TimeProgression") but I have not included that for clarity):
public sealed class TimeProgression : ObservableObject
{
public double ProgressionRatio { get; private set; }
}
I am getting the following error:
Error 127 TimeProgression ProgressionRatio ProgressBar.Progress Double 'ProgressionRatio' property not found on 'TimerToolkit.Models.TimeProgression', target property: 'TimerToolkit.Controls.ProgressBar.Progress'
Please note that if I put the ProgressionRatio property inside my ViewModel instead of inside the TimeProgression instance of my ViewModel, no binding error occurs. And in both case the correct value is resolved at run-time.
What am I missing here? Thanks for taking the time to read me.
EDIT:
I have further troubleshooted the issue and here is the culprit:
public TimeSpan ElapsedTime
{
get => _elapsedTime;
set
{
if (_elapsedTime != value)
{
_elapsedTime = value;
ProgressionRatio = Math.Clamp(ElapsedTime.TotalSeconds / Duration.TotalSeconds, 0, 1);
OnPropertyChanged(nameof(ElapsedTime));
OnPropertyChanged(nameof(ProgressionRatio));
}
}
}
public double ProgressionRatio { get; private set; }
public void AddElapsedSeconds(int seconds)
{
ElapsedTime = ElapsedTime.Add(TimeSpan.FromSeconds(seconds));
}
-> It seems that assigning a new value to ProgressionRatio causes a binding failure in the View. The view is correctly updated with the new value however.
You could reproduce this behavior simply by adding a button on the page and assign its Clicked event like so:
private void Button_Clicked(object sender, EventArgs e)
{
((MainPageViewModel)BindingContext).SessionProgression.AddElapsedSeconds(1);
}
Upvotes: 0
Views: 160
Reputation: 8290
I am so glad that we finally found the cause of the question.
In the BindableProperty
ProgressProperty, it set the BindingMode to TwoWay, which makes no sense in this context and causes the warning.
public static readonly BindableProperty ProgressProperty = BindableProperty.Create(nameof(Progress), typeof(double), typeof(ProgressBarDrawable), 0.0, BindingMode.TwoWay, ...
The solution is to set it as BindingMode.OneWay
or no need set it(The default is OneWay).
I made a demo to test your code,
Here is the XAML file, I add a button behind the ProgressBar,
<ScrollView>
<VerticalStackLayout
Padding="30,0"
Spacing="25">
<Label
Text="Hello, World!"
Style="{StaticResource Headline}"
SemanticProperties.HeadingLevel="Level1" />
<ProgressBar Progress="{Binding SessionProgression.ProgressionRatio}" />
<Button Text="CLICK ME" Clicked="Button_Clicked"/>
</VerticalStackLayout>
</ScrollView>
Set the BindingContext in code behind, and add the Button click handler,
public MainPage()
{
InitializeComponent();
this.BindingContext = new MainPageViewModel();
}
void Button_Clicked(System.Object sender, System.EventArgs e)
{
((MainPageViewModel)BindingContext).SessionProgression.AddElapsedSeconds(1);
}
And define the ViewModel like this,
public partial class MainPageViewModel : ObservableObject
{
[ObservableProperty]
private TimeProgression _sessionProgression = new TimeProgression();
public MainPageViewModel()
{
}
}
Give the ProgressionRatio
an initial value to 0.3,
public sealed class TimeProgression : ObservableObject
{
public double ProgressionRatio { get; private set; }
public TimeSpan Duration = new TimeSpan(0,0,10);
private TimeSpan _elapsedTime;
public TimeSpan ElapsedTime
{
get => _elapsedTime;
set
{
if (_elapsedTime != value)
{
_elapsedTime = value;
ProgressionRatio = Math.Clamp(ElapsedTime.TotalSeconds / Duration.TotalSeconds, 0, 1);
OnPropertyChanged(nameof(ElapsedTime));
OnPropertyChanged(nameof(ProgressionRatio));
}
}
}
public void AddElapsedSeconds(int seconds)
{
ElapsedTime = ElapsedTime.Add(TimeSpan.FromSeconds(seconds));
}
public TimeProgression()
{
//ProgressionRatio = 0.5;
ElapsedTime = new TimeSpan(0, 0, 3);
}
}
Here is the effect, each time we click the button, the progress bar will change.
Upvotes: 1