2017-12-13 11 views
2

もっとコレクションをプロパティとして使用すると、ObserverableCollectionのCollectionChangedイベントが常に機能しないのはなぜですか?質問について

まず、私はC#に多少新たなんだ...空気私は明確にしましょう。私が取り組んでいるアプリケーションでこの問題にぶつかりました。この特定のクラスはObservableCollectionsをPropertiesとして公開していたので、ObservervableCollectionには独自のイベントがあるので、PropertyChangedイベントは必要ありませんでした。最初の試みは美しく働いた。それから、私はコードを整理し始めました。私は本当にバッキング・バールを必要としないことがわかったので、すべてをメソッドに移しました...そしてUIを更新しなくなりました。 INotifyPropertyChangedを追加すると問題は解決しましたが、私は非常に大きな「WHY?」と残されました。

ここで私が把握できるものを見るためにまとめたテストコードを示します。それは悪い習慣で散らばっていることをお勧めしますが、私は実際にはいつCollectionChangedに依存できるか、PropertyChagnedを追加する必要があるかを把握しようとしています。私はPropertyChangedをCollectionChangedに依存する必要がありますか?そうでなければ、ObservableCollectionのオーバーヘッドは本当に必要ないので、別のList型を使用するべきです。本当に両方を使うのは無駄なようです。

ここでコード

private TestClass test = new TestClass(); 

     public MainWindow() 
     { 
      InitializeComponent(); 
      this.DataContext = test; 
     } 

     internal class TestClass : INotifyPropertyChanged 
     { 
      public ObservableCollection<String> test1 = new ObservableCollection<String>(); 
      public ObservableCollection<String> test2 = new ObservableCollection<String>() { "T2-A" }; 

      public TestClass() 
      { 
      } 

      public ObservableCollection<String> Test1 { get => test1; set { } } 
      public ObservableCollection<String> Test2 { get => test2; set { this.test2 = value; } } 
      public ObservableCollection<String> Test3 { get; set; } 

      public event PropertyChangedEventHandler PropertyChanged; 

      public void OnPropertyChanged(string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
     } 

     private void BT1_Click(object sender, RoutedEventArgs e) 
     { 
      //Both of these work fine 
      test.test1.Add("T1-A"); 
      test.Test1.Add("T1-B"); 

      //First line works...Second line breaks it... but there is a setter. 
      test.Test2.Add("T2-C"); 
      test.Test2 = new ObservableCollection<String>() { "T2-A", "T2-B" }; 
      test.test2.Add("T2-D"); 

      //First Line throws Null exception... So it's not creating a backing var of it's own. 
      //test.Test3.Add("T3-A"); 
      var t3 = new ObservableCollection<String>() { "T3-B", "T3-C" }; 
      test.Test3 = t3; 
      test.Test3.Add("T3-D"); 
      //It updates when I trigger the property changed...but what broke CollectionChanged 
      test.OnPropertyChanged(nameof(test.Test3)); 
     } 

の大部分は、誰もが

<ListBox ItemsSource="{Binding Test1}" HorizontalAlignment="Left" Height="205" VerticalAlignment="Top" Width="161" Margin="10,5,0,0" /> 
     <ListBox ItemsSource="{Binding Test2}" Height="205" Margin="186,5,187,0" VerticalAlignment="Top" /> 
     <ListBox ItemsSource="{Binding Test3}" Height="205" Margin="366,5,8,0" VerticalAlignment="Top" /> 
     <Button x:Name="bT1" Content="Test It" HorizontalAlignment="Left" Height="33" Margin="10,215,0,0" VerticalAlignment="Top" Width="516" Click="BT1_Click" /> 

私の調査結果、これまで

興味を持っている場合、それはどうかと思われるXAMLですObservableCollectionを使用したいctionChangedイベントでは、バッキングvarを作成し、Collectionのインスタンスを変更しないでください。つまり、 'var col = new ObservableCollection()'ではなく、Clear()を使用してワイプと再構築を行います。私が紛失しているものはありますか? TwoWayのデータを見ると、これはむしろ薄れてくると思います。誰かがtest2の2行目に沿ってコードを壊すのを防ぐ方法はありますか?

+0

あなたは何も欠けていません。この物件を決して公共のセッターに与えないでください。それは起こるのを待っている事故です。これは、コレクションオブジェクトを公開するプロパティの場合には一般的に当てはまりますが、このオブジェクトはうまくいきます。外部コードが使用されるコレクションオブジェクトを決定する必要がある場合は、コンストラクタを介して渡す必要があります。そのため、一度しか設定できません。裏返しフィールド 'readonly'を宣言してそれを釘付けにします。 –

答えて

0

//第1行目...第2行目はそれを壊していますが、セッターがあります。

test.Test2.Add("T2-C"); 
test.Test2 = new ObservableCollection<String>() { "T2-A", "T2-B" }; 

まあ、新しいコレクションに値を設定しますが、どのような結合についてんセッター

public ObservableCollection<String> Test2 { get => test2; set { this.test2 = value; } } 

を見てみましょうか? バインディングで新しいオブジェクトを知るには、NotifyPropertyChangedイベントを発生させる必要があります。その値を設定した後、バインディングはまだ古いコレクション(まだコレクションの変更をリスニングしていますが、以前の値)を指しています。

private ObservableCollection<String> test2; 
public ObservableCollection<String> Test2 
{ 
    get 
    { 
     return test2; 
    } 
    set 
    { 
     test2 = value; 
     OnPropertyChanged("Test2"); 
    } 
} 

各プロパティにこの例を追加して、コレクション全体が新しいオブジェクトに更新したときに、あなたのバインディングが更新されます通知プロパティを追加すると、更新することが結合できるようになりますセッターを呼び出して変更します。すなわちproperty = new ObservableCollection... ListBoxは新しいオブジェクトのコレクションの変更を探します。

あなたは、コレクションの1つのインスタンスを保持し、それを変更することはできません。

あなたの最後の段落に対応して

、それについて移動するための別の方法があります。 (

次に、クラスのコンストラクタでコレクションを初期化します。あなたがオブジェクトを上書きしない新しいリストを作成する場合、次に

Test2 = new ObservableCollection<String>(); 

(または作品のいずれか、インラインでそれを初期化し、鍵は一度だけそれを初期化することです)。代わりにリストをクリアして、新しい値を次の場所に追加してください:

public void UpdateCollection(List<String> newValues) 
{ 
    Test2.Clear(); //notifies the list box with the CollectionChanged event 
    foreach(var value in newValues) 
    { 
     Test2.Add(value); //notifies the list box with the new item in the collection 
    } 
} 
+0

これはそれ以降になります。 ObservableCollectionをListに変更した場合、PropertyChangedイベントが発生し、リストボックスを更新しますか? 私たちが本当に変更プロセスを制限しない限り、私たちは本当に両方のイベントが必要であると言っているようです。 1つはコレクション内のデータの変更を通知し、もう1つはコレクション自体への変更を通知します。 – lumberajackshaw

+1

@lumberajackshaw ObservableCollectionをListに変更してPropertyChangedイベント呼び出しを追加した場合、オブジェクトを更新するときにリストボックスが更新されますが、リストにアイテムを追加するときに更新されないということは正しいです(ObservableCollectionが君は)。 ObservableCollectionオブジェクトを決して変更せず、アイテムをクリア/追加/削除するだけでない限り、両方のイベントが必要です – chancea

関連する問題