1

因为我在一个非常困难的问题的工作时间:通知时,里面收藏的变化

如何是DataGrid中绑定到一个的ObservableCollection正确更新当其他的ObservableCollection是里面ObservableCollectionDataGrid必然会发生变化?

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

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

有保持的列表的视图模型。这个List是一个ObservableCollection,它包含两个东西:一个整数和另一个List(同样是一个ObservableCollection),它包含四个整数。 然后有一个DataGrid有两列。一列用于整数列表,一列用于整数列表。 这个小应用程序有按钮来修改嵌套列表中的整数,即将+1添加到四个整数中的一个。 目标是由嵌套列表的修改得到DataGrid的反映。 的问题到目前为止,这仅在发生与外部触发(例如,在对应的单元的点击,或在一个列标题的点击的排序列等)

因此,这里是完整的代码:

这是DataGrid绑定到视图模型的代码:

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; 
    } 
} 

操纵的耐思特的整数按钮的代码d名单如下:

private void plus1L(object sender, RoutedEventArgs e) { 

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

    } 

最后这是在数据网格中得到绑定的XAML:你只需要火的List属性时更改事件

<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* 

回答

3

I already told you你更改什么关于它,它并不那么难...

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

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. 
} 

为了更加明确这一点:绑定不关心比你绑定属性路径的任何其他。绑定属性内发生的所有事情都是未知的,因此您需要转发内部更改。

+0

大声笑..看起来像你被捣毁@marc – 2012-02-07 18:24:58

+0

是的,你告诉过我。我做了你告诉我的事情)但它仍然无法正常工作。以上是我完整的源代码 - 如果我在将它包含在源代码中之前几个小时才能理解你。 – 2012-02-07 18:27:03

+1

@Marc:查看编辑。 – 2012-02-07 18:31:18

1

为什么人们坚持要创建从ObservableCollection<SomeObject>继承的类?他们认为使用ObservableCollection<Item>作为数据类型并使用内置更改通知有什么不对吗?

不管怎么说,这样做:

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; } 
} 

你的XAML可以像正常的,但是如果你在InnerCollection更改值,WPF不知道这件事,因为ObservableCollection应该监控变为收集,而不是集合中项目的更改。

要更新UI,您需要为InnerCollection提出PropertyChange通知。

myItem.InnerCollection[0]++; 
myItem.RaisePropertyChanged("InnerCollection"); 

如果InnerCollection包含实现INotifyPropertyChanged对象,您可以订阅他们的PropertyChanged活动,以提高PropertyChanged事件InnerCollection时的物品的变化之一。

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"); 
} 
+0

您将索引转换为没有索引器的对象... – 2012-02-07 18:40:08

+0

Hello Rachel :)很高兴再次读您:) H.B.伴随着我整天与我绑定的问题,我们现在解决了..不过谢谢你给我正确的提示(myItem.RaisePropertyChanged(“InnerCollection”);)...关于ObservableCollection继承:我认为这是必要的以一些能力扩展集合,但老实说,我主要是为了支持一般的编程风格(即如果我想扩展我能够),并且因为_ItemList items_看起来比大多数情况下的_ObservableCollection items_更合适:) – 2012-02-07 18:57:43

+0

@Marc我想,如果你将'List '扩展到它自己的类中,那么我可以看到你的推理,但是如果你不会(我不会!),那么它不需要是它自己的类。类型名称说明了一切:它是一个可观察的集合,它只是一个包含更改通知的集合。很高兴你把它整理出来。 – Rachel 2012-02-07 19:15:42