Search code examples
c#xamluwpbindingxbind

UWP - Is this bounded property running on UI Context?


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.


Solution

  • 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.": enter image description here

    The correct code for the ViewModelBase:

    // ViewModel base class
    private bool _progressRingActive;
    public bool ProgressRingActive
    {
        get => _progressRingActive;
        set
        {
            Set(() => ProgressRingActive, ref _progressRingActive, value);
        }
    }