Обходной путь для ошибки WPF Freezable? - PullRequest
28 голосов
/ 22 августа 2011

Недавно я обнаружил очень серьезную ошибку WPF. Я думаю, что это то же самое, что и эта ошибка в Microsoft Connect.

Наше приложение предназначено для .NET 4.0 Client Profile с использованием Visual Studio 2010.

По сути, когда ViewModel инициирует изменение какого-либо свойства или коллекции, которое вызывает перемещение элементов в ItemsControl, есть вероятность, что будет выдано исключение ниже. Это не всегда происходит и, кажется, происходит на основе разных триггеров в разное время. Это кажется более вероятным вскоре после запуска приложения. Если вы можете использовать его в течение пары минут без исключения, вы, вероятно, никогда не нажмете во время этого экземпляра приложения.

Как и отчет об ошибке подключения, я использую {DynamicResource key} для загрузки SolidColorBrush es из ResourceDictionary. Некоторые из словарей загружаются вручную (для поддержки тем). Я пытался вручную заморозить все в этих словарях, но, похоже, это не помогло.

В последнее время исключения стали намного чаще, когда я добавил еще пару UserControl в главное окно, в котором ItemsControls были привязаны к ObservableCollection s. Раньше я видел исключение только 1 раз из 50, но теперь я вижу его 4 из 5 раз, когда использую программу.

У кого-нибудь есть идеи для обхода? Ошибка Connect указывает на то, что это, вероятно, будет исправлено в следующем выпуске .NET (когда бы то ни было), но эта ошибка делает наше приложение практически непригодным для использования.

    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: Мы также попытались просто подавить любые InvalidOperationException, выброшенные внутри события PropertyChanged базового класса ViewModel. Казалось, что это несколько уменьшило количество исключений, но теперь мы просто ударили их по событию ObservableCollection CollectionChanged.

Ответы [ 3 ]

21 голосов
/ 06 сентября 2011

Чтобы обойти эту ошибку .net, замените все однотонные кисти в вашем коде, чтобы они могли быть зафиксированы.Например,

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

следует изменить на:

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

Более подробные инструкции см. Здесь: Как объекты WPF, полученные из Freezable, могут быть заморожены в XAML? .

3 голосов
/ 22 августа 2011

Я не верю, что есть обходной путь для этого.Приходясь иметь дело с этим самостоятельно, из того, что я прочитал, WPF автоматически замораживает ресурсы при создании.Поэтому каждый раз, когда вы пытаетесь использовать DynamicResource на этом ресурсе, вы получаете замораживаемое исключение.

Вот цитата из Microsoft Foundation Team о том, что происходит:

"WPF будетзаморозить любые freezables внутри стиля или шаблона. Стили и шаблоны могут использоваться в нескольких потоках, а freezables не могут использоваться, если они не заморожены. В настоящее время мы рассматриваем возможность расширения этого на что-нибудь, помещенное в Application.Resources, так как оно имеет то же самоепроблема с многопоточностью ... DynamicResource для замороженной заморозки не работает, потому что у замороженной заморозки потенциально есть несколько родителей - поэтому не ясно, какой родитель мы будем искать для ресурса. "

0 голосов
/ 22 августа 2011

Каждый раз, когда речь заходит о некорректном поведении MVVM с ItemsControl и производными элементами управления, моя первая попытка - отключить VirtualizingStackPanel.

<ItemsControl VirtualizingStackPanel.IsVirtualizing="False" />

Просто попробуй ...

...