我在UWP(Windows 10)中遇到了与绑定和异步方法有关的问题。我有一个复选框,它绑定到一个布尔值isDay
。当我改变isDay
时,复选框也会改变状态。
XAML中我的代码如下:
IsChecked="{x:Bind isDay, Mode=TwoWay}"
当在异步方法中更改
isDay
时,复选框不会改变其状态。我该怎么做才能使此绑定与异步方法一起工作?
我在UWP(Windows 10)中遇到了与绑定和异步方法有关的问题。我有一个复选框,它绑定到一个布尔值isDay
。当我改变isDay
时,复选框也会改变状态。
XAML中我的代码如下:
IsChecked="{x:Bind isDay, Mode=TwoWay}"
isDay
时,复选框不会改变其状态。根据我的经验,复选框未更改的根本原因是UI线程上未触发属性更改通知。我认为UWP会吞噬这个错误而不是抛出关于“从不同线程访问某些内容”的异常。
如果您已经可以轻松访问一个Dispatcher
(您的代码后台或另一个视图级组件),那么您需要执行以下操作:
private async void ClickHandler(object sender, EventArgs args)
{
bool checked = await SomeAsyncWorkThatReturnsBool().ConfigureAwait(false);
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => checkbox.IsChecked = checked);
}
Dispatcher
怎么办?更可能的情况是,您之所以提出此问题,是因为在视图模型、模型或其他层中进行异步工作时,实际上无法访问Dispatcher
(或者当有多个时,无法访问正确的Dispatcher
)。过去,我使用依赖注入并抽象接口,使我的视图模型可以访问UI线程,但这可能是一种代码异味。
目前我最喜欢的方法(希望得到反馈)是在事件注册期间捕获SynchronizationContext
,并使用它来发布更改事件。
public class BindableBase : INotifyPropertyChanged
{
private readonly List<(SynchronizationContext context, PropertyChangedEventHandler handler)> _handlers = new List<...>();
public event PropertyChangedEventHandler PropertyChanged
{
add => _handlers.Add((SynchronizationContext.Current, value));
remove
{
var i = 0;
foreach (var item in _handlers)
{
if (item.handler.Equals(value))
{
_handlers.RemoveAt(i);
break;
}
i++;
}
}
}
protected Task RaisePropertyChanged(string propertyName)
{
var args = new PropertyChangedEventArgs(propertyName);
var tasks = _handlers
.GroupBy(x => x.context, x => x.handler)
.Select(g => invokeContext(g.Key, g));
return Task.WhenAll(tasks);
Task invokeContext(SynchronizationContext context, IEnumerable<PropertyChangedEventHandler> l)
{
if (context != null)
{
var tcs = new TaskCompletionSource<bool>();
context.Post(o =>
{
try { invokeHandlers(l); tcs.TrySetResult(true); }
catch (Exception e) { tcs.TrySetException(e); }
}, null);
return tcs.Task;
}
else
{
return Task.Run(() => invokeHandlers(l));
}
}
void invokeHandlers(IEnumerable<PropertyChangedEventHandler> l)
{
foreach (var h in l)
h(this, args);
}
}
}
由于某些原因,当通过异步方法更新值时,绑定未更新。因此,在异步方法结束时,我只需使用:
Bindings.Update();
并且控件会更新为最新的值。
Task<T>
方法进行绑定。