在另一个 DynamicResource 中引用一个 StaticResource

Referencing a StaticResource in another DynamicResource

如果我更改一些用作 StaticResource 的资源,那么所有引用的控件都会受到影响。

如果资源被称为 DynamicResource,则不会。

但是在 DynamicResource 中称为 StaticResource 的一些资源怎么样?

        <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 引用 TabItemBrush 使用 DynamicResource.

TabItemBrush 使用 StaticResource.

引用 GradientBrush

GradientBrush 使用 StaticResource.

引用 Color
    <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>

我认为当我更改 TabControl 中的选定项目时,所有 TabItem 都变成相同的颜色,因为除了 TabItemBrush.[=26= 之外的所有资源都被称为 StaticResource ]

但只有选中的项目颜色是蓝色,其他都是红色。

此外,如果我将所有 StaticResource 更改为 DynamicResource,它将无法正常工作(全红或全蓝)。

为什么 StaticResource 就像不共享一样工作,而 DynamicResource 像共享一样工作?

我发现你的问题很难理解。但是,我认为您很困惑,因为您不了解 StaticResourceDynamicResource 之间的区别。

  • 顾名思义,StaticResource 引用 will/can 永远不会改变(它们是静态的)。
  • 使用 StaticResource 引用的资源仅在 compile-time 解析一次。
  • 顾名思义,DynamicResource 引用可以更改(它们是动态的)。
  • 使用 DynamicResource 引用的资源在运行时解析。
  • 由于 StaticResource 引用在 compile-time 解析,XAML 解析器可以避免创建中间查找表达式(然后在运行时执行)的开销。
    这就是为什么您应该始终避免 DynamicResource 以提高应用程序性能的原因。
  • StaticResource 不允许前向引用,而 DynamicResource 允许 - 但前向引用方面在您的上下文中并不重要。

"If I change some resource used as StaticResource then all the controls which referring to is affected."

这绝对不正确。您不能更改引用为 StaticResource 的资源,然后观察到这些更改会更新引用对象 - 这不可能从未发生过(请参阅上面对基本特征的解释)。
您在这里的意思可能有所不同。

"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."

这是正确的行为。您已经使用 VisualStateManager 为“Selected”和“Unselected”定义了 VisualState 个对象。根据这些状态,单击 TabItem 会将 selected 项目的 Background 修改为蓝色,将所有其他未selected 项目修改为红色。

"Moreover if i change all the StaticResource to DynamicResource, it works >incorrectly ( all red or all blue)."

这是 XAML 引擎在动画上下文中的特殊行为:它将冻结 Storyboard
这意味着,所有参与的子实例,如 AnimationTimeline(例如,ColorAnimation)或一般的 Animatable 类型,都被冻结,因此 不可更改(他们都继承了Freezable).

因此,所有引用的资源,如 Brush 必须是静态的,并且在 compile-time 处已知:如果引用实例需要冻结,则不能使用 DynamicResource 引用资源.
Storyboard 中使用的资源必须引用为 StaticResource.

现在,假设一个元素(例如 a Border)使用 DynamicResource 来引用本身包含对其他资源的引用的资源:

  • 如果元素使用 DynamicResource 引用纯静态资源(本身不使用 DynamicReference 引用其他资源的资源),则 XAML 引擎将优化通过将其视为 StaticResource 来引用以避免开销,并将其存储在静态资源的内部查找 table 中(请记住,StaticResource 在性能成本方面比查找 DynamicResource)。此资源可以作为 Storyboard 动画的目标,因为它现在是静态的:
<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}" />
  • 如果元素引用的资源本身引用了至少一个使用 DynamicResource 的资源,那么引用将保持动态并且将防止 Storyboard 被冻结和动画无法工作等:
<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}" />

您遇到的另一个效果是,当您 select TabItem 时,所有项目都具有相同的背景。
这与 XAML 引擎处理资源的方式有关。使用 StaticResource 引用资源会导致资源默认共享。

您有两种解决此问题的方法:

  1. 通过将 x:Shared 属性设置为 false:
  2. 将静态引用的每个参与资源声明为 non-shared
<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}" />
  1. 或者,将动画资源定义为内联。这样每个 TabItem 边框都会有其 own/non-shared Brush 资源:
<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>