是否有用于将控件绑定到异步数据的明确定义的模式

本文关键字:数据 模式 定义 异步 用于 控件 绑定 是否 | 更新日期: 2023-09-27 18:30:54

您能判断以下代码片段是否正确绑定到异步数据源吗?

虽然它似乎正在工作,即:UI 不会冻结,但我不完全确定正确性,因为 MSDN 文档并没有真正讨论绑定到这些文档中的"异步"方法:

Binding.IsAsync

ObjectDataProvider.IsAsynchronous

<pages:HottestPageProxy x:Key="PageProxy" ></pages:HottestPageProxy>
<ObjectDataProvider x:Key="DataProviderArtists" IsAsynchronous="True"  ObjectInstance="{StaticResource PageProxy}"  MethodName="GetArtists">
    <ObjectDataProvider.MethodParameters>
        <system:String>Grabbing artists !</system:String>
    </ObjectDataProvider.MethodParameters>
</ObjectDataProvider>

(HottestPageProxy 对象是为控件提供数据的小助手)

public class HottestPageProxy
{
    [UsedImplicitly]
    public async Task<ArtistsQuery> GetArtists([CallerMemberName] string memberName = "")
    {
        Console.WriteLine(memberName);
        string apiKey = App.GetApiKey();
        Task<ArtistsQuery> topHottt = Queries.ArtistTopHottt(new ArtistTopHotttParameters
        {
            ApiKey = apiKey,
            Results = 100,
            Buckets = new[] {ArtistTopHotttBuckets.Hotttnesss}
        });
        return (await topHottt);
    }
}

编辑:"等待topHottt"调用的方法

public static async Task<ArtistsQuery> ArtistTopHottt(ArtistTopHotttParameters parameters)
{
    if (parameters == null) throw new ArgumentNullException("parameters");
    return await Get<ArtistsQuery>(parameters);
}
private static async Task<T> Get<T>(Parameters parameters) where T : Query
{
    if (parameters == null) throw new ArgumentNullException("parameters");
    ValidateParameters(parameters);
    string url = parameters.GetQueryUrl();
    var value = new Uri(url);
    using (var client = GetHttpClient())
    using (var message = await client.GetAsync(url))
    {
        // fetch message content (removed)
        return GetQueryResultObject<T>(s);
    }
}
private static T GetQueryResultObject<T>(string json) where T : class
{
    // create T from Json string (removed)
    return t;
}

使用 AsyncEx 进行编辑

使用您的库是有效的,尽管现在的语法是:

<ItemsControl x:Name="ItemsControlTopHott"
    ItemsSource="{Binding ... Path=Artists.Result.Artists ...}">
</ItemsControl>

"Artists.Result.Artists"真的是你期望我使用的吗? :)

新语法使其更加混乱,因为来源是:

public sealed class ArtistsQuery : Query
{
    public List<Artist> Artists { get; set; }
}

没什么大不了的,但如果我可以避免这样的语法,那就太好了。

你说.结果可能会带来僵局,那么我在实施您的解决方案时是否错过了什么?

Artists.PropertyChanged 事件引发了以下消息:

  • 状态:冉冉
  • 完成
  • 已完成:真
  • 已成功完成:真
  • 结果:成功

我会尝试.ConfigureAwait(false),正如你在帖子中提到的,看看它是如何运作的。

忘了提,实际上我的实现使用.结果确实不会阻止 UI,因为获取结果的典型时间是几秒钟;我会看到 UI 冻结。似乎没错...但我不确定它是否正确,因此我的问题。

是否有用于将控件绑定到异步数据的明确定义的模式

正如其他人所指出的,WPF 类型中的"异步"成员与 asyncawait 无关。

绑定中确实存在问题;路径使用的是 Task.Result ,这将阻止 UI 线程,直到查询完成。此外,使用Result会带来我在博客上描述的死锁的可能性。

我还有另一篇博客文章,涉及async属性,特别是如何数据绑定到(逻辑上)异步的属性。我的 AsyncEx 库有一个名为 NotifyTaskCompletion 的类型,它允许您更自然地将数据绑定到异步Task

因此,例如

,您可以执行以下操作:

public class HottestPageProxy
{
  public HottestPageProxy()
  {
    Artists = NotifyTaskCompletion.Create(GetArtists());
  }
  public INotifyTaskCompletion<ArtistsQuery> Artists { get; private set; }
  private Task<ArtistsQuery> GetArtists()
  {
    string apiKey = App.GetApiKey();
    return Queries.ArtistTopHottt(new ArtistTopHotttParameters
    {
        ApiKey = apiKey,
        Results = 100,
        Buckets = new[] {ArtistTopHotttBuckets.Hotttnesss}
    });
  }
}

然后,你可以对INotifyTaskCompletion<T>上的几个不同的属性进行数据绑定,包括IsCompletedErrorMessageResult(它不会阻塞;如果任务没有完成,它将返回default(T))。