在INotifyTaskCompletion完成其任务后需要额外的PropertyChanged

时间:2013-11-21 19:09:53

标签: c# wpf data-binding async-await inotifytaskcompletion

我使用this blog(最后一段)和INotifyTaskCompletion的方法在视图模型中使用异步属性。它工作得很好,但我不得不适应它,这就是我的问题 - 无论我是否做得正确。

查看模型:

async Task<ObservableCollection<string>> GetInstancesAsync()
{
    var instances = await TaskEx.Run(new Func<List<string>>(() => Agent.GetInstances()));
    return new ObservableCollection<string>(instances);
}
public INotifyTaskCompletion<ObservableCollection<string>> InstancesAsync { get; private set; }
string instance;
public string Instance
{
    get { return instance; }
    set
    {
        instance = value;
        ProjectsAsync = NotifyTaskCompletion.Create(GetProjetsAsync(value));
        raisePropertyChanged("ProjectsAsync");
    }
}

在构造函数中,我开始:

InstancesAsync = NotifyTaskCompletion.Create(GetInstancesAsync(value));
raisePropertyChanged("InstancesAsync");

数据绑定只是ItemsSource="{Binding InstancesAsync.Result}"INotifyTaskCompletion<TResult>的实施并不复杂,缩写为:task.ContinueWith(t => { PropertyChanged(this, new PropertyChangedEventArgs("Result")); };

我的理解如下:启动任务后,raisePropertyChanged("InstancesAsync")会产生wpf尝试查找InstancesAsync.Result的效果。它找到InstancesAsync,因为在任务开始之前,此对象已实例化,但Result null 。完成任务后,wpf会收到PropertyChanged("Result"),现在可以通过现有访问者InstancesAsync Result找到。
填充了wpf中的ItemsSource,现在wpf必须触发SelectedItem的Binding,并以这种方式转到属性Instance。因为在我的情况下,启动了另一个异步属性任务。 为了实现这一点,我需要在任务完成后启动raisePropertyChanged("Instance")

我通过将raisePropertyChanged作为委托并将属性名称作为字符串传递给INotifyTaskCompletion的实现类来解决它。

我对这个解决方案感到满意,但也许我忽视了一些事情? 修改
事实证明,Cleary先生直截了当的方式是正确的,但只有IsSynchronizedWithCurrentItem=True。否则,不会自动选择列表的第一项。

另一个相关的问题突然出现 我添加了INotifyTaskCompletion支票if (propertyChanged == null) throw new Exception("PropertyChanged: no subscribers");,因为之前我遇到了问题。

然后在Instance的setter中我添加了:

private string _instance;
public string Instance
{
  get { return _instance; }
  set
  {
    if (ProjectsAsync == null && !Properties.Settings.Default.Instance.IsNullOrEmpty())
    {
        value = InstantiesAsync.Result.FirstOrDefault(i => i == Properties.Settings.Default.Instance);
    }
    _instance = value;
    raisePropertyChanged("Instance");
    ProjectsAsync = NotifyTaskCompletion.Create(GetProjetsAsync(value));
  }
}

这里的想法是覆盖基础ICollectionView的第0项的默认选择 然后我提到的例外发生了。我必须操纵CurrentItem的{​​{1}},而不是随意更改值。有什么想法吗?

我当前的解决方案是完全避免ICollectionView并在列表设置为新值时设置列表的SelectedItem。
因此,我必须订阅异步PropertyChanged事件:

IsSynchronizedWithCurrentItem=True

然后:

instancesAsync.PropertyChanged += instancesAsync_Ready;

1 个答案:

答案 0 :(得分:1)

我不太了解数据绑定的外观或raisePropertyChanged("Instance")的“额外”调用的位置。它不在上面的代码中。

应该工作的是PropertyChanged仅在您自己的属性发生变化时提升{}}:

async Task<ObservableCollection<string>> GetInstancesAsync()
{
  var instances = await TaskEx.Run(() => Agent.GetInstances());
  return new ObservableCollection<string>(instances);
}

private readonly INotifyTaskCompletion<ObservableCollection<string>> _instancesAsync;
public INotifyTaskCompletion<ObservableCollection<string>> InstancesAsync
{ get { return _instancesAsync; } }

private string _instance;
public string Instance
{
  get { return _instance; }
  set
  {
    _instance = value;
    raisePropertyChanged("Instance");
    ProjectsAsync = NotifyTaskCompletion.Create(GetProjetsAsync(value));
  }
}

private INotifyTaskCompletion<ObservableCollection<string>> _projectsAsync;
public INotifyTaskCompletion<ObservableCollection<string>> ProjectsAsync
{
  get { return _projectsAsync; }
  set
  {
    _projectsAsync = value;
    raisePropertyChanged("ProjectsAsync");
  }
}

此外,无需从构造函数中引发PropertyChanged