在 WPF 中为 TextBox.Foreground 设置动画

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18423547/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 09:29:28  来源:igfitidea点击:

Animating a TextBox.Foreground in WPF

wpfanimationtextboxstoryboardwpf-4.0

提问by ravy amiry

Is there anyway to animate a TextBox.ForegroundProperty?

反正有动画TextBox.ForegroundProperty吗?

<Color x:Key="NormalColor">#FF666666</Color>
<SolidColorBrush x:Key="NormalBrush" Color="{StaticResource NormalColor}" />

<Color x:Key="MouseOverColor">#FF666666</Color>
<SolidColorBrush x:Key="MouseOverBrush" Color="{StaticResource MouseOverColor}" />

<ControlTemplate x:Key="RegularTextBoxTemplate" TargetType="{x:Type TextBox}">
    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CommonStates">
                <VisualStateGroup.Transitions>
                    <VisualTransition GeneratedDuration="0:0:0.1"/>
                </VisualStateGroup.Transitions>
                <VisualState x:Name="Normal"/>
                <VisualState x:Name="MouseOver">
                    <Storyboard>
                        <!-- storyboard to animating foreground here... -->
                    </Storyboard>
                </VisualState>
            </VisualStateGroup >
        </VisualStateManager>
        <ScrollViewer x:Name="PART_ContentHost" 
                      BorderThickness="0"
                      IsTabStop="False"
                      Background="{x:Null}"/>
    </Grid>
</ControlTemplate>

<Style x:Key="RegularTextBox" TargetType="{x:Type TextBox}">
    <Setter Property="Foreground" Value="{StaticResource NormalBrush}"/>
    <Setter Property="Template" Value="{StaticResource RegularTextBoxTemplate}"/>
</Style>

My tried storyboards are:

我尝试过的故事板是:

<ColorAnimationUsingKeyFrames Storyboard.TargetName="PART_ContentHost"
                  Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)">
    <EasingColorKeyFrame KeyTime="0" Value="{StaticResource MouseOverColor}" />
</ColorAnimationUsingKeyFrames>

<ColorAnimationUsingKeyFrames Storyboard.TargetName="PART_ContentHost"
              Storyboard.TargetProperty="(Control.Foreground).(SolidColorBrush.Color)">
    <EasingColorKeyFrame KeyTime="0" Value="{StaticResource MouseOverColor}" />
</ColorAnimationUsingKeyFrames>

<ColorAnimationUsingKeyFrames Storyboard.TargetName="PART_ContentHost"
          Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)">
    <EasingColorKeyFrame KeyTime="0" Value="{StaticResource MouseOverColor}" />
</ColorAnimationUsingKeyFrames>

<ColorAnimationUsingKeyFrames
                  Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)">
    <EasingColorKeyFrame KeyTime="0" Value="{StaticResource MouseOverColor}" />
</ColorAnimationUsingKeyFrames>

<ColorAnimationUsingKeyFrames
              Storyboard.TargetProperty="(TextBox.Foreground).(SolidColorBrush.Color)">
    <EasingColorKeyFrame KeyTime="0" Value="{StaticResource MouseOverColor}" />
</ColorAnimationUsingKeyFrames>

<ColorAnimationUsingKeyFrames
              Storyboard.TargetProperty="(Control.Foreground).(SolidColorBrush.Color)">
    <EasingColorKeyFrame KeyTime="0" Value="{StaticResource MouseOverColor}" />
</ColorAnimationUsingKeyFrames>

<ColorAnimationUsingKeyFrames
          Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)">
    <EasingColorKeyFrame KeyTime="0" Value="{StaticResource MouseOverColor}" />
</ColorAnimationUsingKeyFrames>

None of them work. Any idea? Is it even possible?

他们都没有工作。任何的想法?甚至有可能吗?

回答by Stephen Hewlett

This was more problematic than I thought. Here is my original answer:

这比我想象的更成问题。这是我的原始答案:



It's definitely possible - that's what the ColorAnimationXXX classes are for.

这绝对是可能的 - 这就是 ColorAnimationXXX 类的用途。

Your code is very similar to the code example here, which animates a colour with the ColorAnimation instead. The property in the example takes a Brush (just like TextBox.Foreground) which is defined in XAML and given a name so that it can be referenced easily by the animation.

您的代码与此处的代码示例非常相似,后者使用 ColorAnimation 为颜色设置动画。示例中的属性采用一个 Brush(就像 TextBox.Foreground),它在 XAML 中定义并指定了一个名称,以便动画可以轻松引用它。

So in your case the pertinent code would be:

所以在你的情况下,相关的代码是:

<VisualState Name="...">
   <Storyboard>
      <ColorAnimation To="Green" 
                      Storyboard.TargetName="tbBrush" 
                      Storyboard.TargetProperty="Color"/>
    </Storyboard>
</VisualState>

and:

和:

<TextBox.Foreground>
  <SolidColorBrush x:Name="tbBrush" Color="#FF666666"/>
</TextBox.Foreground>


That was all very well, in theory, until I realised it didn't work in a style. Whereas the Backgroundproperty of the Grid within the style is easily animatable, with something like:

从理论上讲,这一切都很好,直到我意识到它在风格上不起作用。而Background样式中的 Grid 属性很容易设置动画,例如:

Storyboard.TargetProperty="(Grid.Background).(SolidColorBrush.Color)"

it is significantly more difficult to find a property to animate that will have an effect on the Foreground of the text. Initially I tried TextElement.Foreground, which seems intuitive, and I was able to set this property at the Grid and ScrollViewer levels which I expected to have an effect on all the child objects underneath - including whatever object it is at the bottom level that contains the text of the TextBox. My assumption was that the TextBox content would be internally set to a TextBlock, which would obey the value of the Foreground attached property set on it. It seems that my assumption was incorrect, and the content of the PART_ContentHost ScrollViewer is set by the control logic within TextBox to a lower level object that does not obey any of the Foreground dependency properties in the object tree between the top level TextBox and itself.

找到对文本前景产生影响的动画属性要困难得多。最初我试过TextElement.Foreground,这看起来很直观,而且我能够在 Grid 和 ScrollViewer 级别设置此属性,我希望这会对下面的所有子对象产生影响 - 包括它位于包含 TextBox 文本的底层的任何对象。我的假设是 TextBox 内容将在内部设置为 TextBlock,它会遵守设置在其上的 Foreground 附加属性的值。看来我的假设是不正确的,并且 PART_ContentHost ScrollViewer 的内容由 TextBox 中的控制逻辑设置为一个较低级别的对象,该对象不遵守顶级 TextBox 与其自身之间的对象树中的任何 Foreground 依赖属性。

The problem then is how to set the Foreground property of the TextBox within the style of the TextBox being styled. For testing, I tried to set this with a TwoWay TemplatedParent binding. I think I got the PropertyPath to the Color of the SolidColorBrush right, but it still didn't work as the Color property was apparently immutable at that point. I believe this issue is documented here.

那么问题是如何在要设置样式的 TextBox 的样式内设置 TextBox 的 Foreground 属性。为了测试,我尝试使用 TwoWay TemplatedParent 绑定来设置它。我想我正确地获得了 SolidColorBrush 颜色的 PropertyPath,但它仍然不起作用,因为当时 Color 属性显然是不可变的。我相信此问题已记录在此处

On top of the fact that it doesn't work, setting the Foreground property internally did not seem right as external consumers would expect to be in control of the value of that property. So, given that the Foreground of a TextBox will not obey anything in a style, I came to the conclusion that the functionality is best implemented with a nested TextBox within the TextBox style. The outer style contains the state manager and most of the layout, then the inner TextBox has its own style and control template that is designed just to display the text bit. The outer style is able to set the Foreground property of the inner TextBox, which the inner one will obey, and crucially the outer one can set this value in the state manager.

最重要的是它不起作用,在内部设置 Foreground 属性似乎不正确,因为外部消费者希望控制该属性的值。因此,鉴于 TextBox 的 Foreground 不会遵循任何样式,我得出的结论是,最好使用 TextBox 样式中的嵌套 TextBox 来实现该功能。外部样式包含状态管理器和大部分布局,然后内部 TextBox 具有自己的样式和控件模板,旨在显示文本位。外部样式可以设置内部 TextBox 的 Foreground 属性,内部样式会遵守该属性,而最重要的是外部样式可以在状态管理器中设置该值。

<ControlTemplate x:Key="RegularTextBoxTemplate" TargetType="{x:Type TextBox}"> 
    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CommonStates">
                <VisualStateGroup.Transitions>
                    <VisualTransition GeneratedDuration="0:0:0.1"/>
                </VisualStateGroup.Transitions>
                <VisualState x:Name="Normal"/>
                <VisualState x:Name="MouseOver">
                    <Storyboard>
                        <ColorAnimation To="HotPink"
                            Storyboard.TargetName="InternalTextBox"
                            Storyboard.TargetProperty="(TextBox.Foreground).(SolidColorBrush.Color)"/>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <TextBox Foreground="Black" Text="{TemplateBinding Text}" x:Name="InternalTextBox">
            <TextBox.Style>
                <Style TargetType="{x:Type TextBox}">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type TextBox}">
                                <Grid Background="{x:Null}">
                                    <ScrollViewer x:Name="PART_ContentHost"
                                        BorderThickness="0"
                                        IsTabStop="False"
                                        Background="{x:Null}" />
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </TextBox.Style>
        </TextBox>
    </Grid>
</ControlTemplate>

<Style x:Key="RegularTextBox" TargetType="{x:Type TextBox}">
    <Setter Property="Template" Value="{StaticResource RegularTextBoxTemplate}"/>
</Style>

I would be interested in hearing other people's comments on this approach, and whether there are any problems with it that I have not considered. Based on my attempts at solving the problem and snooping the resultant application, it is the simplest solution I can see at the moment.

我很想听听其他人对这种方法的评论,以及它是否存在我没​​有考虑过的问题。根据我解决问题和窥探结果应用程序的尝试,这是我目前能看到的最简单的解决方案。

回答by ravy amiry

Well, thanks to all that were trying to help me, I found my answer. It seems when we set TextBox.Foregroundproperty to a resource, the storyboard cannot animate it. So, the style should be something like this:

好吧,感谢所有试图帮助我的人,我找到了答案。似乎当我们TextBox.Foreground为资源设置属性时,故事板无法为其设置动画。所以,样式应该是这样的:

<Style x:Key="RegularTextBox" TargetType="{x:Type TextBox}">
    <Setter Property="Foreground">
        <Setter.Value>
            <SolidColorBrush Color="{DynamicResource NormalColor}"/>
        </Setter.Value>
    </Setter>
    <Setter Property="Template" Value="{StaticResource RegularTextBoxTemplate}"/>
</Style>

This was the only problem I had. But there is a note to remember. When we want to target a templated parent in a storyboard, it's not necessary to bind to it. We just need to leave it:

这是我遇到的唯一问题。但是有一个注意事项要记住。当我们想在故事板中定位一个模板化的父对象时,没有必要绑定到它。我们只需要离开它:

<!-- It's not necessary to set Storyboard.TargetName in storyboard -->
<!-- It will automatically target the TemplatedParent -->
<ColorAnimationUsingKeyFrames
              Storyboard.TargetProperty="(TextBox.Foreground).(SolidColorBrush.Color)">
    <EasingColorKeyFrame KeyTime="0" Value="{DynamicResource MouseOverColor}" />
</ColorAnimationUsingKeyFrames>

This works for me.

这对我有用。



Hereis a working example.

是一个工作示例。

回答by nmclean

You can bind Storyboard.Targetto the TemplatedParent:

您可以绑定Storyboard.Target到 TemplatedParent:

<ColorAnimationUsingKeyFrames
        Storyboard.Target="{Binding RelativeSource={RelativeSource TemplatedParent}}"
        Storyboard.TargetProperty="(TextBox.Foreground).(SolidColorBrush.Color)">
    <EasingColorKeyFrame KeyTime="0" Value="{StaticResource MouseOverColor}" />
</ColorAnimationUsingKeyFrames>

Unfortunately, I could only get this to work when the normal foreground brush is notset in the style, and directly set on the TextBox element:

不幸的是,我只能在样式中设置普通前景画笔并直接在 TextBox 元素上设置时才能使用它:

<TextBox Foreground="{StaticResource NormalBrush}" Style="{StaticResource RegularTextBox}" />

If it is set in the style, triggering the MouseOver state throws "Cannot animate '(0).(1)' on an immutable object instance."edit: This also happens if you set the TextBox foreground again after it is initialized.

如果它在样式中设置,则触发 MouseOver 状态会引发“无法在不可变对象实例上设置动画 '(0).(1)'”。编辑:如果在初始化后再次设置 TextBox 前景,也会发生这种情况。