简体   繁体   English

如何实现异步INotifyPropertyChanged

[英]How to implement async INotifyPropertyChanged

I have a class with properties that are bound to my view. 我有一个类,其属性绑定到我的视图。 To keep my view up-to-date, I implement INotifyPropertyChanged and raise the event everytime some property changes. 为了使我的视图保持最新,我实现了INotifyPropertyChanged并在每次某些属性更改时引发事件。

Now I got some heavy functions that freeze my application. 现在我得到了一些重要的函数来冻结我的应用程序 I want to put them into a background task. 我想把它们放到后台任务中。

First: here my current approach 第一:这是我目前的做法

(eg on button click) (例如点击按钮)

private async void HeavyFunc()
{
    foreach (var stuff)
    {
        count += await Task.Run(() => stuff.Fetch());
    }

    if (count == 0)
        //...
}

stuff class 东西类

public async Task<int> Fetch()
{
    //network stuff

    RaisePropertyChanged("MyProperty");
}

public async void RaisePropertyChanged(string pChangedProperty)
{
    await Application.Current.Dispatcher.BeginInvoke(
        System.Windows.Threading.DispatcherPriority.Normal,
        new ThreadStart(() =>
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(pChangedProperty);
        }
    );
}

The code above gives an exception ("DependencySource" must be created in the same thread like "DependencyObject"). 上面的代码给出了一个异常(“DependencySource”必须在同一个线程中创建,如“DependencyObject”)。

AFAIK, you generally need to create a new thread and run it (while awaiting it). AFAIK,你通常需要创建一个新线程并运行它(等待它)。 ´await Task.Run(...);´ should do this job. 'goit Task.Run(...);'应该做这个工作。

Since the PropertyChanged event directly influences the UI, calling it in the UI thread seems to be a good decision. 由于PropertyChanged事件直接影响UI,因此在UI线程中调用它似乎是一个很好的决定。 This is why I call Dispatcher.BeginInvoke. 这就是我调用Dispatcher.BeginInvoke的原因。

What I don't understand: the exception above is caused when different threads are responsible for the data. 我不明白:上面的例外是由不同的线程负责数据引起的。 But I explicitely calling the event on my UI-thread and the object should be created by the UI-thread too. 但我明确地在我的UI线程上调用该事件,该对象也应该由UI线程创建。 So why do I get an exception? 那么为什么我会得到例外?

My main question is: How do I implement the events for the INotifyPropertyChanged interface generally to avoid or handle most of the async programming problems like above? 我的主要问题是:如何实现INotifyPropertyChanged接口的事件通常是为了避免或处理上面的大多数异步编程问题? What should be considered while constructing the functions? 构建函数时应该考虑什么?

Now I got some heavy functions that freeze my application. 现在我得到了一些重要的函数来冻结我的应用程序

If you're really doing asynchronous "network stuff", then it shouldn't be freezing the app. 如果你真的在做异步“网络东西”,那么它不应该冻结应用程序。

My main question is: How do I implement the events for the INotifyPropertyChanged interface generally to avoid or handle most of the async programming problems like above? 我的主要问题是:如何实现INotifyPropertyChanged接口的事件通常是为了避免或处理上面的大多数异步编程问题?

The approach that I prefer is to not handle this in the event raising code. 我更喜欢的方法是在提升代码时处理这个问题。 Instead, structure the rest of your code so that it respects the UI layer. 相反,构建其余代码以使其尊重UI层。

In other words, divide your "service" (or "business logic") code from your "UI" code so that it works like this: 换句话说,将“服务”(或“业务逻辑”)代码从“UI”代码中划分出来,使其工作方式如下:

// In StuffService class:
public async Task<Result> FetchAsync()
{
  //network stuff
  return result;
}

// In StuffViewModel class:
public async void ButtonClicked()
{
  foreach (var stuff)
  {
    var result = await Task.Run(() => _stuffService.FetchAsync());
    MyProperty = result.MyProperty;
    count += result.Count;
  }

  if (count == 0)
    //...
}

public Property MyProperty
{
  get { return _myProperty; }
  set
  {
    _myProperty = value;
    RaisePropertyChanged();
  }
}
private void RaisePropertyChanged([CallerMemberName] string pChangedProperty = null)
{
  PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(pChangedProperty));
}

This way, there's no manual thread jumping, all properties have the standard ViewModel implementation, the code is simpler and more maintainable, etc. 这样,就没有手动跳线,所有属性都有标准的ViewModel实现,代码更简单,更易于维护等。

I did leave in the call to Task.Run , although this should be superfluous if your network calls are truly asynchronous. 我确实离开了对Task.Run的调用,尽管如果您的网络调用是真正的异步,这应该是多余的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM