2

最近、Silverlight で datacontext changed イベントの使用に関する問題に遭遇しました。

変更されたイベントをサブスクライブしてからすぐにサブスクライブを解除すると、例外がスローされます。

DataContextChanged += MainPage_DataContextChanged;
void MainPage_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
  var vm = e.NewValue as VM;
  if(vm != null)
  {
     DataContextChange-= MainPage_DataContextChanged;//throws invalidoperationexception for collection modified
  }
}

これを修正するには、後でイベントのサブスクライブを解除するだけです。この状況では、後でではなくすぐにサブスクライブを解除する必要があるため、これは機能します。

DataContextChanged += MainPage_DataContextChanged;
void MainPage_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
  var vm = e.NewValue as VM;
  if(vm != null)
  {
      //forces item onto the dispatcher queue so anything needing to happen with 'collections' happens first
      Dispatcher.BeginInvoke(()=>
        {
     DataContextChange-= MainPage_DataContextChanged;//throws invalidoperationexception for collection modified
         });
  }
}

コレクションは、ビジュアル ツリー内のすべての異なるコントロールの子要素であると推測しています。それらの更新はおそらくディスパッチャー キューで行われているのではないかと推測しているので、私の質問は次のとおりです。

イベントが発生した後にサブスクライブ解除されると、その後に変更または更新されるコレクションに影響するのはなぜですか?

編集:これを考えた後、これはイベントハンドラーの呼び出しリストが終了する前に変更されたことと関係がありますか?

4

1 に答える 1

3

呼び出しリストが変更されているというあなたの疑いは正しいです。

dotPeek の逆コンパイルに従って、DataContextChanged イベントを発生させるコードを次に示します。

private void RaisePublicDataContextChanged()
{
  if (this._dataContextChangedInfo == null)
    return;
  object oldValue = this._dataContextChangedInfo.OldValue;
  object dataContext = this.DataContext;
  if (oldValue == dataContext)
    return;
  this._dataContextChangedInfo.OldValue = dataContext;
  List<DependencyPropertyChangedEventHandler>.Enumerator enumerator = this._dataContextChangedInfo.ChangedHandlers.GetEnumerator();
  try
  {
    // ISSUE: explicit reference operation
    while (((List<DependencyPropertyChangedEventHandler>.Enumerator) @enumerator).MoveNext())
    {
      // ISSUE: explicit reference operation
      ((List<DependencyPropertyChangedEventHandler>.Enumerator) @enumerator).get_Current()((object) this, new DependencyPropertyChangedEventArgs(FrameworkElement.DataContextProperty, oldValue, dataContext));
    }
  }
  finally
  {
    enumerator.Dispose();
  }
}

ご覧のとおり、コードは列挙子を使用してハンドラーのコレクションを反復処理しています。したがって、ハンドラーの呼び出し中にイベントからサブスクライブを解除すると、列挙子が無効になり、表示されている例外が発生します。

于 2012-09-26T16:58:08.570 に答える