0

我有一个简单的 bool 绑定到 ProgressRing IsActive 属性。该属性位于 ViewModel 基类中。这是代码:

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

因此,当页面加载并转到 时OnNavigatedTo,我的理解是我们仍然处于 UI 上下文中。当我们到达await _vm.InitViewModel(state.classState).ConfigureAwait(true);in 时OnNavigatedTo,我的理解是因为这个调用是等待的,所以 InitViewModel 不会发生在 UI Context 中,所以当绑定ProgressRingActive的属性发生变化时,不会在 UI 线程上触发属性更改,因此不会进行任何更改。这是正确的吗?

我之前尝试摆脱 await 调用_vm.InitViewModel,认为它会进入 UI 上下文中的方法,如果我删除vm.InitViewModel调用时的等待,不在ProgressRingActive = true;UI 上下文中进行,那么调用将var success = await _restrictModel.LoadAppsFromServer().ConfigureAwait(true);在不同的上下文中编组,一旦它返回,我们又回到了 UI 上下文,并ProgressRingActive = false;在 UI 上下文中创建?我不是百分百确定。

到目前为止唯一有效的是Bindings.Update()在方法结束时调用OnNavigatedTo

4

3 回答 3

0

我发现是什么。所以,我正在使用 MvvmLight 库,它们提供了一种Set(Expression<Func<T>>, ref T, value)方法“......替换支持字段的旧值,然后引发 PropertyChanged 事件......”。我在提供的代码示例中的错误是,在我的ViewModelBase类中,我将支持变量设置为该值,然后调用Set(...)which WON'T 不会引发属性更改事件,因为支持变量 和value相等。所以解决方案是删除_progressRingActive = value;线。现在,当Set(...)被调用时,它会引发属性更改事件,因为后备变量和value不相等。

编辑器本身也有提示,关键词是“如果需要”。在此处输入图像描述

ViewModelBase 的正确代码:

// ViewModel base class
private bool _progressRingActive;
public bool ProgressRingActive
{
    get => _progressRingActive;
    set
    {
        Set(() => ProgressRingActive, ref _progressRingActive, value);
    }
}
于 2019-11-28T20:23:03.220 回答
0

InitViewModel 不会发生在 UI 上下文中,因此当绑定属性 ProgressRingActive 更改时,不会在 UI 线程上触发属性更改,因此不会进行任何更改。这是正确的吗?

正如这个线程所说,async/await 并没有直接说等待的代码将在不同的线程上运行。当您这样做await _vm.InitViewModel()时,执行进入仍在 UI 线程上的 InitViewModel 方法并仍在等待。然后该_restrictModel.LoadAppsFromServer()方法实际上将在不同的线程上异步运行。一旦 LoadAppsFromServer() 完成,await 将返回 UI 线程。

到目前为止唯一有效的是在 OnNavigatedTo 方法的末尾调用 Bindings.Update()。

关于ProgressRing 没有消失,是因为虽然ProgressRingActive 的值发生了变化,但是你并没有通知UI 发生变化。所以你需要添加RaisePropertyChanged()通知 UI 的方法。

public class MainViewModel : ViewModelBase
{
    private bool _progressRingActive;
    public bool ProgressRingActive
    {
        get => _progressRingActive;
        set
        {
            _progressRingActive = value;
            RaisePropertyChanged();
        }
    }

    ......
}
于 2019-11-28T08:20:22.527 回答
-1

好吧,绝对是,您需要 Dispatcher Access 才能更新 UI,这包括您的元素绑定到的属性。

if (!CoreApplication.MainView.Dispatcher.HasThreadAccess)
                        await CoreApplication.MainView.Dispatcher.Yield();
                    await CoreApplication.MainView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
                    {
//update UI here
})
于 2019-11-27T20:07:07.733 回答