2011-08-22 17 views
27

Recentemente ho riscontrato un bug WPF davvero brutto. Penso che sia lo stesso di this bug su Microsoft Connect.Soluzione alternativa per bug WPF Freezable?

I nostri obiettivi di applicazioni .NET 4.0 Client Profile che utilizzano Visual Studio 2010.

In sostanza, quando un ViewModel innesca una modifica a qualsiasi proprietà o collezione che fa sì che gli elementi di muoversi in un ItemsControl, c'è una possibilità che l'eccezione di seguito sarà gettato. Non sempre accade e sembra accadere in base a trigger diversi tempi diversi. Sembra essere più probabile poco dopo l'avvio dell'applicazione. Se puoi usarlo per un paio di minuti senza colpire l'eccezione, probabilmente non colpirai mai durante quell'istanza dell'applicazione.

Come il bug report Connect, sto utilizzando {DynamicResource key} per caricare il SolidColorBrush da un ResourceDictionary. Alcuni dei dizionari sono caricati manualmente (per il supporto di tematizzazione). Ho provato a bloccare manualmente tutto in quei dizionari, ma non sembra aver aiutato.

Le eccezioni sono diventate molto più frequenti ultimamente quando ho aggiunto un altro paio UserControl s alla finestra principale che contiene ItemsControls associato a ObservableCollection s. In precedenza, vedevo solo l'eccezione 1 volta su 50, ma ora lo vedo 4 volte su 5 che uso il programma.

Qualcuno ha qualche idea per soluzioni alternative? Il bug di Connect indica che questo probabilmente verrà risolto nella successiva versione .NET (ogni volta che lo è) ma questo bug sta rendendo praticamente inutilizzabile la nostra applicazione ora.

 
    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: Abbiamo anche provato semplicemente sopprimere eventuali InvalidOperationException s gettati all'interno della manifestazione PropertyChanged della nostra classe base ViewModel. Sembrava in qualche modo ridurre il numero di eccezioni, ma ora li abbiamo appena raggiunti nell'evento di ObservableCollection.

+0

Hai provato a aumentare OnPropertyChanged nel tuo codice con una priorità diversa utilizzando Dispatcher.Invoke - se possibile? A volte è qualcosa che uso per influenzare - un po 'a caso sono d'accordo :-) WPF in condizioni di gara ... –

+2

Un'ipotesi selvaggia, ma potresti forse congelare tutte le risorse del pennello di riferimento nel dizionario dopo la loro istanziazione? Puoi leggere come farlo qui: http://stackoverflow.com/questions/799890/how-can-wpf-objects-deriving-from-freezable-be-frozen-in-xaml –

+0

Inoltre, hai fatto in modo che stai solo modificando ObservableCollection dal thread dell'interfaccia utente? –

risposta

20

Per ovviare a questo errore .net, è possibile modificare tutti i pennelli di colore solido nel codice in modo che siano congelati.Per esempio

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

dovrebbe essere cambiata a:

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

Per ulteriori istruzioni dettagliate, vedere qui: How can WPF objects deriving from Freezable be frozen in XAML?.

+0

Questo sembra averlo risolto! Immagino che il mio congelamento manuale durante il caricamento dei dizionari non funzionasse completamente. –

+0

fantastico! quanto semplice, mi ha completamente aiutato. –

+2

mi chiedo che cos'è "po:"? – tofutim

0

Ogni volta che si tratta di un comportamento buggy su MVVM con ItemsControl e controlli derivati, il mio primo tentativo è disabilitare VirtualizationStackPanel.

<ItemsControl VirtualizingStackPanel.IsVirtualizing="False" /> 

Basta una prova ...

+1

Ho dato questo una prova e impostarlo su ogni ComboBox/ItemsControl/ListBox nell'applicazione. Non sembra fare la differenza. –

3

Io non credo che ci sia un work-around per questo. Nel dover affrontare me stesso, da quello che ho letto, WPF auto-congela risorse alla creazione. Quindi, ogni volta che proverai a usare una risorsa dinamica su quella risorsa otterrai l'eccezione freezable.

Ecco una citazione dal team di Microsoft Foundation su quanto sta accadendo:.

"WPF si blocca eventuali freezables all'interno di uno stile o un modello Stili e modelli possono essere utilizzati su più thread, e freezables può' t a meno che non siano stati congelati .all'interno di Application.Resources, poiché ha lo stesso problema di threading ... DynamicResource su un freezable congelato non funziona, perché un congelato congelato potenzialmente ha più genitori - quindi è ambiguo quale genitore cercheremo la r eSource."

+0

'DynamicResource's che fa riferimento su congelato' Freezable's funzionano - l'applicazione si avvia e funziona bene la maggior parte del tempo. Le risorse a cui sto facendo riferimento sono solo cose come i pennelli che non hanno alcuna ricerca di risorse dinamiche o statiche all'interno di esse. Sembrano tutti così: '' Inoltre, da quello che posso dire, WPF non sembra bloccare automaticamente le cose immediatamente quando il dizionario viene caricato. Ecco perché li blocco manualmente. –

Problemi correlati