2011-08-22 7 views
27

J'ai récemment frappé un bug WPF vraiment mauvais. Je pense que c'est la même chose que this bug sur Microsoft Connect.Solution de contournement pour bug WPF Freezable?

Nos cibles d'application .NET 4.0 Profil client à l'aide de Visual Studio 2010.

En fait, lorsqu'un ViewModel déclenche un changement d'une propriété ou d'une collection qui cause des éléments à se déplacer dans un ItemsControl, il y a une chance que l'exception ci-dessous sera jeté. Cela n'arrive pas toujours et semble se produire en fonction de différents déclencheurs différents temps. Il semble être plus probable peu après le démarrage de l'application. Si vous pouvez l'utiliser pendant quelques minutes sans toucher à l'exception, vous ne frapperez probablement jamais pendant cette instance d'application. Comme le rapport de bogue Connect, j'utilise {DynamicResource key} pour charger le SolidColorBrush es à partir d'un ResourceDictionary. Certains dictionnaires sont chargés manuellement (pour le support thématique). J'ai essayé de tout geler manuellement dans ces dictionnaires, mais cela ne semble pas avoir aidé.

Les exceptions sont devenues beaucoup plus fréquentes ces derniers temps quand j'ai ajouté un couple plus UserControl s à la fenêtre principale qui a ItemsControls dans eux lié à ObservableCollection s. Auparavant, je ne voyais que l'exception 1 fois sur 50, mais maintenant je la vois 4 fois sur 5 j'utilise le programme.

Est-ce que quelqu'un a des idées pour des solutions de contournement? Le bogue de Connect indique que cela sera probablement corrigé dans la prochaine version de .NET (quand c'est le cas) mais ce bogue rend notre application fondamentalement 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 supprimer simplement les InvalidOperationException s jetés à l'intérieur de notre base ViewModel événement PropertyChanged de classe. Cela semblait réduire un peu le nombre d'exceptions, mais maintenant nous les frappons juste dans CollectionChanged événement.

+0

Avez-vous essayé d'augmenter OnPropertyChanged dans votre code avec une priorité différente en utilisant Dispatcher.Invoke - si possible? C'est parfois quelque chose que j'utilise pour influencer - un peu au hasard je suis d'accord :-) WPF sur les conditions de course ... –

+2

Une conjecture sauvage, mais pourriez-vous geler toutes les ressources de brosse référencées dans le dictionnaire lors de leur instanciation? Vous pouvez lire comment le faire ici: http://stackoverflow.com/questions/799890/how-can-wpf-objects-deriving-from-freezable-be-frozen-in-xaml –

+0

Aussi, avez-vous fait en sorte que vous modifiez uniquement ObservableCollection à partir du thread d'interface utilisateur? –

Répondre

20

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

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

devrait être remplacé:

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

Pour plus d'instructions détaillées, voir ici: How can WPF objects deriving from Freezable be frozen in XAML?.

+0

Cela semble l'avoir réparé! Je suppose que mon gel manuel lors du chargement des dictionnaires ne fonctionnait pas complètement. –

+0

génial! comment simple, m'a totalement aidé. –

+2

je me demande ce qui est "po:"? – tofutim

0

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

<ItemsControl VirtualizingStackPanel.IsVirtualizing="False" /> 

Juste essayer ...

+1

Je l'ai essayé et réglé ceci sur chaque ComboBox/ItemsControl/ListBox dans l'application. Cela ne semble pas faire la différence. –

3

Je ne crois pas qu'il y ait un travail autour pour cela. En ayant à gérer cela moi-même, d'après ce que j'ai lu, WPF auto-gèle les ressources à la création. Donc, chaque fois que vous essayez d'utiliser un DynamicResource sur cette ressource, vous obtiendrez l'exception freezable.

Voici une citation de l'équipe Microsoft Foundation sur ce qui se passe.

« WPF gèle tout ce freezables l'intérieur d'un style ou modèle Styles et modèles peuvent être utilisés sur plusieurs threads, et freezables peut » à moins que ils sont gelés. Nous envisageons actuellement de l'étendre à n'importe quoi mettre dans Application.Resources, car cela a le même problème de threading ... DynamicResource sur un freez gelé ne fonctionne pas, parce qu'un congélateur congelé potentiellement a plusieurs parents - il est donc ambigu parent nous chercherions le r source. "

+0

'DynamicResource's cette référence sur congelée' Freezable's fonctionnent - l'application se lance et fonctionne très bien la plupart du temps. Les ressources que je référence sont seulement des choses comme des pinceaux qui n'ont pas de recherche de ressources dynamiques ou statiques à l'intérieur d'eux. Ils ressemblent tous à ceci: '' Aussi, d'après ce que je peux dire, WPF ne semble pas geler automatiquement les choses immédiatement lorsque le dictionnaire est chargé. C'est pourquoi je les gèle manuellement. –

Questions connexes