Что касается 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>
, чтобы создать свойство с некоторым методом, который можно передать в 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>
.
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
var tasks = new ObservableCollectionWithSubscribers<Task>();
tasks.SubscribeToChanges(Notify, "Completed");
Tasks = tasks;
}
public ObservableCollection<Task> Tasks { get; private set; }
public int Completed
{
get { return Tasks.Count(t => t.IsComplete); }
}
public event PropertyChangedEventHandler PropertyChanged;
void Notify(string property)
{
var handler = PropertyChanged;
if(handler != null) handler(this, new PropertyChangedEventArgs(property));
}
}