这闻起来像是过早的优化。你测量过简单方法的速度吗?在现代 CPU 上迭代几个不太大的集合只需要几毫秒。
如果您想使用 LINQ 计算所有Column
对象是活动的还是非活动的,您可以使用此表达式。
public Boolean CanExecute {
get {
var aggregate = Items
.SelectMany(i => i.Columns)
.Aggregate(
new {
IsEmpty = true,
AllAreActive = true,
AllAreInactive = true
},
(a, c) => new {
IsEmpty = false,
AllAreActive = a.AllAreActive && c.IsActive,
AllAreInactive = a.AllAreInactive && !c.IsActive
}
);
return !aggregate.IsEmpty && (aggregate.AllAreActive || aggregate.AllAreInactive);
}
此代码将遍历所有集合中的所有元素。您可以通过使用for
循环并在两个布尔变量都变为假时打破它来改进这一点。这也可以通过TakeWhile
使用具有副作用的谓词在 LINQ 中完成,但简单的for
循环可能更容易理解。
如果您认为简单的方法太慢,您需要跟踪该级别的CanExecute
属性。您可以通过为所有实例MyClass
设置更改通知处理程序来做到这一点。ObservableCollection
这有点乏味,因为您有两个级别的集合,但它将确保每当Column
从集合中添加或删除 a 或更改属性时,都会更新支持的IsActive
布尔变量。CanExecute
MyClass
最初Column
必须实施INotifyPropertyChanged
:
class Column : INotifyPropertyChanged {
Boolean isActive;
public Boolean IsActive {
get { return this.isActive; }
set {
if (this.isActive == value)
return;
this.isActive = value;
OnPropertyChanged("IsActive");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(String propertyName) {
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Data
必须公开所需的属性,由于缺少更好的名称,我已经调用AllColumnsAreActiveOrInactive
了该属性,并且通过实现来表示对该属性的更改INotifyPropertyChanged
。
为了跟踪所有列的状态,CollectionChanged
集合Column
的处理。当Column
添加一个新的值时,AllColumnsAreActiveOrInactive
可以重新计算而不迭代Column
集合。但是,当IsActive
一个单一的变化Column
或当一个Column
被删除时,集合必须被迭代以确定新的值AllColumnsAreActiveOrInactive
。
class Data : INotifyPropertyChanged {
readonly ObservableCollection<Column> columns;
Boolean allColumnsAreActive = true;
Boolean allColumnsAreInactive = true;
Boolean allColumnsAreActiveOrInactive = false;
public Data() {
this.columns = new ObservableCollection<Column>();
this.columns.CollectionChanged += CollectionChanged;
}
public ObservableCollection<Column> Columns { get { return this.columns; } }
public Boolean AllColumnsAreActiveOrInactive {
get { return this.allColumnsAreActiveOrInactive; }
set {
if (value == this.allColumnsAreActiveOrInactive)
return;
this.allColumnsAreActiveOrInactive = value;
OnPropertyChanged("AllColumnsAreActiveOrInactive");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(String propertyName) {
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
void CollectionChanged(Object sender, NotifyCollectionChangedEventArgs e) {
if (e.Action == NotifyCollectionChangedAction.Reset) {
RecomputeAllColumnsAreActiveOrInactive();
return;
}
if (e.OldItems != null) {
foreach (var item in e.OldItems.Cast<Column>())
item.PropertyChanged -= ColumnPropertyChanged;
RecomputeAllColumnsAreActiveOrInactive();
return;
}
if (e.NewItems != null) {
foreach (var column in e.NewItems.Cast<Column>()) {
column.PropertyChanged += ColumnPropertyChanged;
this.allColumnsAreActive = this.allColumnsAreActive && column.IsActive;
this.allColumnsAreInactive = this.allColumnsAreInactive && !column.IsActive;
}
UpdateAllColumnsAreActiveOrInactive();
}
}
void ColumnPropertyChanged(Object sender, PropertyChangedEventArgs e) {
if (e.PropertyName == "IsActive") {
var column = sender as Column;
RecomputeAllColumnsAreActiveOrInactive();
}
}
void RecomputeAllColumnsAreActiveOrInactive() {
this.allColumnsAreActive = this.columns.All(c => c.IsActive);
this.allColumnsAreInactive = this.columns.All(c => !c.IsActive);
UpdateAllColumnsAreActiveOrInactive();
}
void UpdateAllColumnsAreActiveOrInactive() {
AllColumnsAreActiveOrInactive = this.columns.Any()
&& (this.allColumnsAreActive || this.allColumnsAreInactive);
}
}
class Column : INotifyPropertyChanged {
Boolean isActive;
public Boolean IsActive {
get { return this.isActive; }
set {
if (this.isActive == value)
return;
this.isActive = value;
OnPropertyChanged("IsActive");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(String propertyName) {
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
为了完成这个解决方案Column/Data
,必须为收集重复收集方法Data/MyClass
。