Workaround for WPF Freezable bug?
Asked Answered
D

3

31

I've been hitting a really bad WPF bug recently. I think it's the same as this bug on Microsoft Connect.

Our application targets .NET 4.0 Client Profile using Visual Studio 2010.

Basically, when a ViewModel triggers a change to any property or collection that causes items to move around in an ItemsControl, there's a chance that the exception below will be thrown. It doesn't always happen and seems to happen based on different triggers different times. It seems to be more likely shortly after starting the application. If you can use it for a couple minutes without hitting the exception, you'll probably never hit during that application instance.

Like the Connect bug report, I'm using {DynamicResource key} to load the SolidColorBrushes from a ResourceDictionary. Some of the dictionaries are manually loaded (for theming support). I tried manually freeze everything in those dictionaries, but it doesn't seem to have helped.

The exceptions have gotten much more frequent lately when I added a couple more UserControls to the main window that have ItemsControls in them bound to ObservableCollections. Previously, I'd only see the exception 1 time out of 50, but now I see it 4 out of 5 times I use the program.

Does anyone have any ideas for workarounds? The Connect bug indicates that this will probably be fixed in the next .NET release (whenever that is) but this bug is making our application basically unusable now.

    System.InvalidOperationException: Specified value of type 'System.Windows.Media.SolidColorBrush' must have IsFrozen set to false to modify.
       at System.Windows.Freezable.WritePreamble()
       at System.Windows.Freezable.remove_Changed(EventHandler value)
       at System.Windows.ResourceReferenceExpression.ResourceReferenceExpressionWeakContainer.RemoveChangedHandler()
       at System.Windows.ResourceReferenceExpression.ResourceReferenceExpressionWeakContainer.InvalidateTargetSubProperty(Object sender, EventArgs args)
       at System.Windows.Freezable.FireChanged()
       at System.Windows.Freezable.Freeze(Boolean isChecking)
       at System.Windows.Freezable.Freeze()
       at System.Windows.Freezable.System.Windows.ISealable.Seal()
       at System.Windows.StyleHelper.SealIfSealable(Object value)
       at System.Windows.StyleHelper.GetChildValueHelper(UncommonField`1 dataField, ItemStructList`1& valueLookupList, DependencyProperty dp, DependencyObject container, FrameworkObject child, Int32 childIndex, Boolean styleLookup, EffectiveValueEntry& entry, ValueLookupType& sourceType, FrameworkElementFactory templateRoot)
       at System.Windows.StyleHelper.GetChildValue(UncommonField`1 dataField, DependencyObject container, Int32 childIndex, FrameworkObject child, DependencyProperty dp, FrugalStructList`1& childRecordFromChildIndex, EffectiveValueEntry& entry, ValueLookupType& sourceType, FrameworkElementFactory templateRoot)
       at System.Windows.StyleHelper.GetValueFromTemplatedParent(DependencyObject container, Int32 childIndex, FrameworkObject child, DependencyProperty dp, FrugalStructList`1& childRecordFromChildIndex, FrameworkElementFactory templateRoot, EffectiveValueEntry& entry)
       at System.Windows.FrameworkElement.GetValueFromTemplatedParent(DependencyProperty dp, EffectiveValueEntry& entry)
       at System.Windows.FrameworkElement.GetRawValue(DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry& entry)
       at System.Windows.FrameworkElement.EvaluateBaseValueCore(DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry& newEntry)
       at System.Windows.DependencyObject.EvaluateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry newEntry, OperationType operationType)
       at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
       at System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp)
       at System.Windows.StyleHelper.InvalidateResourceDependentsForChild(DependencyObject container, DependencyObject child, Int32 childIndex, ResourcesChangeInfo info, FrameworkTemplate parentTemplate)
       at System.Windows.TreeWalkHelper.InvalidateStyleAndReferences(DependencyObject d, ResourcesChangeInfo info, Boolean containsTypeOfKey)
       at System.Windows.TreeWalkHelper.OnResourcesChanged(DependencyObject d, ResourcesChangeInfo info, Boolean raiseResourceChangedEvent)
       at System.Windows.FrameworkElement.OnAncestorChangedInternal(TreeChangeInfo parentTreeState)
       at System.Windows.TreeWalkHelper.OnAncestorChanged(DependencyObject d, TreeChangeInfo info)
       at System.Windows.DescendentsWalker`1._VisitNode(DependencyObject d)
       at MS.Internal.PrePostDescendentsWalker`1._VisitNode(DependencyObject d)
       at System.Windows.DescendentsWalker`1.VisitNode(FrameworkElement fe)
       at System.Windows.DescendentsWalker`1.VisitNode(DependencyObject d)
       at System.Windows.DescendentsWalker`1.WalkFrameworkElementLogicalThenVisualChildren(FrameworkElement feParent, Boolean hasLogicalChildren)
       at System.Windows.DescendentsWalker`1.IterateChildren(DependencyObject d)
       at System.Windows.DescendentsWalker`1.StartWalk(DependencyObject startNode, Boolean skipStartNode)
       at MS.Internal.PrePostDescendentsWalker`1.StartWalk(DependencyObject startNode, Boolean skipStartNode)
       at System.Windows.TreeWalkHelper.InvalidateOnTreeChange(FrameworkElement fe, FrameworkContentElement fce, DependencyObject parent, Boolean isAddOperation)
       at System.Windows.FrameworkElement.OnVisualParentChanged(DependencyObject oldParent)
       at System.Windows.Media.Visual.FireOnVisualParentChanged(DependencyObject oldParent)
       at System.Windows.Media.Visual.RemoveVisualChild(Visual child)
       at System.Windows.Media.VisualCollection.DisconnectChild(Int32 index)
       at System.Windows.Media.VisualCollection.Clear()
       at System.Windows.Controls.UIElementCollection.ClearInternal()
       at System.Windows.Controls.Panel.ClearChildren()
       at System.Windows.Controls.Panel.OnItemsChangedInternal(Object sender, ItemsChangedEventArgs args)
       at System.Windows.Controls.Panel.OnItemsChanged(Object sender, ItemsChangedEventArgs args)
       at System.Windows.Controls.ItemContainerGenerator.OnRefresh()
       at System.Windows.Controls.ItemContainerGenerator.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
       at System.Windows.Controls.ItemContainerGenerator.System.Windows.IWeakEventListener.ReceiveWeakEvent(Type managerType, Object sender, EventArgs e)
       at System.Windows.WeakEventManager.DeliverEventToList(Object sender, EventArgs args, ListenerList list)
       at System.Windows.WeakEventManager.DeliverEvent(Object sender, EventArgs args)
       at System.Collections.Specialized.CollectionChangedEventManager.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
       at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
       at System.Windows.Data.CollectionView.OnCollectionChanged(NotifyCollectionChangedEventArgs args)
       at System.Windows.Controls.ItemCollection.System.Windows.IWeakEventListener.ReceiveWeakEvent(Type managerType, Object sender, EventArgs e)
       at System.Windows.WeakEventManager.DeliverEventToList(Object sender, EventArgs args, ListenerList list)
       at System.Windows.WeakEventManager.DeliverEvent(Object sender, EventArgs args)
       at System.Collections.Specialized.CollectionChangedEventManager.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
       at System.Windows.Data.CollectionView.OnCollectionChanged(NotifyCollectionChangedEventArgs args)
       at System.Windows.Data.ListCollectionView.RefreshOverride()
       at System.Windows.Data.CollectionView.Refresh()
       at System.Windows.Data.CollectionView.EndDefer()
       at System.Windows.Data.CollectionView.DeferHelper.Dispose()
       at System.Windows.Controls.ItemCollection.SetCollectionView(CollectionView view)
       at System.Windows.Controls.ItemCollection.SetItemsSource(IEnumerable value)
       at System.Windows.Controls.ItemsControl.OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
       at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
       at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
       at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
       at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
       at System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp)
       at System.Windows.Data.BindingExpressionBase.Invalidate(Boolean isASubPropertyChange)
       at System.Windows.Data.BindingExpression.TransferValue(Object newValue, Boolean isASubPropertyChange)
       at System.Windows.Data.BindingExpression.ScheduleTransfer(Boolean isASubPropertyChange)
       at MS.Internal.Data.ClrBindingWorker.NewValueAvailable(Boolean dependencySourcesChanged, Boolean initialValue, Boolean isASubPropertyChange)
       at MS.Internal.Data.PropertyPathWorker.UpdateSourceValueState(Int32 k, ICollectionView collectionView, Object newValue, Boolean isASubPropertyChange)
       at MS.Internal.Data.ClrBindingWorker.OnSourcePropertyChanged(Object o, String propName)
       at MS.Internal.Data.PropertyPathWorker.System.Windows.IWeakEventListener.ReceiveWeakEvent(Type managerType, Object sender, EventArgs e)
       at System.Windows.WeakEventManager.DeliverEventToList(Object sender, EventArgs args, ListenerList list)
       at System.ComponentModel.PropertyChangedEventManager.OnPropertyChanged(Object sender, PropertyChangedEventArgs args)
       at ***.ViewModelBase.OnPropertyChanged(String name) in c:\***\ViewModelBase.cs:line 17
    .....

EDIT: We've also tried simply suppressing any InvalidOperationExceptions thrown inside of our ViewModel base class's PropertyChanged event. That seemed to reduce the number of exceptions somewhat, but now we just hit them in ObservableCollection's CollectionChanged event.

Deflexed answered 22/8, 2011 at 14:11 Comment(3)
Have you tried to raise the OnPropertyChanged in your code with a different priority using Dispatcher.Invoke - if even possible? It's sometimes something I use to influence - a bit randomly I agree :-) WPF on race conditions...Redaredact
A wild guess, but could you perhaps freeze all of the referenced brush resources in the dictionary upon their instantiation? You can read how to do it here: #800390Homestretch
Also, have you made sure that you're only ever modifying the ObservableCollection from the UI thread?Homestretch
A
21

To workaround this .net bug, change all of the Solid Color Brushes in your code to be freezeable. For example

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

should be changed to:

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

For more detailed instructions see here: How can WPF objects deriving from Freezable be frozen in XAML?.

Alessandro answered 6/9, 2011 at 18:23 Comment(4)
This seems to have fixed it! I guess that my manual freezing when loading the dictionaries wasn't working completely.Deflexed
awesome! how simple, totally helped me out.Fiddlestick
@tofutim : The po namespace shorthand should be defined in an xmlns element in your xaml file to be schemas.microsoft.com/winfx/2006/xaml/presentation/options if you're trying to apply this answer wholesale. A more detailed example is given which includes this xmlns element linked "detailed instructions" in the answer.Alessandro
What is po????Licit
W
3

I don't believe there is a work-around for this. In having to deal with this myself, from what I've read, WPF auto-freezes resources at creation. So anytime you try to use a DynamicResource on that resource you will get the freezable exception.

Here is a quote from the Microsoft Foundation Team on what is happening:

"WPF will freeze any freezables inside a style or template. Styles and templates can be used on multiple threads, and freezables can't unless they're frozen. We're currently considering extending this to anything put inside Application.Resources, since that has the same threading problem... DynamicResource on a frozen freezable doesn't work, because a frozen freezable potentially has multiple parents -- so it's ambiguous which parent we would search for the resource."

Wouldbe answered 22/8, 2011 at 15:27 Comment(1)
DynamicResources that reference on frozen Freezables do work- the application launches and works fine much of the time. The resources that I'm referencing are only things like brushes that don't have any dynamic or static resource lookups inside of them. They all look like this: <SolidColorBrush x:Key="WindowForeground" Color="White" /> Also, from what I can tell, WPF doesn't seem to automatically freeze things immediately when the dictionary is loaded. That's why I manually freeze them.Deflexed
L
0

Every time it comes to buggy behaviour round about MVVM with ItemsControl and derived controls my first try is to disable VirtualizingStackPanel.

<ItemsControl VirtualizingStackPanel.IsVirtualizing="False" />

Just a try...

Lollis answered 22/8, 2011 at 14:34 Comment(1)
I gave this a try and set this on every ComboBox/ItemsControl/ListBox in the application. It doesn't seem to make a difference.Deflexed

© 2022 - 2024 — McMap. All rights reserved.