viewmodel
上的一些属性:
public ObservableCollection<Task> Tasks { get; set; }
public int Count
{
get { return Tasks.Count; }
}
public int Completed
{
get { return Tasks.Count(t => t.IsComplete); }
}
Tasks
更改时更新这些属性的最佳方法是什么?
我目前的方法:
public TaskViewModel()
{
Tasks = new ObservableCollection<Task>(repository.LoadTasks());
Tasks.CollectionChanged += (s, e) =>
{
OnPropertyChanged("Count");
OnPropertyChanged("Completed");
};
}
有更优雅的方法吗?
答案 0 :(得分:9)
关于Count
,您根本不需要这样做。只需绑定到Tasks.Count
,您的绑定就会通过ObservableCollection
收到有关更改的通知。
Completed
是一个不同的故事,因为它超出了ObservableCollection
。不过,从抽象/界面的层面来看,你真的希望Completed
成为Tasks
集合的属性。
为此,我认为更好的方法是为您的Tasks
属性创建“子”视图模型:
public class TasksViewModel : ObservableCollection<Task>
{
public int Completed
{
get { return this.Count(t => t.IsComplete); }
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if(e.PropertyName == "Count") NotifyCompletedChanged();
}
protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
NotifyCompletedChanged();
}
void NotifyCompletedChanged()
{
OnPropertyChanged(_completedChangedArgs);
}
readonly PropertyChangedEventArgs _completedChangedArgs = new PropertyChangedEventArgs("Completed");
}
这为您提供了ObservableCollection
的所有好处,并有效地使Completed
属性成为其中的一部分。我们仍然没有捕获完成项目数量确实发生变化的情况,但我们在一定程度上减少了冗余通知的数量。
现在viewmodel只有属性:
public TasksViewModel Tasks { get; set; }
...您可以轻松绑定到Tasks
,Tasks.Count
和Tasks.Completed
。
作为替代方案,如果您希望在“主”视图模型上创建这些其他属性,则可以使用子类ObservableCollection<T>
的概念来创建一个具有某种方法的概念,您可以在其中传入{ {1}}委托,表示在主视图模型上提出属性更改通知,以及一些属性名称列表。然后,此集合可以有效地在视图模型上引发属性更改通知:
Action<string>
您甚至可以将属性类型保留为public class ObservableCollectionWithSubscribers<T> : ObservableCollection<T>
{
Action<string> _notificationAction = s => { }; // do nothing, by default
readonly IList<string> _subscribedProperties = new List<string>();
public void SubscribeToChanges(Action<string> notificationAction, params string[] properties)
{
_notificationAction = notificationAction;
foreach (var property in properties)
_subscribedProperties.Add(property);
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
NotifySubscribers();
}
protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
NotifySubscribers();
}
void NotifySubscribers()
{
foreach (var property in _subscribedProperties)
_notificationAction(property);
}
}
。
ObservableCollection<Task>
答案 1 :(得分:4)
对我来说相当优雅。我真的不知道你怎么做得更简洁。
(写这样的答案有多奇怪。如果有人提出更优雅的东西,我可能会删除它。)
好的,我注意到一件事,与原来的问题无关:你的Tasks
财产有一个公共制定者。设为private set;
,或者您需要使用支持字段实现set
,这样您就可以删除前一个实例上的委托,替换并连接新的委托,并执行{{1}使用“任务”,“计数”和“已完成”。 (看看如何在构造函数中设置OnPropertyChanged
,我猜测Tasks
是更好的选择。)
不会通知private set;
和Count
更优雅,但它修复了错误。
许多MVVM框架从lambda获取属性名称,因此您可以编写Completed
而不是OnPropertyChanged("Count")
,以便在重构工具的帮助下完成重命名。我不认为重命名经常发生,但它确实避免了一些字符串文字。