'Referencing a StaticResource in another DynamicResource
If I change some resource used as StaticResource then all the controls which referring to is affected.
And it does not in case the resource is referred as DynamicResource.
But how about some resource referred as StaticResource in a DynamicResource?
<Color x:Key="Color">#000000</Color>
<LinearGradientBrush x:Key="GradientBrush" EndPoint="0,0" StartPoint="0,1" >
<GradientStop Color="{StaticResource Color}" Offset="0" />
<GradientStop Color="{StaticResource Color}" Offset="1" />
</LinearGradientBrush>
<DrawingBrush x:Key="TabItemBrush" >
<DrawingBrush.Drawing>
<GeometryDrawing Brush="{StaticResource GradientBrush}">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0 100 100 100"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid x:Name="Root">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected" >
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Panel.Background).(DrawingBrush.Drawing).(GeometryDrawing.Brush).(GradientBrush.GradientStops)[0].(GradientStop.Color)">
<EasingColorKeyFrame KeyTime="0" Value="Red" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Selected">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Panel.Background).(DrawingBrush.Drawing).(GeometryDrawing.Brush).(GradientBrush.GradientStops)[0].(GradientStop.Color)">
<EasingColorKeyFrame KeyTime="0" Value="Blue" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Border" Background="{DynamicResource TabItemBrush}">
<ContentPresenter x:Name="ContentSite" VerticalAlignment="Center"
HorizontalAlignment="Center" ContentSource="Header"
Margin="12,2,12,2" RecognizesAccessKey="True" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Border in TabItem refers TabItemBrush using DynamicResource.
TabItemBrush refers GradientBrush using StaticResource.
GradientBrush refers Color using StaticResource.
<StackPanel>
<TabControl>
<TabItem Header="AAA" >AAA</TabItem>
<TabItem Header="BBB" >BBB</TabItem>
<TabItem Header="CCC" >BBB</TabItem>
</TabControl>
<Border Background="{DynamicResource GradientBrush}" Height="1000" Width="1000"/>
</StackPanel>
I thought that all of TabItem change to same color when I change a selected item in TabControl because all of resources is referred as StaticResource except TabItemBrush.
But only the color of selected item is blue and the others red.
Moreover if i change all the StaticResource to DynamicResource, it works incorrectly ( all red or all blue).
Why StaticResource works as if it is not shared and DynamicResource as shared?
Solution 1:[1]
I found your question very difficult to understand. However, I think you are confused because you are not understanding the difference between StaticResource and DynamicResource.
- As the names suggest,
StaticResourcereferences will/can never change (they are static). - Resources referenced using
StaticResourceare resolved only once at compile-time. - As the names suggest,
DynamicResourcereferences can change (they are dynamic). - Resources referenced using
DynamicResourceare resolved at runtime. - Since
StaticResourcereferences are resolved at compile-time, the XAML parser can avoid the overhead of creating an intermediate lookup expression (which is then executed at runtime).
This is why you should always avoidDynamicResourcewith the goal to improve the application's performance. StaticResourcedoes not allow forward references, whereasDynamicResourcedoes - but the forward reference aspect doesn't matter in your context.
"If I change some resource used as StaticResource then all the controls which referring to is affected."
This is definitely not correct. You can't change resources referenced as StaticResource and then observe that those changes update the referencing objects - this can't have never happened (see explanation of the fundamental characteristics above).
You probably meant something different here.
"I thought that all of TabItem change to same color when I change a selected >item in TabControl because all of resources is referred as StaticResource >except TabItemBrush.
But only the color of selected item is blue and the others red."
This is the correct behavior. You have defined the VisualState objects for "Selected" and "Unselected" with the VisualStateManager. According to these states clicking a TabItem will modify the Background of the selected item to blue and all other unselected items to red.
"Moreover if i change all the StaticResource to DynamicResource, it works >incorrectly ( all red or all blue)."
Here comes in the special behavior of the XAML engine in context of animations: it will freeze the Storyboard.
This means, all participating child instances like AnimationTimeline (e.g., ColorAnimation), or Animatable types in general, are frozen and therefore not changeable (they all inherit Freezable).
As a consequence, all referenced resources like Brush must be static and known at compile-time: you can't reference resources using DynamicResource if the referencing instance needs to be frozen.
Resources used in a Storyboard must be referenced as StaticResource.
Now, given the case that an element, for example a Border, uses DynamicResource to reference a resource that itself contains references to other resources:
- If the element uses
DynamicResourceto reference a pure static resource (a resource that doesn't itself references other resources usingDynamicReference), then the XAML engine will optimize the reference by treating it as aStaticResource, to avoid the overhead, and store it in the internal lookup table for the static resources (remember,StaticResourceis much cheaper in terms of performance costs than looking up aDynamicResource). This resource can be targeted by aStoryboardanimation as it is now static:
<ResourceDictionary>
<Color x:Key="Color">#000000</Color>
<!-- Resource is treated as a static resource -->
<LinearGradientBrush x:Key="GradientBrush" EndPoint="0,0" StartPoint="0,1" >
<GradientStop Color="{StaticResource Color}" Offset="0" />
<GradientStop Color="{StaticResource Color}" Offset="1" />
</LinearGradientBrush>
</ResourceDictionary>
<!-- The lookup behavior is optimized to StaticResource.
A Storyboard therefore will be able to animate the Background resource.
-->
<Border Background="{DynamicResource GradientBrush}" />
- If the resource that an element references is itself referencing at least one resource using
DynamicResource, then the reference will remain dynamic and will prevent e.g., the Storyboard from being frozen and the animation won't work:
<ResourceDictionary>
<Color x:Key="Color">#000000</Color>
<!-- Resource is treated as a dynamic resource,
because it contains at least one DynamicResource reference.
-->
<LinearGradientBrush x:Key="GradientBrush" EndPoint="0,0" StartPoint="0,1" >
<GradientStop Color="{DynamicResource Color}" Offset="0" />
<GradientStop Color="{StaticResource Color}" Offset="1" />
</LinearGradientBrush>
</ResourceDictionary>
<!-- The lookup behavior is now DynamicResource and not optimized.
A Storyboard won't be able to animate the properties of the resource as it can't be frozen.
-->
<Border Background="{DynamicResource GradientBrush}" />
Another effect you ecounter is that all items have the same Background when you select a TabItem.
This is related to the way the XAML engine handles resources. Referenceing a resource using StaticResource, results in the resource being shared by default.
You have two solutions to fix this issue:
- Declare each participating resource of the static reference as non-shared by setting the
x:Sharedattribute tofalse:
<ResourceDictionary>
<Color x:Key="Color"
x:Shared="False">#000000</Color>
<LinearGradientBrush x:Key="GradientBrush"
x:Shared="False"
EndPoint="0,0"
StartPoint="0,1" >
<GradientStop Color="{StaticResource Color}" Offset="0" />
<GradientStop Color="{StaticResource Color}" Offset="1" />
</LinearGradientBrush>
</ResourceDictionary>
<Border Background="{StaticResource GradientBrush}" />
- Alternatively, define the animated resource as inline. This way each
TabItemborder will have its own/non-sharedBrushresource:
<ResourceDictionary>
<Color x:Key="Color">#000000</Color>
</ResourceDictionary>
<Border>
<Border.Background>
<LinearGradientBrush EndPoint="0,0" StartPoint="0,1" >
<GradientStop Color="{StaticResource Color}" Offset="0" />
<GradientStop Color="{StaticResource Color}" Offset="1" />
</LinearGradientBrush>
</Border.Background>
</Border>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
