C# UWP 异步绑定

3

我在UWP(Windows 10)中遇到了与绑定和异步方法有关的问题。我有一个复选框,它绑定到一个布尔值isDay。当我改变isDay时,复选框也会改变状态。

XAML中我的代码如下:

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

当在异步方法中更改isDay时,复选框不会改变其状态。
我该怎么做才能使此绑定与异步方法一起工作?

4
请展示所有相关的最小代码,包括属性声明和改变它的代码。 - Nahuel Ianni
请发布异步方法。有一种方法可以做到,但你可能不会喜欢它。如果您想绑定任何类型的异步方法,应该显示复选框状态正在更改,然后在更新后通知属性并使更改生效。 - Michael Puckett II
3个回答

5

根据我的经验,复选框未更改的根本原因是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);
        }
    }
}

我的朋友,你真是个天才!你在哪里找到/阅读了这种围绕异步和绑定工作的风格……它非常有效,没有其他代码需要更改(就绑定或业务逻辑而言),但现在我想知道更多,我错过了什么! - Chris Schaller
@ChrisSchaller 我不认为我读到了 UWP 中绑定差异的影响。可能有记录表明 INPC 需要在 UI 线程上,但我不确定。至于解决它的方式?这是我想到的一个想法,恰好非常有效。 - Adam Hewitt

1

由于某些原因,当通过异步方法更新值时,绑定未更新。因此,在异步方法结束时,我只需使用:

Bindings.Update();

并且控件会更新为最新的值。


-1
默认情况下,自动生成的 XAML 代码不支持可等待方法,因此您无法在 XAML 中使用 Task<T> 方法进行绑定。

1
我找到了一种方法。我只需要在异步方法的末尾加上Bindings.Update(),它就会更新绑定。 - user3239349
对我来说,这不是一个好的解决方案。我想需要改变这种情况下的逻辑。 - Andrii Krupka
“在pagename.g.cs文件中查找此部分并插入…”当然是荒谬的。您不会更改生成的代码。 - H H

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接