【问题标题】:Dispatcher ViewModel?调度员视图模型?
【发布时间】:2016-04-06 18:32:12
【问题描述】:

我正在学习跨线程,并且有一个关于如何从我创建的类(视图模型)更新主 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;
                //*************
            }
        });
    }
  }

【问题讨论】:

  • 不清楚的问题,在贴出的代码中,正常绑定是可以的。你还想做什么?
  • Count 绑定到文本块。发生交叉线程。
  • 没关系,我猜跨线程可以在没有直接在 WPF 应用程序中使用调度程序的情况下工作。我在不同平台之间移动。

标签: wpf task


【解决方案1】:

无需将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.

【讨论】:

    【解决方案2】:

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

    您的代码似乎是一种进度报告。合适的解决方案是IProgress&lt;T&gt;Progress&lt;T&gt;

    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&lt;T&gt; 在所有 UI 平台上都能很好地工作,包括具有不同类型调度程序的平台,甚至包括 WinForms 等非 MVVM 平台。

    【讨论】:

      猜你喜欢
      • 2015-05-02
      • 1970-01-01
      • 2022-01-20
      • 2011-09-10
      • 2016-07-02
      • 2016-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多