2012-09-26 19 views
2

我最近偶然发现了Silverlight中的一个问题,它使用了datacontext已更改的事件。为什么取消订阅DataContextChanged会导致InvalidOperation异常,因为Collection被修改

如果您订阅改变事件,然后立即取消它会抛出一个异常,

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

我猜的集合是在视觉树所有不同的控件的子元素,和我猜他们的更新是在调度程序队列中可能发生的事情,所以我的问题是这样的:

为什么事件在启动后会被取消订阅会影响在此之后要修改或更新的集合?

编辑: 给了这个想法之后这可能与事件处理程序调用列表在完成之前被修改有关吗?

+0

你有没有得到一个消息,例外? – Patrick

回答

3

您对修改调用列表的怀疑是正确的。

这里是触发该事件DataContextChanged仅代码,根据dotPeek的反编译:

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

正如你看到的,代码是用枚举器通过处理程序的集合进行迭代。因此,当您在调用处理程序期间取消订阅事件时,您正在使枚举数无效,导致您看到的异常。

+0

真棒,谢谢! – tam

相关问题