【问题标题】:How to implement async INotifyPropertyChanged如何实现异步 INotifyPropertyChanged
【发布时间】:2016-12-29 02:37:09
【问题描述】:

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

现在我有一些繁重的功能会冻结我的应用程序。我想把它们放到后台任务中。

首先:这是我目前的方法

(例如点击按钮)

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

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

东西类

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);
        }
    );
}

上面的代码给出了一个异常(“DependencySource”必须像“DependencyObject”一样在同一个线程中创建)。

AFAIK,您通常需要创建一个新线程并运行它(在等待它的同时)。 'await Task.Run(...);' 应该完成这项工作。

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

我不明白:上面的异常是当不同的线程负责数据时引起的。但是我在我的 UI 线程上明确调用了该事件,并且该对象也应该由 UI 线程创建。那么为什么会出现异常呢?

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

【问题讨论】:

  • 您在线程上引发 propertychanged 事件,因此如果它从未创建过 ui 元素,它将无法访问它们。
  • 阅读this以真正了解ui线程与另一个线程执行的代码
  • 属性本身是像位图一样的 DepedencyObject 吗?

标签: c# wpf asynchronous inotifypropertychanged


【解决方案1】:

现在我有一些繁重的函数会冻结我的应用程序。

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

我的主要问题是:我如何实现 INotifyPropertyChanged 接口的事件,以避免或处理上述大多数异步编程问题?

我更喜欢的方法是在事件引发代码中处理此问题。相反,构建代码的 rest 使其尊重 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));
}

这样就不用手动跳线程了,所有属性都有标准的ViewModel实现,代码更简单更易维护等等。

我确实离开了对Task.Run 的调用,尽管如果您的网络调用是真正异步的,这应该是多余的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-16
    • 1970-01-01
    • 2012-07-08
    • 2019-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多