Dispatcher ViewModel?

时间:2016-04-06 18:32:13

标签: wpf task

我正在学习跨线程,并且对如何从我创建的类(viewmodel)更新主UI线程有疑问。从我收集的信息来看,调度员是可行的方式。如何在类中使用主UI线程调度程序?或者有更好的方法来做到这一点。在这个例子中,我将文本块数据绑定到计数值。我需要做些什么才能让它发挥作用。谢谢!

class customVM : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChange(string PropertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
        }
    }

    private int _count;

    public int Count
    {
        get { return _count; }
        set {
            _count = value;
            OnPropertyChange(nameof(Count));
        }
    }

    public async void methodAsync()
    {
        await method();
    }

    private Task method()
    {

        return Task.Run(() =>
        {
            for (int i = 0; i < 10; i++)
            {
                Task.Delay(1000).Wait();
                //*************
                Count = i;
                //*************
            }
        });
    }
  }

2 个答案:

答案 0 :(得分:2)

不需要将PropertyChanged从另一个线程发送到UI线程,因为PropertyChanged事件会自动编组到UI调度程序。

private Task method()
{
    //The following code is okay. There is no need to marshal it explicitly        
    return Task.Run(() =>
    {
        for (int i = 0; i < 10; i++)
        {
            Task.Delay(1000).Wait();
            //*************
            Count = i;
            //*************
        }
    });
}

正如MSDN article所说:

  

请注意,在WPF中,情况有所不同,代码如图5所示   即使Status属性是数据绑定到TextBlock也可以工作。这个   是因为WPF自动将PropertyChanged事件调度到   主线程,与所有其他XAML框架不同。在所有其他   框架,需要一个调度解决方案。

但是,仅适用于标量属性的更改通知(即PropertyChanged事件)。集合更改通知(INotifyCollectionChanged.CollectionChanged事件)不会以这种方式工作,必须手动在UI线程上引发它们。也就是说,当使用INotifyCollectionChanged(例如使用ObservableCollection)时,这些更改不会封送到UI线程。这意味着如果您从非UI线程修改集合,您将获得异常。我们在ViewModel类中,我们不使用Dispatcher来更新UI。所以我建议你使用David Rickard的方法:

public static class DispatchService
{
    public static void Invoke(Action action)
    {
        Dispatcher dispatchObject = Application.Current.Dispatcher;
        if (dispatchObject == null || dispatchObject.CheckAccess())
    {
            action();
        }
        else
        {
            dispatchObject.Invoke(action);
        }
    }
}

DispatchService.Invoke(() =>
{
    this.MyCollection.Add("new value");
});

David Rickard article at msdn blog.

答案 1 :(得分:2)

跨线程标量属性绑定仅适用于WPF(也可能适用于Xamarin)。它在其他MVVM平台上工作。

您的代码似乎是一种进度报告。适当的解决方案是IProgress<T> Progress<T>

class customVM : INotifyPropertyChanged
{
  private int _count;

  public int Count
  {
    get { return _count; }
    set {
      _count = value;
      OnPropertyChange(nameof(Count));
    }
  }

  public async void methodAsync()
  {
    var progress = new Progress<int>(count => { Count = count; });
    await Task.Run(() => method(progress));
  }

  private void method(IProgress<int> progress)
  {
    for (int i = 0; i < 10; i++)
    {
      Thread.Sleep(1000);
      progress.Report(i);
    }
  }
}

Progress<T>适用于所有UI平台,包括具有不同类型调度程序的平台,甚至包括WinForms等非MVVM平台。