UWP - 这是 属性 运行 在 UI 上下文中的边界吗?
UWP - Is this bounded property running on UI Context?
我有一个简单的 bool 绑定到 ProgressRing IsActive 属性。 属性 驻留在 ViewModel 基础 class 中。这是代码:
<!--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 上下文中。当我们到达 OnNavigatedTo
中的 await _vm.InitViewModel(state.classState).ConfigureAwait(true);
时,我的理解是因为等待此调用, InitViewModel 不会在 UI 上下文中发生,所以当绑定 属性 ProgressRingActive
更改,属性 更改未在 UI 线程上触发,因此未进行任何更改。这样对吗?
我尝试去掉_vm.InitViewModel
之前的await调用,认为它会进入UI上下文中的方法,如果我删除vm.InitViewModel
调用中的await,是不是't ProgressRingActive = true;
在 UI 上下文中进行,然后对 var success = await _restrictModel.LoadAppsFromServer().ConfigureAwait(true);
的调用在不同的上下文中编组,一旦它 returns 我们回到 UI 上下文,ProgressRingActive = false;
是在 UI 上下文中生成的?我不是 100% 确定。
到目前为止唯一有效的方法是在 OnNavigatedTo
方法的末尾调用 Bindings.Update()
。
嗯,绝对是,您需要 Dispatcher Access 才能更新 UI,这包括您的元素绑定到的属性。
if (!CoreApplication.MainView.Dispatcher.HasThreadAccess)
await CoreApplication.MainView.Dispatcher.Yield();
await CoreApplication.MainView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
//update UI here
})
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?
正如这个 所说,async/await 并没有直接说等待的代码将 运行 在不同的线程上。当你做await _vm.InitViewModel()
的时候,执行进入InitViewModel方法,还在UI线程上,还在等待。然后 _restrictModel.LoadAppsFromServer()
方法实际上将 运行 在不同的线程上异步。一旦 LoadAppsFromServer() 完成,await 将 returns 到 UI 线程。
The only thing that has worked so far is calling Bindings.Update() at
the end of the OnNavigatedTo method.
About ProgressRing没有消失,是因为ProgressRingActive的值虽然变了,但是你没有通知UI变。所以需要添加RaisePropertyChanged()
方法来通知UI.
public class MainViewModel : ViewModelBase
{
private bool _progressRingActive;
public bool ProgressRingActive
{
get => _progressRingActive;
set
{
_progressRingActive = value;
RaisePropertyChanged();
}
}
......
}
我发现了原来的样子。所以,我正在使用 MvvmLight 库,它们提供了一个 Set(Expression<Func<T>>, ref T, value)
方法 "...replaces the old value of the backing field and then raises the PropertyChanged event..."。我在我提供的代码示例中的错误是,在我的 ViewModelBase
class 中,我将支持变量设置为值,然后调用 Set(...)
WON'T 引发 属性 changed 事件,因为支持变量和 value
相等。所以解决方案是只删除 _progressRingActive = value;
行。现在,当 Set(...)
被调用时,它会引发 属性 changed 事件,因为支持变量和 value
不相等。
编辑器本身也有提示,关键词是"if needed.":
ViewModelBase 的正确代码:
// ViewModel base class
private bool _progressRingActive;
public bool ProgressRingActive
{
get => _progressRingActive;
set
{
Set(() => ProgressRingActive, ref _progressRingActive, value);
}
}
我有一个简单的 bool 绑定到 ProgressRing IsActive 属性。 属性 驻留在 ViewModel 基础 class 中。这是代码:
<!--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 上下文中。当我们到达 OnNavigatedTo
中的 await _vm.InitViewModel(state.classState).ConfigureAwait(true);
时,我的理解是因为等待此调用, InitViewModel 不会在 UI 上下文中发生,所以当绑定 属性 ProgressRingActive
更改,属性 更改未在 UI 线程上触发,因此未进行任何更改。这样对吗?
我尝试去掉_vm.InitViewModel
之前的await调用,认为它会进入UI上下文中的方法,如果我删除vm.InitViewModel
调用中的await,是不是't ProgressRingActive = true;
在 UI 上下文中进行,然后对 var success = await _restrictModel.LoadAppsFromServer().ConfigureAwait(true);
的调用在不同的上下文中编组,一旦它 returns 我们回到 UI 上下文,ProgressRingActive = false;
是在 UI 上下文中生成的?我不是 100% 确定。
到目前为止唯一有效的方法是在 OnNavigatedTo
方法的末尾调用 Bindings.Update()
。
嗯,绝对是,您需要 Dispatcher Access 才能更新 UI,这包括您的元素绑定到的属性。
if (!CoreApplication.MainView.Dispatcher.HasThreadAccess)
await CoreApplication.MainView.Dispatcher.Yield();
await CoreApplication.MainView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
//update UI here
})
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?
正如这个 await _vm.InitViewModel()
的时候,执行进入InitViewModel方法,还在UI线程上,还在等待。然后 _restrictModel.LoadAppsFromServer()
方法实际上将 运行 在不同的线程上异步。一旦 LoadAppsFromServer() 完成,await 将 returns 到 UI 线程。
The only thing that has worked so far is calling Bindings.Update() at the end of the OnNavigatedTo method.
About ProgressRing没有消失,是因为ProgressRingActive的值虽然变了,但是你没有通知UI变。所以需要添加RaisePropertyChanged()
方法来通知UI.
public class MainViewModel : ViewModelBase
{
private bool _progressRingActive;
public bool ProgressRingActive
{
get => _progressRingActive;
set
{
_progressRingActive = value;
RaisePropertyChanged();
}
}
......
}
我发现了原来的样子。所以,我正在使用 MvvmLight 库,它们提供了一个 Set(Expression<Func<T>>, ref T, value)
方法 "...replaces the old value of the backing field and then raises the PropertyChanged event..."。我在我提供的代码示例中的错误是,在我的 ViewModelBase
class 中,我将支持变量设置为值,然后调用 Set(...)
WON'T 引发 属性 changed 事件,因为支持变量和 value
相等。所以解决方案是只删除 _progressRingActive = value;
行。现在,当 Set(...)
被调用时,它会引发 属性 changed 事件,因为支持变量和 value
不相等。
编辑器本身也有提示,关键词是"if needed.":
ViewModelBase 的正确代码:
// ViewModel base class
private bool _progressRingActive;
public bool ProgressRingActive
{
get => _progressRingActive;
set
{
Set(() => ProgressRingActive, ref _progressRingActive, value);
}
}