2011-08-22 22 views
27

Ostatnio trafiłem na naprawdę zły błąd WPF. Wydaje mi się, że jest taki sam jak this bug na Microsoft Connect.Obejście problemu z błędem WPF Freezable?

Nasze cele aplikacji .NET 4.0 Client Profile użyciu programu Visual Studio 2010.

Zasadniczo, gdy ViewModel wyzwala zmianę jakiegokolwiek majątku lub kolekcji, która powoduje elementy do poruszania się w ItemsControl, istnieje szansa, że ​​wyjątek poniżej zostanie rzucony. Nie zawsze się to zdarza i zdaje się, że dzieje się w oparciu o różne wyzwalacze w różnym czasie. Wydaje się, że jest to bardziej prawdopodobne wkrótce po uruchomieniu aplikacji. Jeśli możesz go używać przez kilka minut bez uderzania w wyjątek, prawdopodobnie nigdy nie trafisz w to wystąpienie aplikacji.

Podobnie jak raport o błędzie Connect, używam {DynamicResource key} do załadowania SolidColorBrush es z ResourceDictionary. Niektóre słowniki są ładowane ręcznie (do obsługi motywów). Próbowałem ręcznie zamrozić wszystko w tych słownikach, ale wydaje się, że nie pomogło.

Wyjątki ostatnio pojawiły się znacznie częściej, gdy dodałem kilka kolejnych UserControl s do głównego okna, w którym ItemsControls w nich są powiązane z ObservableCollection s. Wcześniej widziałem tylko wyjątek 1 raz na 50, ale teraz widzę go 4 razy z 5 razy używam programu.

Czy ktoś ma jakieś pomysły na obejście tego problemu? Błąd Connect wskazuje, że prawdopodobnie zostanie to naprawione w następnym wydaniu .NET (kiedykolwiek tak jest), ale ten błąd sprawia, że ​​nasza aplikacja jest teraz w zasadzie bezużyteczna.

 
    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: Mamy też próbował po prostu tłumiąc wszelkie InvalidOperationException s rzucony wewnątrz imprezy naszego ViewModel klasy bazowej za PropertyChanged. Wydaje się, że w pewnym stopniu zmniejszyło to liczbę wyjątków, ale teraz po prostu trafiliśmy na nie w wydarzeniu 'CollectionChanged.

+0

Czy próbowałeś podnieść OnPropertyChanged w kodzie o innym priorytecie za pomocą Dispatcher.Invoke - jeśli to możliwe? Czasem zdarza się coś, na co wpływam - nieco losowo, zgadzam się :-) WPF na warunkach wyścigu ... –

+2

Dziwne przypuszczenie, ale czy możliwe jest, że zablokuje wszystkie odnośne zasoby pędzla w słowniku po ich utworzeniu? Możesz przeczytać, jak to zrobić tutaj: http://stackoverflow.com/questions/799890/how-can-wpf-objects-deriving-from-freezable-be-frozen-in-xaml –

+0

Czy upewniłeś się, że modyfikujesz ObservableCollection tylko z wątku interfejsu użytkownika? –

Odpowiedz

20

Aby obejść ten błąd .net, zmień wszystkie pędzle Solid Color w kodzie, aby umożliwić ich zamrożenie.Na przykład

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

powinny zostać zmienione na:

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

Bardziej szczegółowe instrukcje można znaleźć tutaj: How can WPF objects deriving from Freezable be frozen in XAML?.

+0

Wydaje się, że to naprawiło! Przypuszczam, że moje ręczne zamrażanie podczas ładowania słowników nie działało całkowicie. –

+0

niesamowite! jak proste, całkowicie mi pomogło. –

+2

Zastanawiam się, co to jest "po:"? – tofutim

0

Za każdym razem, gdy chodzi o błędne zachowanie wokół MVVM z ItemsControl i pochodnymi kontrolkami, moją pierwszą próbą jest wyłączenie VirtualizingStackPanel.

<ItemsControl VirtualizingStackPanel.IsVirtualizing="False" /> 

Wystarczy spróbować ...

+1

Dałem to spróbować i ustawić to na każdym ComboBox/ItemsControl/ListBox w aplikacji. To nie robi różnicy. –

3

nie wierzę, istnieje obejście tego. Sam muszę sobie z tym poradzić, z tego co przeczytałem, WPF automatycznie zamraża zasoby podczas tworzenia. Tak więc za każdym razem, gdy spróbujesz użyć DynamicResource na tym zasobie, dostaniesz wyjątek freezable.

Oto cytat z Zespołu Microsoft Foundation, co się dzieje.

„WPF zamrozi żadnych freezables wewnątrz styl lub szablon Style i szablony mogą być wykorzystywane na wiele wątków, a freezables może” t chyba że są one zamrożone.Obecnie rozważamy rozszerzenie tego na wszystko umieścić wewnątrz Application.Resources, ponieważ ma to ten sam problem z gwintowaniem ... DynamicResource na zamrożonej freezable nie działa, ponieważ zamrożone freezable potencjalnie ma wielu rodziców - więc jest niejednoznaczne, który rodzic będzie szukał esource. "

+0

'' DynamicResource's to odwołanie do zamrożonych 'Freezable's działają - aplikacja uruchamia się i działa dobrze przez większość czasu. Zasoby, do których się odwołuję, to tylko rzeczy takie jak pędzle, które nie mają żadnych dynamicznych lub statycznych odnośników do zasobów wewnątrz nich. Wszystkie wyglądają tak: '' Ponadto, z tego, co mogę powiedzieć, WPF nie wydaje się automatycznie zamrażać rzeczy natychmiast po załadowaniu słownika. Dlatego ręcznie je zamrażam. –

Powiązane problemy