如何在WPF中检测来自父对象的子项的更改?

时间:2012-04-05 14:37:17

标签: c# wpf xaml design-patterns

我们有一个WPF应用程序,假设我有一个Department对象,其中我有一个ObservableCollection的课程,在每个课程中我有一个ObservableCollection的教师,在每个教师里面我有一个ObservableCollection的学生:

Department.cs ---- ObservableCollection课程

Course.cs ---- ObservableCollection教师

Teacher.cs ---- ObservableCollection学生

Student.cs ----姓名,年龄等

这4个类都实现了INotifyPropertyChanged,它们自己工作正常,我想要实现的是当Department对象图中的任何内容被更改时,是否是学生的年龄变化,或者添加了新课程还是教师删除,任何事情,我想知道它,理想情况下通过像DepartmentChanged这样的事件(理想情况下这只会加一次),所以我可以调用其他一些长时间运行的动作。

我很难想出一个干净的方法来做到这一点。据我所知,INotifyPropertyChanged仅跟踪类的属性更改,而ObservationCollection不知道其中一个项是否已更改。

到目前为止,我尝试了嵌套循环(伪代码):

foreach (var course in d.Courses){
    foreach (var teacher in course.Teachers){
        foreach (var student in teacher.Students){
            ((INotifyPropertyChanged)student).PropertyChanged += ...
        }
        teacher.Students.CollectionChanged += ...
    }
}

但是可以想象,这会创建许多PropertyChanged事件,因为每个属性都订阅了它,而我想在Department中执行的操作最终被执行了很多次,这很糟糕。

然后我尝试使用一个计时器(我们的每个类都有一个IsDirty()来确定对象是否已经改变),间隔设置为500:

if(department != null && department.IsDirty()){
    //call some long running Action here
}

虽然这似乎有效,但我觉得这种方法是错误的,如果可能的话,我真的想避免使用计时器,我很乐意听到其他建议和想法!

谢谢,

2 个答案:

答案 0 :(得分:1)

这只是一个简单的想法,没有计时器,你可以在课程上实现IsDirty事件,&老师班?

例如,教师,内部PropertyChanged上的经理(当学生集合更改时正确订阅/取消订阅)。 当任何一个学生有一个有效的变化时,它所要做的只是提出IsDirty事件,所有事件都将学生obj作为一个参数。

课程将倾听并捕捉老师。这是一个很重要的事件,并将提升自己的课程.IsDirty WhereEventEventArgs = new CourseEventArgs将有一个课程,教师与学生有脏字。

在Department类中,您可以收听Course IsDirty事件并解析args ......

这个想法,似乎与你有同样的事情,但不是真的,因为它只处理最低级别的任何PropertyChanged然后利用你自己的事件将必要的信息鼓泡到顶部。其中顶级类别与学生的PropertyChanged行为实施以及广泛的喋喋不休。 此外,如果在学生级别,您进行更改(通过UI,发布它们,单击保存或其他),那么您可以控制何时引发IsDirty,可能只有一次。

答案 1 :(得分:1)

我认为您仍然需要在单个对象级别使用IsDrity位和INotifyPropertyChanged,但为什么不扩展ObservableCollection<T>并覆盖一些基本方法来处理对象属性更改的注册?例如,创建类似的东西:

public class ChangeTrackerCollection<T> : ObservableCollection<T>
{
    protected override void ClearItems()
    {
        foreach (var item in this)
            UnregisterItemEvents(item);

        base.ClearItems();
    }

    protected override void InsertItem(int index, T item)
    {
        base.InsertItem(index, item);

        RegisterItemEvents(item);
    }

    protected override void RemoveItem(int index)
    {
        UnregisterItemEvents(this[index]);

        base.RemoveItem(index);
    }

    private void RegisterItemEvents(T item)
    {
        item.PropertyChanged += this.OnItemPropertyChanged;
    }

    private void UnregisterItemEvents(T item)
    {
        item.PropertyChanged -= this.OnItemPropertyChanged;
    }

    private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        //TODO: raise event to parent object to notify that there was a change...
    }
}