我使用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;
答案 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
。