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