我最近在使用datacontext更改事件的Silverlight中遇到了一个问题。
如果您订阅已更改的事件,然后立即取消订阅,则会抛出异常,
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 :(得分: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();
}
}
如您所见,代码使用枚举器来遍历处理程序集合。因此,当您在调用处理程序期间取消订阅该事件时,您将使枚举数无效,从而导致您看到的异常。