31 votes

Solution de contournement pour WPF Freezable bug?

J'ai été frapper un très mauvais WPF bug récemment. Je pense que c'est la même chose que ce bug sur Microsoft Connect.

Notre application cible .NET 4.0 Client Profile à l'aide de Visual Studio 2010.

En gros, quand un ViewModel déclenche un changement de propriété de tout ou d'une collection qui provoque les éléments à déplacer dans un ItemsControl, il y a une chance que l'exception ci-dessous seront jetés. Il n'est pas toujours le cas et semble arriver basé sur des déclencheurs différents moments différents. Il semble plus probable que peu de temps après le démarrage de l'application. Si vous pouvez l'utiliser pour un couple de minutes sans toucher l'exception, vous aurez probablement jamais touché au cours de cette instance de l'application.

Comme le Connect rapport de bug, je suis à l'aide d' {DynamicResource key} à la charge de l' SolidColorBrushes à partir d'un ResourceDictionary. Certains dictionnaires sont chargé manuellement (pour la thématisation de soutien). J'ai essayé manuellement bloquer tout dans ces dictionnaires, mais il ne semble pas avoir beaucoup aidé.

Les exceptions sont devenues beaucoup plus fréquentes ces derniers temps quand j'ai ajouté un couple de plus UserControls à la fenêtre principale qui ont ItemsControls en eux lié à l' ObservableCollections. Auparavant, j'avais seulement voir l'exception, 1 fois sur 50, mais maintenant je le vois 4 fois sur 5 je utiliser le programme.

Quelqu'un aurait-il des idées pour des solutions de contournement? La connexion bug indique que ce sera probablement corrigé dans la prochaine .NET version (à chaque fois que c'est) mais ce bug est de faire de notre application en fait inutilisable maintenant.

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: Nous avons également essayé de simplement supprimer tout InvalidOperationExceptions jetées à l'intérieur de notre ViewModel de la classe de base de l'événement PropertyChanged. Qui semblait réduire le nombre d'exceptions un peu, mais maintenant nous frapper juste en ObservableCollections' CollectionChanged événement.

21voto

Sindhudweep Points 454

Pour contourner ce bogue .net, modifiez tous les pinceaux de couleur unie de votre code pour qu'ils puissent être congelés. Par exemple

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

devrait être remplacé par:

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

Pour des instructions plus détaillées, voir ici: Comment les objets WPF dérivés de Freezable peuvent-ils être gelés en XAML? .

3voto

Josh Points 2857

Je ne crois pas qu'il existe une solution de contournement pour cette. Dans avoir à faire avec moi-même, de ce que j'ai lu, WPF auto-gèle des ressources à la création. Donc, à chaque fois que vous essayez d'utiliser un DynamicResource sur la ressource que vous obtiendrez la freezable exception.

Voici une citation de Microsoft l'Équipe de la Fondation sur ce qui se passe:

"WPF va geler toute freezables à l'intérieur d'un style ou d'un modèle. De Styles et de les modèles peuvent être utilisés sur plusieurs threads, et freezables peut pas, à moins que ils sont congelés. Nous sommes actuellement à envisager d'étendre cela à rien de mettre à l'intérieur de l'Application.Ressources, parce que cela a le même problème de thread... DynamicResource sur la glace gelé ne fonctionne pas, parce qu'un gelé gelé a peut-être plusieurs parents, il est donc ambigu qui parent nous rechercher la ressource."

0voto

Dirk Points 2950

Chaque fois qu'il s'agit de comportement bogué autour de MVVM avec ItemsControl et des contrôles dérivés, mon premier essai est de désactiver VirtualizingStackPanel.

 <ItemsControl VirtualizingStackPanel.IsVirtualizing="False" />
 

Juste un essai ...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X