WPF如何正确地动态改变颜色

3

虽然单次更改背景很容易,而且已经有许多答案(链接),但我如何使颜色动画变化保持一致(例如不只一次,也不是只到单一颜色)?

为了给出一个玩具示例,假设我有一个名为“红色”、“蓝色”和“绿色”的RadioButton,并拥有一个Rectangle。 我该如何使Rectangle填充色的动画对应选中的单选按钮(例如,每当您点击“蓝色”时,Rectangle变成蓝色)?

<StackPanel HorizontalAlignment="Left">
    <Rectangle Width="50" Height="50" Style="{StaticResource ColorChangingStyle}"/>
    <RadioButton Name="Red" Content="Red" GroupName="Group1" IsChecked="True"/>
    <RadioButton Name="Green" Content="Green" GroupName="Group1"/>
    <RadioButton Name="Blue" Content="Blue" GroupName="Group1"/>
</StackPanel>

我现在面临的问题是,每当有多个动画附加到“矩形”填充颜色时,由于动画优先级,它会以各种方式惨败,例如如果有多个“红色”,“绿色”和“蓝色”的触发器:
<DataTrigger Binding="{Binding IsChecked, ElementName=Blue}" Value="True">
    <DataTrigger.EnterActions>
        <BeginStoryboard Name="ToBlue">
            <Storyboard>
                <ColorAnimation Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)" To="Blue" />
            </Storyboard>
        </BeginStoryboard>
    </DataTrigger.EnterActions>
</DataTrigger>

如果在“填充”为蓝色的情况下点击“绿色”,它将保持蓝色(因为“转换为蓝色”在“转换为绿色”之后定义)。更糟糕的是,如果您先尝试删除“转换为蓝色”:
<RemoveStoryboard BeginStoryboardName="ToBlue"/>

它将从默认颜色动画到绿色,而不是从蓝色到绿色。


加分项:即使在动画进行时目标颜色发生变化,它也能持续工作。 - wondra
@Frenchy,你在RadioButtons中提出的“push”语义的解决方案我认为是有效的(不是我投票)。 不过毫无疑问,“VisualStateManager”更加优美,因为它允许更容易的绑定。 - wondra
1个回答

1

是的,问题确实是一个“BUG”。触发器是“样式”元素的旧方法。新方法是使用VisualStates

例如:

注意:此示例对您无效。

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup Name="ColorStates">

      <VisualState Name="ToGreen">
        <Storyboard>
          <ColorAnimation To="Green" 
                          Storyboard.TargetProperty="Fill"/>
        </Storyboard>
      </VisualState>

      <VisualState Name="ToBlue">
        <Storyboard>
          <ColorAnimation To="Blue" 
                          Storyboard.TargetProperty="Fill"/>
        </Storyboard>
      </VisualState>

      <VisualState Name="ToRed">
        <Storyboard>
          <ColorAnimation To="Red" 
                          Storyboard.TargetProperty="Fill"/>
        </Storyboard>
      </VisualState>

    </VisualStateGroup>
  </VisualStateManager.VisualStateGroups>

然后,要改变状态,您可以使用一点点C#。

 VisualStateManager.GoToState(YOUR_CONTROL, "YOUR_STATE", true);

如果太丑的话,您可以创建一个状态行为
public class StateBehavior 
{
      public static readonly DependencyProperty StateProperty =
          DependencyProperty.RegisterAttached("State", 
                                              typeof(string), 
                                              typeof(StateBehavior),
                                              new UIPropertyMetadata(null, StateChanged));

      internal static void StateChanged(DependencyObject target, DependencyPropertyChangedEventArgs args) 
      {
          if (args.NewValue != null)
             VisualStateManager.GoToState((FrameworkElement)target, args.NewValue, true);
      }
}

并像这样使用它。
<YOUR_CONTROL local:StateBehavior.State="{Binding Path=YOUR_STATE_DATA, Mode=TwoWay}" />

奖励

你可以使用转场效果来实现一些特效(我知道这不是主题)

PS:很抱歉没有展示如何解决你的问题。(我只是懒得切换到Windows,目前在使用Linux)


不错,运行无瑕疵,但最重要的是始终如一。此外,即使是“基本”示例也满足了奖励点——动画完美流畅。对于实现者的一个提示:您很可能需要为矩形定义新的实例<SolidColorBrush/>,否则应用程序会崩溃并显示一些神秘的异常。 - wondra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接