3

I have the following style applied to treeviewitem container. Basically, used to override selection color:

<Style x:Key="element-designer-node-host" TargetType="{x:Type TreeViewItem}">
    <Style.Resources>
        <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" 
                         Color="{DynamicResource nav-tvi-sel-bg1}" />
        <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" 
                         Color="{DynamicResource nav-tvi-sel-bg1}" />
        <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" 
                         Color="{DynamicResource nav-tvi-sel-bg2}" />
    </Style.Resources>
    <Setter Property="IsExpanded" Value="{Binding Expanded, Mode=TwoWay}" />
    <Setter Property="IsSelected" Value="{Binding Selected, Mode=TwoWay}" />
</Style>

Appiled to a tree view:

<TreeView
    ItemContainerStyle="{StaticResource element-designer-node-host}"
    ItemsSource="{Binding Nodes}">

Now, I start a window with this tree view in a new thread and everything works fine.

After that window is closed, I start totally new window in a totally new thread and when I try to click on some node, I get the exception:

The calling thread cannot access this object because a different thread owns it.

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.StyleHelper.GetChildValueHelper(UncommonField1 dataField, ItemStructList1& valueLookupList, DependencyProperty dp, DependencyObject container, FrameworkObject child, Int32 childIndex, Boolean styleLookup, EffectiveValueEntry& entry, ValueLookupType& sourceType, FrameworkElementFactory templateRoot) at System.Windows.StyleHelper.GetChildValue(UncommonField1 dataField, DependencyObject container, Int32 childIndex, FrameworkObject child, DependencyProperty dp, FrugalStructList1& childRecordFromChildIndex, EffectiveValueEntry& entry, ValueLookupType& sourceType, FrameworkElementFactory templateRoot) at System.Windows.StyleHelper.GetValueFromTemplatedParent(DependencyObject container, Int32 childIndex, FrameworkObject child, DependencyProperty dp, FrugalStructList1& 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, Boolean preserveCurrentValue) at System.Windows.StyleHelper.InvalidateDependents(Style ownerStyle, FrameworkTemplate frameworkTemplate, DependencyObject container, DependencyProperty dp, FrugalStructList
1& dependents, Boolean invalidateOnlyContainer) at System.Windows.StyleHelper.OnTriggerSourcePropertyInvalidated(Style ownerStyle, FrameworkTemplate frameworkTemplate, DependencyObject container, DependencyProperty dp, DependencyPropertyChangedEventArgs changedArgs, Boolean invalidateOnlyContainer, FrugalStructList`1& triggerSourceRecordFromChildIndex, FrugalMap& propertyTriggersWithActions, Int32 sourceChildIndex) 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 System.Windows.Controls.TreeView.ChangeSelection(Object data, TreeViewItem container, Boolean selected) at System.Windows.Controls.TreeViewItem.Select(Boolean selected) at System.Windows.Controls.TreeViewItem.OnGotFocus(RoutedEventArgs e)
at System.Windows.UIElement.IsFocused_Changed(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(DependencyPropertyKey key, Object value) at System.Windows.Input.FocusManager.OnFocusedElementChanged(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.Input.FocusManager.SetFocusedElement(DependencyObject element, IInputElement value) at System.Windows.Input.KeyboardNavigation.UpdateFocusedElement(DependencyObject focusTarget) at System.Windows.FrameworkElement.OnGotKeyboardFocus(Object sender, KeyboardFocusChangedEventArgs e) at System.Windows.Input.KeyboardFocusChangedEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget) at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs) at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args) at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args) at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted) at System.Windows.Input.InputManager.ProcessStagingArea() at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input) at System.Windows.Input.KeyboardDevice.ChangeFocus(DependencyObject focus, Int32 timestamp) at System.Windows.Input.KeyboardDevice.TryChangeFocus(DependencyObject newFocus, IKeyboardInputProvider keyboardInputProvider, Boolean askOld, Boolean askNew, Boolean forceToNullIfFailed) at System.Windows.Input.KeyboardDevice.Focus(DependencyObject focus, Boolean askOld, Boolean askNew, Boolean forceToNullIfFailed) at System.Windows.Input.KeyboardDevice.Focus(IInputElement element) at System.Windows.UIElement.Focus() at System.Windows.Controls.TreeViewItem.OnMouseLeftButtonDown(MouseButtonEventArgs e) at System.Windows.UIElement.OnMouseLeftButtonDownThunk(Object sender, MouseButtonEventArgs e) at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget) at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs) at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent) at System.Windows.UIElement.OnMouseDownThunk(Object sender, MouseButtonEventArgs e) ...

Why does this happen and what is the solution?

Note that when I remove those three brush overrides from style resources, the problem does not occur.

Dusan
  • 5,000
  • 6
  • 41
  • 58
  • Are these `SystemColors.ControlBrushKey` created on the UI thread? – mrogal.ski Feb 02 '18 at 09:53
  • 1
    Start the new Window on a UI thread instead, IDK why this is happening but I think that the `StaticResources` are retained in the memory even after the window is closed and because you created the window in another thread and destroyed it then new thread cannot access it as all control are not thread safe. – XAMlMAX Feb 02 '18 at 09:53
  • 1
    @XAMlMAX, probably has something with the `DynamicResource`... When I switch to the `StaticResource`, works just fine in other thread... – Dusan Feb 02 '18 at 09:56
  • 1
    So when you replace the `DynamicResource` with `StaticResource` it works? That is weird as the Dynamic will evaluate the expression during runtime while static one will do it during load time. Perhaps the newly created Window then becomes an owner of the newly evaluated resources. – XAMlMAX Feb 02 '18 at 10:00
  • Yes, it works when I switch to `StaticResource`. I was using `DynamicResource` to be able to replace the initial colors with the colors from the specific skin resource dictionary. But that obviously has issues like this one. – Dusan Feb 02 '18 at 10:05
  • 1
    Ah I see, this would allow you to manage the colour scheme in real time. Can you create the window in a UI thread instead and keep using the `DynamicResource`? – XAMlMAX Feb 02 '18 at 10:11
  • Yes I can of course, but it will leave that as a last resort. Another workaround is to make colors generic, skin agnostic. But many thanks on this! Just discovered `x:Shared="False"` for the resources, will see if that makes a difference. – Dusan Feb 02 '18 at 10:16
  • 1
    @XAMlMAX, Adding the `x:Shared="False"` for the style `element-designer-node-host` solves the problem! Not an optimal but the problem is gone. You can write the answer and I will accept it. – Dusan Feb 02 '18 at 10:21
  • I will create an answer with my original suggestions, the solution to solve your problem was yours! You're welcome. – XAMlMAX Feb 02 '18 at 10:31

1 Answers1

2

It seems that the actual references of the style are retained in the memory for the element-designer-node-host. This is making the owner of the reference the thread that has been destroyed. More info from MSDN.
This has probably something to do with the new thread using those resources for the first time, but without seeing more code this is more of a guess.
Solution would be to either make the new Window being created on the UI thread or what you have found is to use x:Shared="false" which then in turn uses newly created references per usage rather than using a shared reference. Bear in mind that this will increase memory footprint of your app and can lead to out of memory exception if your target machine has limited amount of memory available.
More info from MSDN - OutOfMemoryException.

XAMlMAX
  • 2,268
  • 1
  • 14
  • 23