在动画中使用自定义控件属性

时间:2011-02-03 23:03:01

标签: wpf wpf-controls

如何连接在控件模板中设置动画的自定义控件属性?

我正在创建一个包含多个状态的自定义按钮,我希望在状态发生变化时为按钮文本的颜色设置动画。通常,文本为灰色,并且在MouseOver上变为黑色。

我为黑色文本创建了一个NormalTextBrush,为灰色文本创建了一个FadedTextBrush:

<SolidColorBrush x:Key="NormalTextBrush" Color="Black" />
<SolidColorBrush x:Key="FadedTextBrush" Color="DarkSlateGray" />

到目前为止,这么好。我的动画运行没有错误。当鼠标移过按钮时,文本从灰色变为黑色。但我真正想做的是让使用该控件的开发人员指定文本颜色。

因此,我将两个文本画笔重新定义为绑定到控件的Foreground属性。 FadedTextBrush使用值转换器淡化前景色。重新定义的资源如下所示:

<SolidColorBrush x:Key="NormalTextBrush" Color="{Binding Path=Foreground, RelativeSource={RelativeSource TemplatedParent}}" />
<SolidColorBrush x:Key="FadedTextBrush" Color="{Binding Path=Foreground, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource ColorConverter}, ConverterParameter='1.2'}" />

这就是我遇到麻烦的地方。 WPF不允许对控件模板内的动画进行绑定。它会抛出异常,并显示消息“无法冻结此Storyboard时间轴树以供跨线程使用”。该问题记录在案here

这让我想到了一个问题:如何设置我的Brush资源以便它们连接到Foreground属性,但是我仍然可以将它们与我的控件模板中的动画一起使用?

任何能够回答这个问题的人,下次见到你时我会给你买啤酒!谢谢你的帮助。

1 个答案:

答案 0 :(得分:0)

您必须使用可用的工具。我建议占用相同屏幕空间的两个文本块,然后在故事板中操纵每个文本块的不透明度。以下是如何在默认状态下覆盖顶部透明的两个文本块:

<Grid>
    <TextBlock x:Name="textBlock1" Foreground="Black"
        Margin="{TemplateBinding Padding}"
        VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
        Text="{TemplateBinding Content}"/>
    <TextBlock x:Name="textBlock2" Foreground="Gray"
        Margin="{TemplateBinding Padding}"
        VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
        Text="{TemplateBinding Content}" Opacity="0"/>
</Grid>

然后使用可视状态管理器设置一个可视状态组来交换它们的不透明度:

<VisualStateGroup x:Name="CommonStates">
    <VisualStateGroup.Transitions>
        <VisualTransition GeneratedDuration="0:0:0.5"/>
    </VisualStateGroup.Transitions>
    <VisualState x:Name="Normal"/>
    <VisualState x:Name="MouseOver">
        <Storyboard>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="textBlock1">
                <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="textBlock2">
                <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </VisualState>
    <VisualState x:Name="Pressed"/>
    <VisualState x:Name="Disabled"/>
</VisualStateGroup>

或者您可以使用自己的故事板。关键是我们现在正在使用故事板操纵常量值,但您可以使用您选择的任何方法设置每个状态的文本块的前景。