【问题标题】:Workaround for WPF Freezable bug?WPF Freezable 错误的解决方法?
【发布时间】:2011-08-22 14:11:15
【问题描述】:

我最近遇到了一个非常糟糕的 WPF 错误。我认为它与 Microsoft Connect 上的 this bug 相同。

我们的应用程序使用 Visual Studio 2010 以 .NET 4.0 Client Profile 为目标。

基本上,当 ViewModel 触发对任何属性或集合的更改导致项目在 ItemsControl 中移动时,就有可能引发以下异常。它并不总是发生,而且似乎是基于不同时间的不同触发器而发生的。似乎更有可能在启动应用程序后不久。如果您可以在几分钟内使用它而不会触发异常,那么您可能永远不会在该应用程序实例期间触发。

与 Connect 错误报告一样,我使用 {DynamicResource key}ResourceDictionary 加载 SolidColorBrushes。一些字典是手动加载的(用于主题支持)。我尝试手动冻结这些词典中的所有内容,但似乎没有帮助。

最近,当我在主窗口中添加了几个 UserControls 并将其中的 ItemsControls 绑定到 ObservableCollections 时,异常变得更加频繁。以前,我在 50 次中只会看到 1 次异常,但现在我在使用该程序的 5 次中看到了 4 次。

有人对解决方法有任何想法吗? Connect 错误表明这可能会在下一个 .NET 版本(无论何时)中得到修复,但这个错误使我们的应用程序现在基本上无法使用。

System.InvalidOperationException:“System.Windows.Media.SolidColorBrush”类型的指定值必须将 IsFrozen 设置为 false 才能修改。 在 System.Windows.Freezable.WritePreamble() 在 System.Windows.Freezable.remove_Changed(EventHandler 值) 在 System.Windows.ResourceReferenceExpression.ResourceReferenceExpressionWeakContainer.RemoveChangedHandler() 在 System.Windows.ResourceReferenceExpression.ResourceReferenceExpressionWeakContainer.InvalidateTargetSubProperty(对象发件人,EventArgs args) 在 System.Windows.Freezable.FireChanged() 在 System.Windows.Freezable.Freeze(布尔 isChecking) 在 System.Windows.Freezable.Freeze() 在 System.Windows.Freezable.System.Windows.ISealable.Seal() 在 System.Windows.StyleHelper.SealIfSealable(对象值) 在 System.Windows.StyleHelper.GetChildValueHelper(UncommonField`1 dataField,ItemStructList`1& valueLookupList,DependencyProperty dp,DependencyObject 容器,FrameworkObject 子项,Int32 childIndex,布尔样式查找,EffectiveValueEntry& 条目,ValueLookupType& sourceType,FrameworkElementFactory templateRoot) 在 System.Windows.StyleHelper.GetChildValue(UncommonField`1 数据字段,DependencyObject 容器,Int32 childIndex,FrameworkObject 子项,DependencyProperty dp,FrugalStructList`1& childRecordFromChildIndex,EffectiveValueEntry& 条目,ValueLookupType& sourceType,FrameworkElementFactory templateRoot) 在 System.Windows.StyleHelper.GetValueFromTemplatedParent(DependencyObject 容器、Int32 childIndex、FrameworkObject 子项、DependencyProperty dp、FrugalStructList`1& childRecordFromChildIndex、FrameworkElementFactory templateRoot、EffectiveValueEntry& 条目) 在 System.Windows.FrameworkElement.GetValueFromTemplatedParent(DependencyProperty dp,EffectiveValueEntry& 条目) 在 System.Windows.FrameworkElement.GetRawValue(DependencyProperty dp,PropertyMetadata 元数据,EffectiveValueEntry& 条目) 在 System.Windows.FrameworkElement.EvaluateBaseValueCore(DependencyProperty dp,PropertyMetadata 元数据,EffectiveValueEntry & newEntry) 在 System.Windows.DependencyObject.EvaluateEffectiveValue(EntryIndex entryIndex,DependencyProperty dp,PropertyMetadata 元数据,EffectiveValueEntry oldEntry,EffectiveValueEntry newEntry,OperationType operationType) 在 System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex、DependencyProperty dp、PropertyMetadata 元数据、EffectiveValueEntry oldEntry、EffectiveValueEntry& newEntry、Boolean coerceWithDeferredReference、Boolean coerceWithCurrentValue、OperationType operationType) 在 System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp) 在 System.Windows.StyleHelper.InvalidateResourceDependentsForChild(DependencyObject 容器,DependencyObject 子项,Int32 childIndex,ResourcesChangeInfo 信息,FrameworkTemplate parentTemplate) 在 System.Windows.TreeWalkHelper.InvalidateStyleAndReferences(DependencyObject d,ResourcesChangeInfo 信息,布尔 containsTypeOfKey) 在 System.Windows.TreeWalkHelper.OnResourcesChanged(DependencyObject d,ResourcesChangeInfo 信息,布尔 raiseResourceChangedEvent) 在 System.Windows.FrameworkElement.OnAncestorChangedInternal(TreeChangeInfo parentTreeState) 在 System.Windows.TreeWalkHelper.OnAncestorChanged(DependencyObject d,TreeChangeInfo 信息) 在 System.Windows.DescendentsWalker`1._VisitNode(DependencyObject d) 在 MS.Internal.PrePostDescendentsWalker`1._VisitNode(DependencyObject d) 在 System.Windows.DescendentsWalker`1.VisitNode(FrameworkElement fe) 在 System.Windows.DescendentsWalker`1.VisitNode(DependencyObject d) 在 System.Windows.DescendentsWalker`1.WalkFrameworkElementLogicalThenVisualChildren(FrameworkElement feParent, Boolean hasLogicalChildren) 在 System.Windows.DescendentsWalker`1.IterateChildren(DependencyObject d) 在 System.Windows.DescendentsWalker`1.StartWalk(DependencyObject startNode, Boolean skipStartNode) 在 MS.Internal.PrePostDescendentsWalker`1.StartWalk(DependencyObject startNode, Boolean skipStartNode) 在 System.Windows.TreeWalkHelper.InvalidateOnTreeChange(FrameworkElement fe,FrameworkContentElement fce,DependencyObject 父级,布尔 isAddOperation) 在 System.Windows.FrameworkElement.OnVisualParentChanged(DependencyObject oldParent) 在 System.Windows.Media.Visual.FireOnVisualParentChanged(DependencyObject oldParent) 在 System.Windows.Media.Visual.RemoveVisualChild(视觉孩子) 在 System.Windows.Media.VisualCollection.DisconnectChild(Int32 索引) 在 System.Windows.Media.VisualCollection.Clear() 在 System.Windows.Controls.UIElementCollection.ClearInternal() 在 System.Windows.Controls.Panel.ClearChildren() 在 System.Windows.Controls.Panel.OnItemsChangedInternal(对象发送者,ItemsChangedEventArgs 参数) 在 System.Windows.Controls.Panel.OnItemsChanged(对象发送者,ItemsChangedEventArgs args) 在 System.Windows.Controls.ItemContainerGenerator.OnRefresh() 在 System.Windows.Controls.ItemContainerGenerator.OnCollectionChanged(对象发送者,NotifyCollectionChangedEventArgs 参数) 在 System.Windows.Controls.ItemContainerGenerator.System.Windows.IWeakEventListener.ReceiveWeakEvent(类型 managerType,对象发送者,EventArgs e) 在 System.Windows.WeakEventManager.DeliverEventToList(对象发送者,EventArgs 参数,ListenerList 列表) 在 System.Windows.WeakEventManager.DeliverEvent(对象发送者,EventArgs 参数) 在 System.Collections.Specialized.CollectionChangedEventManager.OnCollectionChanged(对象发送者,NotifyCollectionChangedEventArgs 参数) 在 System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(对象发送者,NotifyCollectionChangedEventArgs e) 在 System.Windows.Data.CollectionView.OnCollectionChanged(NotifyCollectionChangedEventArgs 参数) 在 System.Windows.Controls.ItemCollection.System.Windows.IWeakEventListener.ReceiveWeakEvent(类型 managerType,对象发送者,EventArgs e) 在 System.Windows.WeakEventManager.DeliverEventToList(对象发送者,EventArgs 参数,ListenerList 列表) 在 System.Windows.WeakEventManager.DeliverEvent(对象发送者,EventArgs 参数) 在 System.Collections.Specialized.CollectionChangedEventManager.OnCollectionChanged(对象发送者,NotifyCollectionChangedEventArgs 参数) 在 System.Windows.Data.CollectionView.OnCollectionChanged(NotifyCollectionChangedEventArgs 参数) 在 System.Windows.Data.ListCollectionView.RefreshOverride() 在 System.Windows.Data.CollectionView.Refresh() 在 System.Windows.Data.CollectionView.EndDefer() 在 System.Windows.Data.CollectionView.DeferHelper.Dispose() 在 System.Windows.Controls.ItemCollection.SetCollectionView(CollectionView 视图) 在 System.Windows.Controls.ItemCollection.SetItemsSource(IEnumerable 值) 在 System.Windows.Controls.ItemsControl.OnItemsSourceChanged(DependencyObject d,DependencyPropertyChangedEventArgs e) 在 System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e) 在 System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e) 在 System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs 参数) 在 System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex、DependencyProperty dp、PropertyMetadata 元数据、EffectiveValueEntry oldEntry、EffectiveValueEntry& newEntry、Boolean coerceWithDeferredReference、Boolean coerceWithCurrentValue、OperationType operationType) 在 System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp) 在 System.Windows.Data.BindingExpressionBase.Invalidate(布尔 isASubPropertyChange) 在 System.Windows.Data.BindingExpression.TransferValue(对象 newValue,布尔 isASubPropertyChange) 在 System.Windows.Data.BindingExpression.ScheduleTransfer(布尔 isASubPropertyChange) 在 MS.Internal.Data.ClrBindingWorker.NewValueAvailable(布尔依赖源更改,布尔初始值,布尔 isASubPropertyChange) 在 MS.Internal.Data.PropertyPathWorker.UpdateSourceValueState(Int32 k,ICollectionView 集合视图,对象 newValue,布尔 isASubPropertyChange) 在 MS.Internal.Data.ClrBindingWorker.OnSourcePropertyChanged(对象 o,字符串 propName) 在 MS.Internal.Data.PropertyPathWorker.System.Windows.IWeakEventListener.ReceiveWeakEvent(类型 managerType,对象发送者,EventArgs e) 在 System.Windows.WeakEventManager.DeliverEventToList(对象发送者,EventArgs 参数,ListenerList 列表) 在 System.ComponentModel.PropertyChangedEventManager.OnPropertyChanged(对象发送者,PropertyChangedEventArgs 参数) 在 c:\***\ViewModelBase.cs:line 17 中的 ***.ViewModelBase.OnPropertyChanged(String name) ......

编辑: 我们还尝试简单地抑制在 ViewModel 基类的 PropertyChanged 事件中抛出的任何 InvalidOperationExceptions。这似乎在一定程度上减少了异常的数量,但现在我们只是在ObservableCollectionCollectionChanged 事件中遇到了它们。

【问题讨论】:

  • 您是否尝试过使用 Dispatcher.Invoke 以不同的优先级提高代码中的 OnPropertyChanged - 如果可能的话?这有时是我用来影响的东西——我有点随机地同意 :-) WPF 在竞争条件下......
  • 一个疯狂的猜测,但您是否可以在实例化字典中冻结所有引用的画笔资源?您可以在此处阅读如何操作:stackoverflow.com/questions/799890/…
  • 另外,你确定你只是从 UI 线程修改 ObservableCollection 吗?

标签: .net wpf .net-4.0 freezable


【解决方案1】:

要解决此 .net 错误,请将代码中的所有纯色画笔更改为可冻结。例如

<SolidColorBrush x:Key="WindowBackground" Color="Black" />

应该改为:

<SolidColorBrush po:Freeze="True" x:Key="WindowBackground" Color="Black" />. 

有关更详细的说明,请参阅此处:How can WPF objects deriving from Freezable be frozen in XAML?

【讨论】:

  • 这似乎已经解决了!我想我在加载字典时手动冻结并没有完全工作。
  • 太棒了!多么简单,完全帮助了我。
  • @tofutim :如果您尝试批量应用此答案,则应在 xaml 文件的 xmlns 元素中将 po 命名空间简写定义为 schemas.microsoft.com/winfx/2006/xaml/presentation/options。给出了一个更详细的示例,其中在答案中包含链接“详细说明”的此 xmlns 元素。
  • 什么是po????
【解决方案2】:

我认为没有解决方法。根据我所读到的,在不得不自己处理这个问题时,WPF 会在创建时自动冻结资源。因此,无论何时您尝试在该资源上使用 DynamicResource,您都会得到可冻结的异常。

以下是微软基金会团队关于正在发生的事情的引述:

"WPF 将冻结样式或模板中的所有可冻结对象。样式和 模板可以在多个线程上使用,freezables 不能,除非 他们被冻结了。我们目前正在考虑将其扩展到任何东西 放入Application.Resources,因为它具有相同的线程 问题... 冻结可冻结的 DynamicResource 不起作用,因为 一个冷冻冷冻可能有多个父母——所以它是 不明确我们将搜索资源的哪个父级。”

【讨论】:

  • DynamicResources 在冻结Freezables 上的引用确实有效 - 应用程序在大部分时间启动并运行良好。我引用的资源只是内部没有任何动态或静态资源查找的画笔之类的东西。它们看起来都像这样:&lt;SolidColorBrush x:Key="WindowForeground" Color="White" /&gt; 另外,据我所知,WPF 似乎不会在加载字典时立即自动冻结内容。这就是我手动冻结它们的原因。
【解决方案3】:

每次涉及到带有 ItemsControl 和派生控件的 MVVM 的错误行为时,我的第一次尝试是禁用 VirtualizingStackPanel。

<ItemsControl VirtualizingStackPanel.IsVirtualizing="False" />

试一试……

【讨论】:

  • 我试了一下,并在应用程序中的每个 ComboBox/ItemsControl/ListBox 上进行了设置。好像没什么区别。
猜你喜欢
  • 1970-01-01
  • 2012-01-04
  • 1970-01-01
  • 2012-01-11
  • 2011-01-19
  • 2014-03-22
  • 2012-01-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多