Wpf 集合内部的集合更改时DataGrid的通知

Wpf 集合内部的集合更改时DataGrid的通知,wpf,silverlight,data-binding,datagrid,inotifypropertychanged,Wpf,Silverlight,Data Binding,Datagrid,Inotifypropertychanged,几个小时以来,我一直在研究一个非常棘手的问题: 当可观察集合内的另一个可观察集合绑定到可观察集合时,绑定到可观察集合的数据网格如何正确更新 到目前为止,当我单击相应的单元格时,DataGrid只会刷新 我准备了一个完整的源代码示例来说明以下(非常简单)情况: 有一个保存列表的ViewModel。此列表是一个ObservableCollection,包含两个内容:一个整数和另一个包含四个整数的列表(同样是ObservableCollection)。 然后是一个有两列的DataGrid。一列表示整数

几个小时以来,我一直在研究一个非常棘手的问题:

可观察集合内的另一个可观察集合绑定到可观察集合时,绑定到可观察集合数据网格如何正确更新

到目前为止,当我单击相应的单元格时,DataGrid只会刷新

我准备了一个完整的源代码示例来说明以下(非常简单)情况:

有一个保存列表的ViewModel。此列表是一个ObservableCollection,包含两个内容:一个整数和另一个包含四个整数的列表(同样是ObservableCollection)。 然后是一个有两列的DataGrid。一列表示整数,一列表示整数列表。 这个小应用程序有修改嵌套列表中整数的按钮,即将+1添加到四个整数中的一个。 的目标是通过DataGrid反映对嵌套列表的修改。 到目前为止,的问题是,这种情况只发生在具有外部触发器的情况下(例如,单击相应的单元格,或单击一个列标题对列进行排序等)

下面是完整的代码:

这是DataGrid绑定到的ViewModel代码:

public class ViewModel: INotifyPropertyChanged {

    private Items items;

    public Items Items {
        get { return items; }
        set {
            items = value;
            firePropertyChanged("Items");
        }
    }

    public ViewModel() {

        Items = new Items();
    }

    private void firePropertyChanged(string property) {

        if (PropertyChanged != null) {


            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

public class Items: ObservableCollection<Item> {

    public Items()
        : base() {

        this.CollectionChanged += new NotifyCollectionChangedEventHandler(OnCollectionChanged);
    }

    private void OnCollectionChanged(object o, NotifyCollectionChangedEventArgs e) {


        if (e.NewItems != null) {

            foreach (Object item in e.NewItems) {

                (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }

        if (e.OldItems != null) {

            foreach (Object item in e.OldItems) {

                (item as INotifyPropertyChanged).PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }
    }

    void item_PropertyChanged(object sender, PropertyChangedEventArgs e) {

        NotifyCollectionChangedEventArgs a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
        OnCollectionChanged(a);
    }
}

public class List: ObservableCollection<NumberItem> {

    public List()
        : base() {

        this.CollectionChanged += new NotifyCollectionChangedEventHandler(OnCollectionChanged);
    }

    private void OnCollectionChanged(object o, NotifyCollectionChangedEventArgs e) {

        if (e.NewItems != null) {

            foreach (Object item in e.NewItems) {

                (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }

        if (e.OldItems != null) {

            foreach (Object item in e.OldItems) {

                (item as INotifyPropertyChanged).PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }
    }

    void item_PropertyChanged(object sender, PropertyChangedEventArgs e) {

        NotifyCollectionChangedEventArgs a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
        OnCollectionChanged(a);
    }
}

public class NumberItem : INotifyPropertyChanged {

    private int number;

    public int Number {
        get { return number; }
        set {
            number = value;
            firePropertyChanged("Number");   
        }
    }

    public NumberItem(int i) {

        Number = i;
    }

    private void firePropertyChanged(string property) {

        if (PropertyChanged != null) {

            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

}

public class Item : INotifyPropertyChanged {

    private List list;

    public List List {
        get { return list; }
        set {
            list = value;
            firePropertyChanged("List");
        }
    }

    private int numberOne;

    public int NumberOne {
        get { return numberOne; }
        set {
            numberOne = value;
            firePropertyChanged("NumberOne");
        }
    }

    private void firePropertyChanged(string property) {

        if (PropertyChanged != null) {

            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

/// <summary>
/// This converter simply transforms the list of integers into a string.
/// </summary>
public class Converter : IValueConverter {

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {

    List l = (List)value;

    string s = "";

    return s + l[0].Number + " " + l[1].Number + " " + l[2].Number + " " + l[3].Number;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {

        return null;
    }
}
private void plus1L(object sender, RoutedEventArgs e) {

        vm.Items[0].List[0].Number += 1;

    }
<sdk:DataGrid x:Name="dg" Margin="17,139,21,0" ItemsSource="{Binding Items}" AutoGenerateColumns="False" VerticalAlignment="Top" Height="164" d:LayoutOverrides="Width, HorizontalMargin">
        <sdk:DataGrid.Columns>
            <sdk:DataGridTextColumn x:Name="A" Header="A" Binding="{Binding NumberOne}"/>
            <sdk:DataGridTextColumn x:Name="List" Header="List" Binding="{Binding List, Converter={StaticResource Converter}}"/>
        </sdk:DataGrid.Columns>
    </sdk:DataGrid>*emphasized text*
最后,这是绑定DataGrid的XAML:

public class ViewModel: INotifyPropertyChanged {

    private Items items;

    public Items Items {
        get { return items; }
        set {
            items = value;
            firePropertyChanged("Items");
        }
    }

    public ViewModel() {

        Items = new Items();
    }

    private void firePropertyChanged(string property) {

        if (PropertyChanged != null) {


            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

public class Items: ObservableCollection<Item> {

    public Items()
        : base() {

        this.CollectionChanged += new NotifyCollectionChangedEventHandler(OnCollectionChanged);
    }

    private void OnCollectionChanged(object o, NotifyCollectionChangedEventArgs e) {


        if (e.NewItems != null) {

            foreach (Object item in e.NewItems) {

                (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }

        if (e.OldItems != null) {

            foreach (Object item in e.OldItems) {

                (item as INotifyPropertyChanged).PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }
    }

    void item_PropertyChanged(object sender, PropertyChangedEventArgs e) {

        NotifyCollectionChangedEventArgs a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
        OnCollectionChanged(a);
    }
}

public class List: ObservableCollection<NumberItem> {

    public List()
        : base() {

        this.CollectionChanged += new NotifyCollectionChangedEventHandler(OnCollectionChanged);
    }

    private void OnCollectionChanged(object o, NotifyCollectionChangedEventArgs e) {

        if (e.NewItems != null) {

            foreach (Object item in e.NewItems) {

                (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }

        if (e.OldItems != null) {

            foreach (Object item in e.OldItems) {

                (item as INotifyPropertyChanged).PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }
    }

    void item_PropertyChanged(object sender, PropertyChangedEventArgs e) {

        NotifyCollectionChangedEventArgs a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
        OnCollectionChanged(a);
    }
}

public class NumberItem : INotifyPropertyChanged {

    private int number;

    public int Number {
        get { return number; }
        set {
            number = value;
            firePropertyChanged("Number");   
        }
    }

    public NumberItem(int i) {

        Number = i;
    }

    private void firePropertyChanged(string property) {

        if (PropertyChanged != null) {

            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

}

public class Item : INotifyPropertyChanged {

    private List list;

    public List List {
        get { return list; }
        set {
            list = value;
            firePropertyChanged("List");
        }
    }

    private int numberOne;

    public int NumberOne {
        get { return numberOne; }
        set {
            numberOne = value;
            firePropertyChanged("NumberOne");
        }
    }

    private void firePropertyChanged(string property) {

        if (PropertyChanged != null) {

            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

/// <summary>
/// This converter simply transforms the list of integers into a string.
/// </summary>
public class Converter : IValueConverter {

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {

    List l = (List)value;

    string s = "";

    return s + l[0].Number + " " + l[1].Number + " " + l[2].Number + " " + l[3].Number;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {

        return null;
    }
}
private void plus1L(object sender, RoutedEventArgs e) {

        vm.Items[0].List[0].Number += 1;

    }
<sdk:DataGrid x:Name="dg" Margin="17,139,21,0" ItemsSource="{Binding Items}" AutoGenerateColumns="False" VerticalAlignment="Top" Height="164" d:LayoutOverrides="Width, HorizontalMargin">
        <sdk:DataGrid.Columns>
            <sdk:DataGridTextColumn x:Name="A" Header="A" Binding="{Binding NumberOne}"/>
            <sdk:DataGridTextColumn x:Name="List" Header="List" Binding="{Binding List, Converter={StaticResource Converter}}"/>
        </sdk:DataGrid.Columns>
    </sdk:DataGrid>*emphasized text*

*强调文本*
当您更改
列表
属性的任何内容时,只需为该属性触发更改事件,这并不难

编辑:在处理程序中,您以某种方式更改列表,但您什么也不做

private void plus1L(object sender, RoutedEventArgs e)
{
    vm.Items[0].List[0].Number += 1;
    vm.Items[0].OnPropertyChanged("List"); // This is needed if you bind to List.
}
更明确地说:绑定只关心绑定到的属性路径。绑定属性内部发生的一切都是未知的,因此您需要转发内部更改。

当您更改有关它的任何内容时,只需为
列表
属性触发更改事件,这并不难

编辑:在处理程序中,您以某种方式更改列表,但您什么也不做

private void plus1L(object sender, RoutedEventArgs e)
{
    vm.Items[0].List[0].Number += 1;
    vm.Items[0].OnPropertyChanged("List"); // This is needed if you bind to List.
}

更明确地说:绑定只关心绑定到的属性路径。绑定属性内部发生的一切都是未知的,因此需要转发内部更改。

为什么人们坚持创建继承自
ObservableCollection
的类?他们是否认为使用
observateCollection
作为数据类型并使用内置的更改通知有问题

无论如何,要这样做:

public class SomeViewModel : INotifyPropertyChanged
{
    public ObservableCollection<MyItem> OuterCollection { get; set; }
}

public class MyItem : INotifyPropertyChanged
{
    public int SomeInt { get; set; }
    public ObservableCollection<int> InnerCollection { get; set; }
}
如果
InnerCollection
包含实现
INotifyPropertyChanged
的对象,您可以订阅它们的
PropertyChanged
事件,以便在其中一个项目更改时引发
InnerCollection
PropertyChanged
事件

void SomeConstructor()
{
    InnerCollection = new ObservableCollection<SomeItem>();
    InnerCollection.CollectionChanged += InnerCollection_CollectionChanged;
}

void InnerCollection_CollectionChanged(object sender, CollectionChangedEventArgs e)
{
    if (e.NewItems != null)
        for each (SomeItem item in e.NewItems)
            item.PropertyChanged += SomeItem_PropertyChanged;

    if (e.OldItems!= null)
        for each (SomeItem item in e.OldItems)
            item.PropertyChanged -= SomeItem_PropertyChanged;
}

void SomeItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    RaisePropertyChanged("InnerCollection");
}
void SomeConstructor()
{
InnerCollection=新的ObservableCollection();
InnerCollection.CollectionChanged+=InnerCollection\u CollectionChanged;
}
void InnerCollection\u CollectionChanged(对象发送方,CollectionChangedEventArgs e)
{
如果(如NewItems!=null)
对于每个(e.NewItems中的SomeItem项)
item.PropertyChanged+=某些item\u PropertyChanged;
如果(例如,OldItems!=null)
对于每个(e.OldItems中的SomeItem项目)
item.PropertyChanged-=SomeItem\u PropertyChanged;
}
void SomeItem\u PropertyChanged(对象发送方,PropertyChangedEventArgs e)
{
RaisePropertyChanged(“内部集合”);
}

为什么人们坚持创建继承自
ObservableCollection
的类?他们是否认为使用
observateCollection
作为数据类型并使用内置的更改通知有问题

无论如何,要这样做:

public class SomeViewModel : INotifyPropertyChanged
{
    public ObservableCollection<MyItem> OuterCollection { get; set; }
}

public class MyItem : INotifyPropertyChanged
{
    public int SomeInt { get; set; }
    public ObservableCollection<int> InnerCollection { get; set; }
}
如果
InnerCollection
包含实现
INotifyPropertyChanged
的对象,您可以订阅它们的
PropertyChanged
事件,以便在其中一个项目更改时引发
InnerCollection
PropertyChanged
事件

void SomeConstructor()
{
    InnerCollection = new ObservableCollection<SomeItem>();
    InnerCollection.CollectionChanged += InnerCollection_CollectionChanged;
}

void InnerCollection_CollectionChanged(object sender, CollectionChangedEventArgs e)
{
    if (e.NewItems != null)
        for each (SomeItem item in e.NewItems)
            item.PropertyChanged += SomeItem_PropertyChanged;

    if (e.OldItems!= null)
        for each (SomeItem item in e.OldItems)
            item.PropertyChanged -= SomeItem_PropertyChanged;
}

void SomeItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    RaisePropertyChanged("InnerCollection");
}
void SomeConstructor()
{
InnerCollection=新的ObservableCollection();
InnerCollection.CollectionChanged+=InnerCollection\u CollectionChanged;
}
void InnerCollection\u CollectionChanged(对象发送方,CollectionChangedEventArgs e)
{
如果(如NewItems!=null)
对于每个(e.NewItems中的SomeItem项)
item.PropertyChanged+=某些item\u PropertyChanged;
如果(例如,OldItems!=null)
对于每个(e.OldItems中的SomeItem项目)
item.PropertyChanged-=SomeItem\u PropertyChanged;
}
void SomeItem\u PropertyChanged(对象发送方,PropertyChangedEventArgs e)
{
RaisePropertyChanged(“内部集合”);
}

是的,你告诉我的。我照你说的做了;)但它仍然不起作用。以上是我的完整源代码——如果我在将其包含在源代码中之前几个小时正确理解了您的意思,那么现在就可以了。我只是因为太多的树而失去了森林的焦点。而且,花几个小时关注同一个问题也不是一个好办法。尽管如此,如果我有可能,我会付你一杯啤酒和/或和你一起喝。。。但在这里我只能说:谢谢!:)。。。但我想我已经让你非常恼火了,以至于你会在没有我的情况下喝啤酒;)@马克:不客气:)别担心,但是你可以在你的另一个问题中澄清这个问题,因为你没有给我完整的情况,我想这会给我们双方都省去一些麻烦。是的,你告诉我的。我照你说的做了;)但它仍然不起作用。以上是我完整的源代码-a