C# UWP 异步绑定(bind)

标签 c# asynchronous binding uwp

我在 UWP (Windows 10) 中遇到绑定(bind)和异步方法的问题。我有一个绑定(bind)到一个 bool 值 isDay 的复选框。当我更改 isDay 时,复选框也会更改其状态。

我的 XAML 代码如下所示:

IsChecked="{x:Bind isDay, Mode=TwoWay}"

当在异步方法中更改 isDay 时,复选框不会更改其状态。

我应该怎么做才能使此绑定(bind)与异步方法一起使用?

最佳答案

根据我的经验,复选框未更改的根本原因是未在 UI 线程上触发属性更改通知。我相信 UWP 会吞下失败而不是抛出关于“从不同线程访问某些东西”的异常。

如果您已经可以轻松访问 Dispatcher(您的代码隐藏或其他 View 级组件),那么您可以执行以下操作:

private async void ClickHandler(object sender, EventArgs args)
{
    bool checked = await SomeAsyncWorkThatReturnsBool().ConfigureAwait(false);
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => checkbox.IsChecked = checked);
}

但是如果我没有 Dispatcher 怎么办?

更有可能的是,您问的原因是 View 模型、模型或实际上无法访问 Dispatcher(或正确的 Dispatcher 当有多个时)。过去,我使用过依赖注入(inject) 并抽象化接口(interface),让我的 View 模型能够访问 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);
        }
    }
}

关于C# UWP 异步绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41484994/

相关文章:

c# - 绑定(bind)自定义属性类 WPF

C# 更新 SQL Server 表

c# - 根据文件名运行正确的方法 (C#)

c# - .NET Core 控制台应用程序中解决 FileIO does not exist in nameespace Microsoft.VisualBasic

c++ - 如何将结构元素传递给异步线程?

c# - 从同步操作方法 : Task. Run 或 ConfigureAwaits(false) 调用异步方法

ios - 如何让循环等待任务完成

WPF控件的TabItem可见性复选框

c# - TestMethod 生成错误 CS0201

c# - 不工作绑定(bind)