如何实现异步INotifyPropertyChanged

fed*_*dab 3 c# wpf asynchronous inotifypropertychanged

我有一个类,其属性绑定到我的视图.为了使我的视图保持最新,我实现了INotifyPropertyChanged并在每次某些属性更改时引发事件.

现在我得到了一些重要的函数来冻结我的应用程序 我想把它们放到后台任务中.

第一:这是我目前的做法

(例如点击按钮)

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

    if (count == 0)
        //...
}
Run Code Online (Sandbox Code Playgroud)

东西类

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);
        }
    );
}
Run Code Online (Sandbox Code Playgroud)

上面的代码给出了一个异常("DependencySource"必须在同一个线程中创建,如"DependencyObject").

AFAIK,你通常需要创建一个新线程并运行它(等待它).'goit Task.Run(...);'应该做这个工作.

由于PropertyChanged事件直接影响UI,因此在UI线程中调用它似乎是一个很好的决定.这就是我调用Dispatcher.BeginInvoke的原因.

我不明白:上面的例外是由不同的线程负责数据引起的.但我明确地在我的UI线程上调用该事件,该对象也应该由UI线程创建.那么为什么我会得到例外?

我的主要问题是:如何实现INotifyPropertyChanged接口的事件通常是为了避免或处理上面的大多数异步编程问题?构建函数时应该考虑什么?

Ste*_*ary 6

现在我得到了一些重要的函数来冻结我的应用程序

如果你真的在做异步"网络东西",那么它不应该冻结应用程序.

我的主要问题是:如何实现INotifyPropertyChanged接口的事件通常是为了避免或处理上面的大多数异步编程问题?

我更喜欢的方法是在提升代码时处理这个问题.相反,构建其余代码以使其尊重UI层.

换句话说,从"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));
}
Run Code Online (Sandbox Code Playgroud)

这样,就没有手动跳线,所有属性都有标准的ViewModel实现,代码更简单,更易于维护等.

我确实离开了电话Task.Run,但如果你的网络电话是真正的异步,这应该是多余的.