I have a simple bool bound to a ProgressRing IsActive property. The property resides in a ViewModel base class. Here is the code:
<!--XAML-->
<ProgressRing
x:Name="RestrictAppProgress"
Margin="10"
IsActive="{x:Bind _vm.ProgressRingActive, Mode=OneWay}" Width="30" Height="30"
Foreground="#FF9333"/>
// ViewModel base class
private bool _progressRingActive;
public bool ProgressRingActive
{
get => _progressRingActive;
set
{
_progressRingActive = value;
Set(() => ProgressRingActive, ref _progressRingActive, value);
}
}
// ViewModel
public override async Task InitViewModel(ClassState classState)
{
ProgressRingActive = true;
_restrictModel = new RestrictAppModel();
var success = await _restrictModel.LoadAppsFromServer().ConfigureAwait(true);
ProgressRingActive = false;
}
// Code-behind OnNavigatedTo from Page class
protected override async void OnNavigatedTo(NavigationEventArgs restrictArgs)
{
...
await _vm.InitViewModel(state.classState).ConfigureAwait(true);
}
// Model class method that makes an await call
public async Task<bool> LoadAppsFromServer()
{
try
{
var appsListJson = await service.GetAppList(Settings.TeacherDistrictId).ConfigureAwait(true);
...
}
catch (ApiException ex)
{
...
return false;
}
return true;
}
So, when the page loads and goes to OnNavigatedTo
, my understanding is that we are still in the UI context. When we reach await _vm.InitViewModel(state.classState).ConfigureAwait(true);
in OnNavigatedTo
, my understanding is that because this call is awaited, InitViewModel does not take place in the UI Context, so when the bound property ProgressRingActive
changes, the property change is not being fired on UI thread, so no change is made. Is this right?
I tried getting rid of the await call before _vm.InitViewModel
, thinking that it would enter the method on the UI context, if I remove the await on vm.InitViewModel
call, isn't ProgressRingActive = true;
made on the UI context, then the call to var success = await _restrictModel.LoadAppsFromServer().ConfigureAwait(true);
is marshalled on a different context, and once it returns we are back on the UI context, with ProgressRingActive = false;
being made inside UI context? I am not 100% sure.
The only thing that has worked so far is calling Bindings.Update()
at the end of the OnNavigatedTo
method.
I found out what is was. So, I am using MvvmLight libs, and they provide a Set(Expression<Func<T>>, ref T, value)
method that "...replaces the old value of the backing field and then raises the PropertyChanged event...". My mistake in the code example that I provided was that in my ViewModelBase
class, I set the backing variable to the value, then called Set(...)
which WON'T raise the property changed event, because the backing variable and value
are equal. So the solution is to just remove the _progressRingActive = value;
line. Now, when Set(...)
is called, it will raise the property changed event, because the backing variable and value
are not equal.
There was also a hint in the editor itself, the key words being "if needed.":
The correct code for the ViewModelBase:
// ViewModel base class
private bool _progressRingActive;
public bool ProgressRingActive
{
get => _progressRingActive;
set
{
Set(() => ProgressRingActive, ref _progressRingActive, value);
}
}