wpf MVVM c# 如何将异步数据加载到属性中?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/43368904/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 14:03:50  来源:igfitidea点击:

MVVM c# how to load async data into a property?

c#wpfmvvm

提问by KillemAll

I wonder if there's a better approach to load async data into a property. now I create an async function and raise a Task in the Get part of the property like this:

我想知道是否有更好的方法将异步数据加载到属性中。现在我创建一个异步函数并在属性的 Get 部分引发一个任务,如下所示:

private ObservableCollection<CProyecto> prope;

public ObservableCollection<CProyecto> Prope
{
    get 
    {
        if (prope == null)
        {
            Task.Run(()=> LoadData()).Wait();
        }

        return proyectos;
    }
    set 
    { 
        prope = value; 
        RaisePropertyChanged(); 
    }
}

async private Task LoadData() 
{
    Prope = await clsStaticClassDataLoader.GetDataFromWebService();
}

This approach works, but I don't like the use of .Wait, because that can freeze the screen if the service doesn′t respond fast.

这种方法有效,但我不喜欢使用 .Wait,因为如果服务没有快速响应,它会冻结屏幕。

Can you please guide me on this matter?

你能指导我解决这个问题吗?

thanks in advance

提前致谢

采纳答案by Berin Loritsch

The way I handled this was to start the process of loading the property when the object was constructed, but I did not await the result. Since the property notifies when it is populated, the bindings worked just fine. Essentially it works like this:

我处理这个的方法是在构造对象时开始加载属性的过程,但我没有等待结果。由于该属性会在填充时发出通知,因此绑定工作得很好。本质上它是这样工作的:

public class MyClass : INotifyPropertyChanged
{
    private ObservableCollection<CProyecto> prope;

    public ObservableCollection<CProyecto> Prope
    {
        get { return prope; }
        set { prope = value; RaisePropertyChanged(nameof(Prope)); }
    }

    public MyClass()
    {
        // Don't wait or await.  When it's ready
        // the UI will get notified.
        LoadData();
    }

    async private Task LoadData() 
    {
        Prope = await clsStaticClassDataLoader.GetDataFromWebService();
    }
}

This works very well, and does not cause any delays or stuttering in the UI. If you want the collection to never be null(a good practice IMO), you can pre-initialize the propefield with an empty collection.

这非常有效,并且不会在 UI 中造成任何延迟或卡顿。如果您希望该集合永远不会成为null(IMO 的一个好习惯),您可以prope使用一个空集合预先初始化该字段。

回答by Stephen Cleary

I suggest you read my MSDN article on async MVVM data-binding. I have a librarythat provides a NotifyTask<T>type, which can be used as such:

我建议您阅读我关于异步 MVVM 数据绑定的MSDN 文章。我有一个提供NotifyTask<T>type的库,可以这样使用:

public class MyClass : INotifyPropertyChanged
{
  public NotifyTask<ObservableCollection<CProyecto>> Prope { get; private set; }

  public MyClass()
  {
    // Synchronously *start* the operation.
    Prope = NotifyTask.Create(LoadDataAsync());
  }

  async private Task<ObservableCollection<CProyecto>> LoadDataAsync()
  {
    return await clsStaticClassDataLoader.GetDataFromWebService();
  }
}

Then your databinding would operate on Prope.Result.

然后您的数据绑定将在Prope.Result.

The advantage of this approach is that you can also use databinding to hide/show busy indicators (Prope.IsNotCompleted), show controls when the data is available (Prope.IsSuccessfullyCompleted), and error notifications (Prope.IsFaulted/ Prope.ErrorMessage).

这种方法的优点是您还可以使用数据绑定来隐藏/显示繁忙指示符 ( Prope.IsNotCompleted)、在数据可用时显示控件 ( Prope.IsSuccessfullyCompleted) 和错误通知 ( Prope.IsFaulted/ Prope.ErrorMessage)。

Also, you can specify a non-nulldefault value, if you wish:

此外,null如果您愿意,您可以指定一个非默认值:

Prope = NotifyTask.Create(LoadDataAsync(), new ObservableCollection<CProyecto>());

回答by mm8

Your current implementation of the Propeproperty doesn't make much sense. It is pointless to execute the LoadDatamethod on a background thread since you block the main thread anyway when you call Wait(). You might as well call Wait()directly on the task returned by the LoadData()method:

您当前对该Prope属性的实现没有多大意义。LoadData在后台线程上执行该方法毫无意义,因为在调用Wait(). 也可以Wait()直接在LoadData()方法返回的任务上调用:

//BAD IMPLEMENTATION!
private ObservableCollection<CProyecto> prope;
public ObservableCollection<CProyecto> Prope
{
    get
    {
        if (prope == null)
            LoadData().Wait();
        return proyectos;
    }
    set { prope = value; RaisePropertyChanged(); }
}

The above implementation is still a bad one. The getter of a property is not supposed to perform an asynchronous operation. You should read @Stephen Cleary's blog post on the subject: https://blog.stephencleary.com/2013/01/async-oop-3-properties.html

上面的实现仍然是一个糟糕的实现。属性的 getter 不应该执行异步操作。您应该阅读@Stephen Cleary 关于该主题的博客文章:https://blog.stephencleary.com/2013/01/async-oop-3-properties.html

...and look into his NotifyTaskCompletiontype in his AsyncExlibrary: https://github.com/StephenCleary/AsyncEx

...并NotifyTaskCompletion在他的AsyncEx图书馆中查看他的类型:https: //github.com/StephenCleary/AsyncEx