wpf - 如何在不同的线程上使用静态资源?
问题描述
我有一个 WPF 应用程序,在 UI 线程上有一个长时间运行的任务,我想为其显示一个进度条 (*)。由于 UI 很忙,因此遵循此解决方案,我选择在单独的 UI/STA-Thread 上打开带有进度条的窗口。
一切正常 - 我第一次创建窗口。问题显然是窗口使用Style="{StaticResource CustomWindowStyle}"
并且样式的实例是静态的,即“缓存”并在使用此样式的所有实例之间共享。
但是,这个实例(作为所有/大多数 UI 元素)DispatcherObject
只能在最初创建它的线程中使用。因此,当我第二次打开一个窗口(在它自己的新 UI 线程上)时,它会访问Style
以前在不同线程上构建的相同静态资源,我得到以下异常:
System.Windows.Markup.XamlParseException
HResult=0x80131501
Message='Set property 'System.Windows.FrameworkElement.Style' threw an exception.' Line number '15' and line position '9'.
Source=PresentationFramework
StackTrace:
at System.Windows.Markup.XamlReader.RewrapException(Exception e, IXamlLineInfo lineInfo, Uri baseUri)
This exception was originally thrown at this call stack:
System.Windows.StyleHelper.ProcessInstanceValuesHelper(ref MS.Utility.ItemStructList<System.Windows.ChildValueLookup>, System.Windows.DependencyObject, int, System.Collections.Specialized.HybridDictionary, bool)
System.Windows.StyleHelper.ProcessInstanceValuesForChild(System.Windows.DependencyObject, System.Windows.DependencyObject, int, System.Collections.Specialized.HybridDictionary, bool, ref MS.Utility.FrugalStructList<System.Windows.ChildRecord>)
System.Windows.StyleHelper.DoStyleInvalidations(System.Windows.FrameworkElement, System.Windows.FrameworkContentElement, System.Windows.Style, System.Windows.Style)
System.Windows.StyleHelper.UpdateStyleCache(System.Windows.FrameworkElement, System.Windows.FrameworkContentElement, System.Windows.Style, System.Windows.Style, ref System.Windows.Style)
System.Windows.FrameworkElement.OnStyleChanged(System.Windows.DependencyObject, System.Windows.DependencyPropertyChangedEventArgs)
System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs)
System.Windows.FrameworkElement.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs)
System.Windows.DependencyObject.NotifyPropertyChange(System.Windows.DependencyPropertyChangedEventArgs)
System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex, System.Windows.DependencyProperty, System.Windows.PropertyMetadata, System.Windows.EffectiveValueEntry, ref System.Windows.EffectiveValueEntry, bool, bool, System.Windows.OperationType)
System.Windows.DependencyObject.SetValueCommon(System.Windows.DependencyProperty, object, System.Windows.PropertyMetadata, bool, bool, System.Windows.OperationType, bool)
...
[Call Stack Truncated]
Inner Exception 1:
InvalidOperationException: Cannot access Freezable 'System.Windows.Shell.WindowChrome' across threads because it cannot be frozen.
如果我删除该Style
属性,一切都很好。
我也尝试过使用DynamicResource
,但引用的样式引用了我无法控制的其他静态资源,并导致“更进一步”的相同问题。
这可以解决吗?
(*) 是的,我知道:长时间运行的操作不应该由 UI 线程处理,但是改变它需要太多的重构(目前),即使发生这种情况,用户也不能做任何事情,如果操作被外包给了一项任务,无论如何我实际上都会禁用用户界面。
编辑1:所以带我去这篇文章;我尝试添加x:Shared="False"
到窗口声明,但没有帮助。
编辑 2:我也尝试按照此处所述冻结样式资源,但没有帮助。
解决方案
如此处所述,解决方案是x:Shared="True"
为引用的样式而不是窗口设置。
幸运的是,就我而言,我确实可以控制这种样式定义(但不能控制该样式使用的其他资源),所以我可以修改它的定义。
由于我非常希望不必修改样式,因此我还尝试Window.Resources
像这样定义本地样式:
<Window.Resources>
<Style x:Key="CustomStyle" TargetType="{x:Type Window}" BasedOn="{StaticResource CustomWindowStyle}" x:Shared="False"/>
</Window.Resources>
然后我尝试改用它。首先,我必须通过代码分配样式,因为我不知道如何在窗口的属性中使用样式,该属性(稍后)被定义为窗口自己的资源之一:
this.Style = this.Resources["CustomStyle"] as Style;
但是,遗憾的是,这失败了,但出现了以下异常(就在那一行):
System.InvalidOperationException
HResult=0x80131509
Message=The calling thread cannot access this object because a different thread owns it.
Source=WindowsBase
StackTrace:
at System.Windows.Threading.Dispatcher.VerifyAccess()
at System.Windows.Freezable.System.Windows.ISealable.get_IsSealed()
at System.Windows.StyleHelper.SealIfSealable(Object value)
at System.Windows.Setter.Seal()
at System.Windows.SetterBaseCollection.Seal()
at System.Windows.Style.ProcessSetters(Style style)
at System.Windows.Style.ProcessSetters(Style style)
at System.Windows.Style.Seal()
at System.Windows.StyleHelper.UpdateStyleCache(FrameworkElement fe, FrameworkContentElement fce, Style oldStyle, Style newStyle, Style& styleCache)
at System.Windows.FrameworkElement.OnStyleChanged(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.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
at ...PopupWindow..ctor()...
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
This exception was originally thrown at this call stack:
System.Windows.Threading.Dispatcher.VerifyAccess()
System.Windows.Freezable.System.Windows.ISealable.IsSealed.get()
System.Windows.StyleHelper.SealIfSealable(object)
System.Windows.Setter.Seal()
System.Windows.SetterBaseCollection.Seal()
System.Windows.Style.ProcessSetters(System.Windows.Style)
System.Windows.Style.ProcessSetters(System.Windows.Style)
System.Windows.Style.Seal()
System.Windows.StyleHelper.UpdateStyleCache(System.Windows.FrameworkElement, System.Windows.FrameworkContentElement, System.Windows.Style, System.Windows.Style, ref System.Windows.Style)
System.Windows.FrameworkElement.OnStyleChanged(System.Windows.DependencyObject, System.Windows.DependencyPropertyChangedEventArgs)
...
[Call Stack Truncated]
所以,现在,我正在使用修改后的静态样式:
<Style x:Key="CustomWindowStyle" TargetType="{x:Type Window}" x:Shared="False">
...
</Style>
推荐阅读
- linux - Mininet Ping 问题
- c# - SQL 超时执行错误 [我在高容量请求中遇到超时执行错误]
- xml - Xpath 获取位于具有特定属性的两个元素之间的元素
- jquery - 使用 lodash 通过 ajax 调用调用函数的节流
- ios - 使用 YTPlayerView 无法在 iOS 中播放 Youtube 视频
- nuxeo - 为什么 nuxeo 要求注册实例?
- scala - 过滤案例类对象属性的最佳实践
- java - 尝试编辑数据集时 eclipse 冻结并且没有响应
- c# - 数组 c# 中的子字符串 (Unity)
- nginx - proxy_pass 以 nginx 变量作为参数