Обходной путь для WPF Freezable ошибка?
Недавно я столкнулся с очень плохой ошибкой WPF. Я думаю, что это так же, как эта ошибка в Microsoft Connect.
Наше приложение нацелено на профиль клиента .NET 4.0 с помощью Visual Studio 2010.
В принципе, когда ViewModel запускает изменение любого свойства или коллекции, которое заставляет элементы перемещаться в ItemsControl, есть вероятность, что будет выбрано исключение. Это не всегда происходит и, похоже, происходит в зависимости от разных триггеров в разное время. Скорее всего, вскоре после запуска приложения. Если вы можете использовать его в течение пары минут, не попав в исключение, вы, вероятно, никогда не будете бить во время этого экземпляра приложения.
Как и отчет об ошибке подключения, я использую {DynamicResource key}
для загрузки SolidColorBrush
es из ResourceDictionary
. Некоторые словари загружаются вручную (для поддержки поддержки). Я попытался вручную заморозить все в этих словарях, но, похоже, это не помогло.
В последнее время исключения стали гораздо более частыми, когда я добавил еще пару UserControl
в главное окно, в которых элементы ItemsControls привязаны к ObservableCollection
s. Раньше я видел только исключение 1 раз из 50, но теперь я вижу это 4 из 5 раз, я использую программу.
Есть ли у кого-нибудь идеи для обходных решений? Ошибка подключения указывает, что это, вероятно, будет исправлено в следующей версии .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
.
Ответы
Ответ 1
Чтобы обойти эту ошибку .net, измените все сплошные цветные кисти в вашем коде, чтобы их можно было заморозить. Например
<SolidColorBrush x:Key="WindowBackground" Color="Black" />
следует изменить на:
<SolidColorBrush po:Freeze="True" x:Key="WindowBackground" Color="Black" />.
Более подробные инструкции см. здесь: Как можно заморозить объекты WPF из Freezable в XAML?.
Ответ 2
Я не верю, что для этого есть обход. Имея дело с этим сам, из того, что я читал, WPF автоматически замораживает ресурсы при создании. Поэтому в любое время, когда вы пытаетесь использовать DynamicResource на этом ресурсе, вы получите исключение для замораживания.
Вот цитата из команды Microsoft Foundation о том, что происходит:
"WPF заморозит любые freezables внутри стиля или шаблона. Стили и
шаблоны могут использоваться на нескольких потоках, а freezables не может, кроме
они заморожены. В настоящее время мы планируем распространить это на любой
внутри Application.Resources, так как это имеет ту же проблему с потоками...
DynamicResource на замороженном замораживании не работает, поскольку
у замороженного замораживания потенциально есть несколько родителей - так что это двусмысленно,
родительский, мы будем искать ресурс ".
Ответ 3
Каждый раз, когда речь идет о неправильном поведении вокруг MVVM с помощью ItemsControl и производных элементов управления, первая попытка - отключить VirtualizingStackPanel.
<ItemsControl VirtualizingStackPanel.IsVirtualizing="False" />
Просто попробуйте...