3

While it is it easy to one-off background change and it already has many answers, how can I consistently animate color changing (e.g. not once, not to single color).
To give a toy example of the problem, lets say I have a Rectangle and three RadioButtons named "Red", "Blue" and "Green", how can I animate Rectangle's Fill color so that it is corresponding to checked radio button (e.g. whenever you click "Blue" and rectangle turns Blue)?

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

The problem I am facing right now is, whenever there is more than one animation attached to the Rectangle's Fill color, it fails miserably in various ways due to animation precedence, for example if there are multiple triggers for "Red", "Green" and "Blue":

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

and you click "Green" while Fill is Blue, it will stay Blue (because "ToBlue" was defined below "ToGreen"). Worse yet if you attempt to remove "ToBlue" beforehand:

<RemoveStoryboard BeginStoryboardName="ToBlue"/>

it will animate from the default color to Green rather than from Blue to Green.

wondra
  • 3,271
  • 3
  • 29
  • 48
  • Bonus points: it would work continuously even if target color is changed while animation is in progress. – wondra Aug 16 '19 at 09:20
  • @Frenchy your suggested solution with 'push' semantics from RadioButtons looks valid to me (not me voting). Though undoubtebly `VisualStateManager` is more elegant one because it allows easier bindings. – wondra Aug 16 '19 at 13:06

1 Answers1

1

Yeah the problem is .... it IS a "BUG". Triggers are old method of "styling" elements. The new method is using VisualStates

an example would be:

NOTE: this example will not work for you.

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

and then for changing the state you can use a bit c#

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

Or if it's too ugly, you can make a State Behavior

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);
      }
}

and use it like this

<YOUR_CONTROL local:StateBehavior.State="{Binding Path=YOUR_STATE_DATA, Mode=TwoWay}" />

BONUS

you can use Transitions for some effects (Ik its off-topic)

PS: Sorry for not showing how to solve your problem. (I'm just too lazy for switching to Windows, I'm on Linux currently)

Zer0
  • 1,146
  • 6
  • 15
  • Nice, works flawlessly but most importantly consistently. In addition, even the 'basic' example fulfills the bonus point - animation is perfectly fluid. Just a note to implementors: you will most likely need need to define new instance `` for the Rectangle otherwise the app crashes on some cryptic exception. – wondra Aug 16 '19 at 13:00