一文で書くとなんかややこしく感じたので箇条書き。
- Windows XP x86/.NET Framework4のWPFで(他の環境は未確認)
- DataContext内からCollectionChangedイベントが飛ぶような操作を(調べてないけどたぶん他の依存プロパティも)
- Dispatcherスレッド以外ですると
- NotSupportedExceptionになる。
public class Data :INotifyPropertyChanged { public ObservableCollection<Container> _Collection; public ObservableCollection<Container> Collection { get { return _Collection; } set { _Collection = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Collection")); } } } public class Container : INotifyPropertyChanged { private string _Value; public string Value { get { return _Value; } set { _Value = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Value")); } } }で、以下の操作をDispatcherスレッド以外からすると、
Data dataContext = (Data)DataContext; IList<Container> collection = datContext.Collection; // OK dataContext.Collection = new ObservableCollection<Container>( new Container[] { new Container() { Value = "Value1" }, new Container() { Value = "Value2" } } ); // OK collection[0].Value = "Value1"; collection[1].Value = "Value2"; // NotSupportedException collection.Clear(); // NotSupportedException collection.Add(new Container() { Value = "Value1" }); // NotSupportedException collection[0] = new Container() { Value = "Value1" };
OKのコードがと例外が出るものとの違いは、プロパティの中身を総書き換えするためCollectionChangedではなくPropertyChangedが飛ぶからと思われます。コレクション操作でも例外が飛ばなくなれば便利なのですが、foreach内で回している最中のIEnumerableにアイテムを追加・削除できないのと同様、改善は期待できないような気がする…
参考リンクで回答されているようにDispatcher.Invokeを使えば何でもできるのですが、MVVMの構造を崩しかねない(*1)のでコレクションを総交換するかコレクション内の各アイテムの値をちまちま書き換えてPropertyChangedイベントで対処した方がいいかなぁ。
(*1) Viewにコレクション操作用のメソッド追加したり、ModelやViewModelがDispatcherを得ようとしたり
[参考] Where do I get a thread-safe CollectionView? (Stackoverflow)
[2013/4/4 追記] 続き
Comments