3

我想Button使用重新定义的模板和动画创建一个通用样式,以在鼠标进入、退出、向上、向下以及禁用和启用状态之间进行转换。这不是问题,但我想做另一个按钮样式,除了背景颜色外,基本相同。

我在样式资源和情节提要中为NormalHoverDisabled状态定义了颜色:

<Style.Resources>
    <Color x:Key="DisabledBackground">#4c4c4c</Color>
    <Color x:Key="NormalBackground">#538ce1</Color>
    <Color x:Key="HoverBackground">#6ea8ff</Color>

    <Storyboard x:Key="MouseOverAnimation">
        <ColorAnimation Storyboard.TargetName="BackgroundBrush" 
                        Storyboard.TargetProperty="Color"
                        To="{StaticResource HoverBackground}"
                        Duration="0:0:0.3" />

        <DoubleAnimation Storyboard.TargetName="Underlay"
                         Storyboard.TargetProperty="Opacity"
                         To="0.7"
                         Duration="0:0:0.3" />
    </Storyboard>

    <!-- and few others... -->
</Style>

然后我定制了模板,最后是这个ControlTemplate.Triggers部分:

<Trigger Property="IsMouseOver" Value="True">
    <Trigger.EnterActions>
        <BeginStoryboard Storyboard="{DynamicResource MouseOverAnimation}"/>
    </Trigger.EnterActions>

    <Trigger.ExitActions>
        <BeginStoryboard Storyboard="{DynamicResource MouseOutAnimation}"/>
    </Trigger.ExitActions>
</Trigger>

<!-- and few others... -->

现在我想要的是创建新样式并更改颜色,DisabledBackground如下NormalBackground所示:

<Style x:Key="Start"
       TargetType="{x:Type Button}"
       BasedOn="{StaticResource {x:Type Button}}">

    <Style.Resources>
        <Color x:Key="DisabledBackground">#4c4c4c</Color>
        <Color x:Key="NormalBackground">#960a0a</Color>
        <Color x:Key="HoverBackground">#de1111</Color>
    </Style.Resources>
</Style>

并让控件模板原封不动。您可能已经注意到,我DynamicResource在常用按钮样式中使用了以异常结尾的样式资源中的情节提要,因为情节提要不能具有绑定或动态资源。这是我最后一个不起作用的“解决方案”,但我想不出别的办法。

我不想复制和粘贴我的整个按钮样式只是为了更改两种颜色。如何修改我的样式,以便能够“动态”更改故事板动画中使用的颜色,或者至少继承样式并在那里设置颜色?

完成 XAML

<Style TargetType="{x:Type Button}">

    <Style.Resources>
        <Color x:Key="DisabledBackground">#4c4c4c</Color>
        <Color x:Key="NormalBackground">#538ce1</Color>
        <Color x:Key="HoverBackground">#6ea8ff</Color>

        <Storyboard x:Key="MouseOverAnimation">
            <ColorAnimation Storyboard.TargetName="BackgroundBrush" Storyboard.TargetProperty="Color" To="{StaticResource HoverBackground}" Duration="0:0:0.3" />
            <DoubleAnimation Storyboard.TargetName="Underlay" Storyboard.TargetProperty="Opacity" To="0.7" Duration="0:0:0.3" />
        </Storyboard>
        <Storyboard x:Key="MouseOutAnimation" FillBehavior="Stop">
            <ColorAnimation Storyboard.TargetName="BackgroundBrush" Storyboard.TargetProperty="Color" To="{StaticResource NormalBackground}" Duration="0:0:0.3" />
            <DoubleAnimation Storyboard.TargetName="Underlay" Storyboard.TargetProperty="Opacity" To="0.2" Duration="0:0:0.3" />
        </Storyboard>
        <Storyboard x:Key="MouseDownAnimation">
            <DoubleAnimation Storyboard.TargetName="OverlayGradient" Storyboard.TargetProperty="Opacity" To="0.45" Duration="0:0:0.1" />
        </Storyboard>
        <Storyboard x:Key="MouseUpAnimation" Storyboard.TargetProperty="Background" FillBehavior="Stop">
            <DoubleAnimation Storyboard.TargetName="OverlayGradient" Storyboard.TargetProperty="Opacity" To="0.5" Duration="0:0:0.1" />
        </Storyboard>
        <Storyboard x:Key="DisabledAnimation">
            <ColorAnimation Storyboard.TargetName="BackgroundBrush" Storyboard.TargetProperty="Color" To="{StaticResource DisabledBackground}" Duration="0:0:0.3" />
            <ColorAnimation Storyboard.TargetName="UnderlayFillBrush" Storyboard.TargetProperty="Color" To="{StaticResource DisabledBackground}" Duration="0:0:0.3" />
        </Storyboard>
        <Storyboard x:Key="EnabledAnimation">
            <ColorAnimation Storyboard.TargetName="BackgroundBrush" Storyboard.TargetProperty="Color" To="{StaticResource NormalBackground}" Duration="0:0:0.3" />
            <ColorAnimation Storyboard.TargetName="UnderlayFillBrush" Storyboard.TargetProperty="Color" To="{StaticResource NormalBackground}" Duration="0:0:0.3" />
        </Storyboard>
    </Style.Resources>

    <Setter Property="Template">
        <Setter.Value>

            <ControlTemplate TargetType="Button">

                <Grid>

                    <!-- Button underlay glow

                    -->
                    <Rectangle x:Name="Underlay" Opacity="0.2">
                        <Rectangle.Fill>
                            <SolidColorBrush x:Name="UnderlayFillBrush" Color="{DynamicResource NormalBackground}"/>
                        </Rectangle.Fill>

                        <Rectangle.Effect>
                            <BlurEffect Radius="35" KernelType="Gaussian"/>
                        </Rectangle.Effect>
                    </Rectangle>

                    <!-- Button base border with rounded corners

                    Contains base background
                    -->
                    <Border x:Name="ButtonBackground" BorderThickness="1" CornerRadius="2">
                        <Border.BorderBrush>
                            <SolidColorBrush Color="Black" Opacity="0.8"/>
                        </Border.BorderBrush>

                        <Border.Background>
                            <SolidColorBrush x:Name="BackgroundBrush" Color="{DynamicResource NormalBackground}"/>
                        </Border.Background>

                        <!-- Button Overlay

                        Adds the background overlay gradient -->
                        <Border CornerRadius="2">
                            <Border.Background>
                                <LinearGradientBrush x:Name="OverlayGradient" Opacity="0.5" StartPoint="0,0" EndPoint="0,1">
                                    <GradientStop Offset="0" Color="White"/>
                                    <GradientStop Offset="0.02" Color="White"/>
                                    <GradientStop Offset="0.02" Color="Transparent"/>
                                    <GradientStop Offset="0.85" Color="#000000" />
                                </LinearGradientBrush>
                            </Border.Background>


                            <Border BorderThickness="1" CornerRadius="2">
                                <Border.BorderBrush>
                                    <SolidColorBrush Color="#b4b4b4" Opacity="0.2"/>
                                </Border.BorderBrush>

                                <!-- Inner text -->
                                <TextBlock Text="{TemplateBinding Content}"
                                           FontSize="{TemplateBinding FontSize}"
                                           FontFamily="Segoe UI"
                                           Foreground="White"
                                           TextWrapping="Wrap"
                                           HorizontalAlignment="Center"
                                           VerticalAlignment="Center"
                                           TextOptions.TextFormattingMode="Display"
                                           RenderOptions.BitmapScalingMode="NearestNeighbor">
                                    <TextBlock.Effect>
                                        <DropShadowEffect ShadowDepth="0" BlurRadius="6" Color="Black" RenderingBias="Quality"/>
                                    </TextBlock.Effect>
                                </TextBlock>

                            </Border>

                        </Border>

                    </Border>

                </Grid>

                <ControlTemplate.Triggers>

                    <Trigger Property="IsEnabled" Value="False">
                        <Trigger.EnterActions>
                            <BeginStoryboard Storyboard="{DynamicResource DisabledAnimation}"/>
                        </Trigger.EnterActions>

                        <Trigger.ExitActions>
                            <BeginStoryboard Storyboard="{DynamicResource EnabledAnimation}"/>
                        </Trigger.ExitActions>
                    </Trigger>

                    <Trigger Property="IsMouseOver" Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard Storyboard="{DynamicResource MouseOverAnimation}"/>
                        </Trigger.EnterActions>

                        <Trigger.ExitActions>
                            <BeginStoryboard Storyboard="{DynamicResource MouseOutAnimation}"/>
                        </Trigger.ExitActions>
                    </Trigger>

                    <Trigger Property="IsPressed" Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard Storyboard="{DynamicResource MouseDownAnimation}"/>
                        </Trigger.EnterActions>

                        <Trigger.ExitActions>
                            <BeginStoryboard Storyboard="{DynamicResource MouseUpAnimation}"/>
                        </Trigger.ExitActions>
                    </Trigger>

                </ControlTemplate.Triggers>

            </ControlTemplate>


        </Setter.Value>
    </Setter>

</Style>

4

2 回答 2

2

引用MSDN上的文档:

您不能使用动态资源引用或数据绑定表达式来设置 Storyboard 或动画属性值。这是因为 Style 中的所有内容都必须是线程安全的,并且计时系统必须冻结 Storyboard 对象以使其成为线程安全的。如果 Storyboard 或其子时间线包含动态资源引用或数据绑定表达式,则无法冻结 Storyboard。有关冻结和其他Freezable功能的更多信息,请参阅Freezable 对象概述

您当然可以使用超级黑客等,但就您而言,在我看来,在资源中使用不同的样式或颜色更容易。这不会那么困难。

有关更多信息,请参阅:

MSDN 文档

类似的问题

于 2013-06-17T11:45:17.277 回答
0

尽管这个问题被提出和回答已经有好几年了,但我有一些有用的信息供其他人记住,这样你就不会犯我多年前在尝试解决这个问题时犯的同样错误。

TLDR使用ComponentResourceKey作为一种使动态资源静态的方式时 要非常谨慎,以便您可以将其与可冻结对象一起使用。它会编译,它甚至可以运行,但你很幸运,而且它可能一开始就不应该被允许,因为它可以防止人们从悬崖上走下来。请参阅底部的代码示例,不要使用freezables这样做。

详细信息 与其他人一样,我希望尽可能地保持资源的灵活性。我知道我们不能将动态资源用于动画,但遇到了一种“聪明”的方法,代码类似于以下示例。这里的关键(不是双关语)是使用ComponentResourceKey。大约一年前我使用这种方法后,当它与动画一起工作时,我几乎在地板上跳霹雳舞。乍一看,您可能会想,“这当然行得通,您在每个代码示例中都使用了静态资源”。但是,一旦您意识到ComponentResourceKey是一个提供“超能力”的标记扩展您不必将该程序集作为参考包含在内,更容易理解这有效地转换为伪动态资源。

在可冻结的情况下,我相信当时这对我有用,因为我不小心创建了一个初始化排序,使它看起来有效。也就是说,该值是在首次使用之前定义的。然后在这个周末(现在是几年后),我开始将我所有的 bin/obj 和 ext 文件转储到一个公共位置,在那里它们可以很容易地被我的构建系统破坏,所以我相信我有一个干净的构建。突然,我开始遇到致命的 xaml 异常,表明我在某些位置使用的 ComponentResourceKey 没有定义。然后我直接将程序集包含在使用它们的位置而不是主 exe 中。我知道这违背了我试图避免的(耦合),但它是在午夜之后,我希望它在我退休之前编译,

在我克服了

好吧,它工作了一年多,这一定是我最近改变的东西

感觉我们都有,很明显,我所做的事情一开始就不应该被使用。我认为我们心中的“优秀程序员”总是希望尽可能地灵活和解耦,即使这意味着想出一些真正有创意的技术来满足我们解决当天面临的问题的需要。

除了真正破坏构建之间的二进制文件之外,我还将大约 128 个库转换为 Nuget 包,我认为这在某种程度上影响了设置键值与使用时的初始化顺序。午夜过后,这一切似乎都像巫毒一样。

我仍然认为使用ComponentResourceKey标记扩展可能是解耦 xaml 库相互依赖关系的好方法,您不能安全地使用它们与可冻结对象(例如情节提要动画中的对象)一起使用。当我第一次应用这种技术时,我并没有尝试在运行时动态更改值,因为对我来说这只是一个延伸目标,我知道有一天我会回顾并庆幸我花时间让它变得如此灵活. (现在我希望我能回到那个时代!)

使用ComponentResourceKey的示例 不要对可冻结对象执行此操作

<BeginStoryboard>
    <Storyboard FillBehavior="Stop" Duration="0:0:.1">
        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background">
            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource {ComponentResourceKey TypeInTargetAssembly={x:Type local:SettingKeys}, ResourceId=DefaultButtonClickBackgroundBrush}}" />
        </ObjectAnimationUsingKeyFrames>
    </Storyboard>
</BeginStoryboard>

顺便说一句,这是一个定义密钥的示例,以防您想将此技术应用于除可冻结文件以外的任何内容。请注意,local:SettingKeys 只是一个空的 C Sharp 类,谷歌该技术以完全理解。

<SolidColorBrush x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:SettingKeys},ResourceId=DefaultButtonClickBackgroundBrush}" Color="#FE8D00" />

如果这让别人头疼,我会很高兴。就像接受的答案的作者所说的那样,您可以使用创造性的技巧,但您应该避免使用它们或像我一样从悬崖上走下来。

于 2019-12-07T19:39:10.277 回答