最近做一个WPF的项目时出现在更新Datagrid数据源时,抛出了一个错误,错误信息如下
CoreCLR Version: 6.0.3624.51421
.NET Version: 6.0.36
Description: The process was terminated due to an unhandled exception.
Exception Info: System.InvalidOperationException: 某个 ItemsControl 与它的项源不一致。\n请参见内部异常以获取更多信息。
---> System.Exception: 针对开发人员的信息(使用文本可视化工具来阅读此内容):
引发此异常的原因是名为“dataGridVehicleInfo”的控件“System.Windows.Controls.DataGrid Items.Count:21”的生成器已接收到一个 CollectionChanged 事件序列,这些事件与 Items 集合的当前状态不符。 检测到以下差异:
累积计数 19 与实际计数 21 不相同。[累积计数的计算方式为: 上次重置时的计数 + 添加数 - 自上次重置后的删除数。]
以下的一个或多个源可能已引发错误事件:
System.Windows.Controls.ItemContainerGenerator
System.Windows.Controls.ItemCollection
System.Windows.Data.ListCollectionView
* System.Collections.ObjectModel.ObservableCollection`1[[xxx.xxx.Models.VehicleModel, xxx.xxx, Version=3.3.1.3, Culture=neutral, PublicKeyToken=null]]
(标有星号的源被认为更有可能是问题的根源。)
最常见的原因有: (a)在未引发相应事件的情况下更改了集合或集合的计数,(b)引发的事件使用了错误的索引或项参数。
异常的堆栈跟踪将描述不一致情况是如何检测到的,而不是描述不一致情况是如何发生的。 要获得更及时的异常,应将生成器上附加的属性“PresentationTraceSources.TraceLevel”设置为值“High”,然后重新运行该方案。 完成此操作的一个方法是,在“即时”窗口中运行与下面的命令类似的命令:\n System.Diagnostics.PresentationTraceSources.SetTraceLevel(myItemsControl.ItemContainerGenerator, System.Diagnostics.PresentationTraceLevel.High)
。这会使得在发生每个 CollectionChanged 事件之后运行检测逻辑,因此,这将减慢应用程序的运行速度。
--- End of inner exception stack trace ---
at System.Windows.Controls.ItemContainerGenerator.Verify()
at System.Windows.Controls.VirtualizingStackPanel.MeasureChild(IItemContainerGenerator& generator, IContainItemStorage& itemStorageProvider, IContainItemStorage& parentItemStorageProvider, Object& parentItem, Boolean& hasUniformOrAverageContainerSizeBeenSet, Double& computedUniformOrAverageContainerSize, Double& computedUniformOrAverageContainerPixelSize, Boolean& computedAreContainersUniformlySized, Boolean& hasAnyContainerSpanChanged, IList& items, Object& item, IList& children, Int32& childIndex, Boolean& visualOrderChanged, Boolean& isHorizontal, Size& childConstraint, Rect& viewport, VirtualizationCacheLength& cacheSize, VirtualizationCacheLengthUnit& cacheUnit, Int64& scrollGeneration, Boolean& foundFirstItemInViewport, Double& firstItemInViewportOffset, Size& stackPixelSize, Size& stackPixelSizeInViewport, Size& stackPixelSizeInCacheBeforeViewport, Size& stackPixelSizeInCacheAfterViewport, Size& stackLogicalSize, Size& stackLogicalSizeInViewport, Size& stackLogicalSizeInCacheBeforeViewport, Size& stackLogicalSizeInCacheAfterViewport, Boolean& mustDisableVirtualization, Boolean isBeforeFirstItem, Boolean isAfterFirstItem, Boolean isAfterLastItem, Boolean skipActualMeasure, Boolean skipGeneration, Boolean& hasBringIntoViewContainerBeenMeasured, Boolean& hasVirtualizingChildren)
at System.Windows.Controls.VirtualizingStackPanel.MeasureOverrideImpl(Size constraint, Nullable`1& lastPageSafeOffset, List`1& previouslyMeasuredOffsets, Nullable`1& lastPagePixelSize, Boolean remeasure)
at System.Windows.Controls.VirtualizingStackPanel.MeasureOverride(Size constraint)
at System.Windows.Controls.Primitives.DataGridRowsPresenter.MeasureOverride(Size constraint)
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.ContextLayoutManager.UpdateLayout()
at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.ProcessQueue()
at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.Run()
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at System.Windows.Application.Run()这个异常通常发生在 WPF 的 ItemsControl(例如 DataGrid、ListBox 等)与其数据源同步出现问题时。它表示控件的内部生成器(ItemContainerGenerator)接收到的 CollectionChanged 事件序列与数据源的实际状态不一致,导致控件无法正确更新 UI。
异常原因分析
从异常信息可以看出:
控件是 DataGrid,当前 Items.Count 为 21。
内部生成器计算的累积计数为 19,与实际计数 21 不符。
累积计数的计算方式:上次重置时的计数 + 添加数 - 自上次重置后的删除数。
这通常意味着以下情况之一:
集合在非 UI 线程上被修改
ObservableCollection<T> 的 CollectionChanged 事件默认不会跨线程封送,如果在后台线程修改集合,UI 线程接收到的通知可能不完整或顺序错乱,导致生成器状态不一致。
集合的更改未通过 INotifyCollectionChanged 正确通知
如果绑定的是一个普通集合(如 List<T>),而不是实现了 INotifyCollectionChanged 的集合(如 ObservableCollection<T>),UI 无法感知到集合的更改,但开发者又手动添加/删除了项,就可能出现状态不匹配。
在 CollectionChanged 事件处理中直接修改了集合
在事件处理过程中再次修改集合(例如在 Add 或 Remove 后立即执行另一个修改),可能导致生成器收到的事件顺序与预期不符。
大量并发修改导致事件丢失或重复
如果短时间内对集合进行了多次添加/删除,而事件处理机制无法及时处理,也可能引发此异常。
Dispatcher 未正确使用
即使使用了 Dispatcher.Invoke,如果调用方式不当(如异步调用导致多个修改交错),也可能导致问题。
知道原因,接下来就来解决他
lock (Lock)
{
Application.Current.Dispatcher.Invoke(new Action<VehicleModel>((vehicle1) =>
{
//todo datagrid 数据源相关操作
Vehicles.Add(vehicle1);
}), vehicle);
}