0

I have a WPF application with a long-running task on the UI thread for which I would like to show a progress bar (*). Since the UI is busy, following this solution, I chose to open the window with the progress bars on a separate UI/STA-Thread.

Everything works fine - the first time I create the window. The problem is apparently that the window uses Style="{StaticResource CustomWindowStyle}" and the instance of the style is static, i.e. "cached" and shared between all instances using this style.

But, this instance is (as all/most UI elements) a DispatcherObject that may only be used from the thread it was initially created on. So, when I open a window a second time (on it's own, new UI thread), it accesses the same static Style resource that was previously built on a different thread and I get the following exception:

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.

If I remove the Style attribute, everything is fine.

I also tried using DynamicResource, but the referenced style references other static resources that I have no control over and cause the same problem "further down the line".

Can this be resolved?

(*) Yes, I know: long-running operations should not be processed by the UI thread, but changing that would require too much refactoring (for right now) and the user is not to do anything while this happens so even, if the operation was outsourced to a task, I would practically disable the UI anyway.

EDIT 1: SO lead me to this post; I tried adding x:Shared="False" to the window declaration, but it didn't help.

EDIT 2: I also tried freezing the style resource as described here, but it didn't help.

mike
  • 1,627
  • 1
  • 14
  • 37

1 Answers1

0

As described here, the solution is to set x:Shared="True" for the referenced style - and not the window.

Luckily, in my case, I do indeed have control over this style definition (but not over other resources used by that style), so I can modify it's definition.

Since I would greatly prefer not having to modify the style, I also tried defining a local style in Window.Resources like this:

<Window.Resources>
    <Style x:Key="CustomStyle" TargetType="{x:Type Window}" BasedOn="{StaticResource CustomWindowStyle}" x:Shared="False"/>
</Window.Resources>

And then I tried to use that instead. Firstly, I had to assign the style via code, because I couldn't figure out how to use a style in a window's attribute that is defined (later) as one of the window's own resources:

this.Style = this.Resources["CustomStyle"] as Style;

But, sadly, that failed with the following exception (on just that line):

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]
    

So, for now, I'm using the modified static style:

<Style x:Key="CustomWindowStyle" TargetType="{x:Type Window}" x:Shared="False">
    ...
</Style>
mike
  • 1,627
  • 1
  • 14
  • 37
  • If you have a long-running task on the UI thread, isn't it a lot better to make that task asynchronous and have it work on a background thread? Seems lot simpler than creating multiple UI threads. – Joe Aug 09 '21 at 01:54