wpfxamlanimationcoloranimation

How to animate SolidColorBrush resource?


I'm trying to animate SolidColorBrush, which is the resource of my custom control. This brush is used as the fill for five rectangles.

While on design time, all works as expected, but at run time the app closes immediately with System.InvalidOperationException, pointing that the brush's name couldn't be found.

I started a sample project, having:

<SolidColorBrush x:Name="rectBrush" x:Key="rectangleBrush" Color="#b266b2" />
<Grid>
    <StackPanel
        Orientation="Horizontal"
        VerticalAlignment="Center"
        HorizontalAlignment="Center">
        <Rectangle
            Fill="{StaticResource rectangleBrush}" />
        <Rectangle
            Fill="{StaticResource rectangleBrush}" />
        <Rectangle
            Fill="{StaticResource rectangleBrush}" />
        <Rectangle
            Fill="{StaticResource rectangleBrush}" />
        <Rectangle
            Fill="{StaticResource rectangleBrush}" />
    </StackPanel>
</Grid>

I'm using Blend for Visual Studio to see if the storyboard is correct and working.

On launch I get:

System.InvalidOperationException: ''rectBrush' name cannot be found in the name scope of 'AnimationExample.MainWindow'

Complete XAML markup:

<Window x:Class="AnimationExample.MainWindow"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="200" d:DesignWidth="200">
    <Window.Resources>
        <SolidColorBrush x:Name="rectBrush" x:Key="rectangleBrush" Color="#b266b2" />
        <Style TargetType="Rectangle">
            <Setter Property="Width" Value="6" />
            <Setter Property="Height" Value="6" />
            <Setter Property="Margin" Value="1" />
            <Setter Property="HorizontalAlignment" Value="Left" />
            <Setter Property="VerticalAlignment" Value="Bottom" />
            <Setter Property="RadiusX" Value="3" />
            <Setter Property="RadiusY" Value="3" />
        </Style>
        <Storyboard 
            x:Key="NowPlayingAnimation"
            RepeatBehavior="Forever"
            AutoReverse="True">
            <ColorAnimation
                Storyboard.TargetName="rectBrush"
                Storyboard.TargetProperty="Color"
                From="#b266b2"
                To="#6666ff"
                Duration="0:0:1" />
            <ColorAnimation
                Storyboard.TargetName="rectBrush"
                Storyboard.TargetProperty="Color"
                From="#6666ff"
                To="#66b266"
                Duration="0:0:1"
                BeginTime="0:0:1"/>
            <ColorAnimation
                Storyboard.TargetName="rectBrush"
                Storyboard.TargetProperty="Color"
                From="#66b266"
                To="#ffff66"
                Duration="0:0:1"
                BeginTime="0:0:2"/>
            <ColorAnimation
                Storyboard.TargetName="rectBrush"
                Storyboard.TargetProperty="Color"
                From="#ffff66"
                To="#ffc966"
                Duration="0:0:1"
                BeginTime="0:0:3" />
            <ColorAnimation
                Storyboard.TargetName="rectBrush"
                Storyboard.TargetProperty="Color"
                From="#ffc966"
                To="#ff4c4c"
                Duration="0:0:1"
                BeginTime="0:0:4" />
        </Storyboard>
    </Window.Resources>
    <Window.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
            <BeginStoryboard Storyboard="{StaticResource NowPlayingAnimation}"/>
        </EventTrigger>
    </Window.Triggers>
    <Grid>
        <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
            <Rectangle Fill="{StaticResource rectangleBrush}" />
            <Rectangle Fill="{StaticResource rectangleBrush}" />
            <Rectangle Fill="{StaticResource rectangleBrush}" />
            <Rectangle Fill="{StaticResource rectangleBrush}" />
            <Rectangle Fill="{StaticResource rectangleBrush}" />
        </StackPanel>
    </Grid>
</Window>

I have two questions:

  1. Why on design time all works fine and I'm able to see what I actually would like to get?

  2. Is there a way to achieve this?

    • completely in XAML;
    • without animating each rectangle's fill individually?

Thank you in advance.


Solution

  • You've got two options:

    1. Put the animation in the Style, and directly animate Background.Color.
    2. Put the brush someplace where it, or its parent, is in the visual tree where a name can be resolved.

    Version 2:

    <Window.Resources>
        <SolidColorBrush x:Name="rectBrush" x:Key="rectangleBrush" Color="#b266b2" />
        <Style TargetType="Rectangle">
            <Setter Property="Width" Value="6" />
            <Setter Property="Height" Value="6" />
            <Setter Property="Margin" Value="1" />
            <Setter Property="HorizontalAlignment" Value="Left" />
            <Setter Property="VerticalAlignment" Value="Bottom" />
            <Setter Property="RadiusX" Value="3" />
            <Setter Property="RadiusY" Value="3" />
        </Style>
        <Storyboard 
            x:Key="NowPlayingAnimation"
            RepeatBehavior="Forever"
            AutoReverse="True">
            <ColorAnimation
                Storyboard.TargetName="BrushCarrier"
                Storyboard.TargetProperty="Tag.Color"
                From="#b266b2"
                To="#6666ff"
                Duration="0:0:1" />
            <ColorAnimation
                Storyboard.TargetName="BrushCarrier"
                Storyboard.TargetProperty="Tag.Color"
                From="#6666ff"
                To="#66b266"
                Duration="0:0:1"
                BeginTime="0:0:1"/>
            <ColorAnimation
                Storyboard.TargetName="BrushCarrier"
                Storyboard.TargetProperty="Tag.Color"
                From="#66b266"
                To="#ffff66"
                Duration="0:0:1"
                BeginTime="0:0:2"/>
            <ColorAnimation
                Storyboard.TargetName="BrushCarrier"
                Storyboard.TargetProperty="Tag.Color"
                From="#ffff66"
                To="#ffc966"
                Duration="0:0:1"
                BeginTime="0:0:3" />
            <ColorAnimation
                Storyboard.TargetName="BrushCarrier"
                Storyboard.TargetProperty="Tag.Color"
                From="#ffc966"
                To="#ff4c4c"
                Duration="0:0:1"
                BeginTime="0:0:4" />
        </Storyboard>
    </Window.Resources>
    <Window.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
            <BeginStoryboard Storyboard="{StaticResource NowPlayingAnimation}"/>
        </EventTrigger>
    </Window.Triggers>
    <Grid>
        <FrameworkElement
            x:Name="BrushCarrier"
            Tag="{StaticResource rectangleBrush}"
            />
        <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
            <Rectangle Fill="{StaticResource rectangleBrush}" />
            <Rectangle Fill="{StaticResource rectangleBrush}" />
            <Rectangle Fill="{StaticResource rectangleBrush}" />
            <Rectangle Fill="{StaticResource rectangleBrush}" />
            <Rectangle Fill="{StaticResource rectangleBrush}" />
        </StackPanel>
    </Grid>
    

    Version 1. Here, the storyboard isn't defined as a resource, and you don't start it when the window is loaded. Each Rectangle takes care of its own animation.

    <Style TargetType="Rectangle" x:Key="ColorShiftRectangle">
        <Setter Property="Width" Value="6" />
        <Setter Property="Height" Value="6" />
        <Setter Property="Margin" Value="1" />
        <Setter Property="HorizontalAlignment" Value="Left" />
        <Setter Property="VerticalAlignment" Value="Bottom" />
        <Setter Property="RadiusX" Value="3" />
        <Setter Property="RadiusY" Value="3" />
        <Setter Property="Fill" Value="#b266b2" />
        <Style.Triggers>
            <EventTrigger RoutedEvent="Loaded">
                <BeginStoryboard>
                    <Storyboard 
                        RepeatBehavior="Forever"
                        AutoReverse="True">
                        <ColorAnimation
                            Storyboard.TargetProperty="Fill.Color"
                            From="#b266b2"
                            To="#6666ff"
                            Duration="0:0:1" />
                        <ColorAnimation
                            Storyboard.TargetProperty="Fill.Color"
                            From="#6666ff"
                            To="#66b266"
                            Duration="0:0:1"
                            BeginTime="0:0:1"/>
                        <ColorAnimation
                            Storyboard.TargetProperty="Fill.Color"
                            From="#66b266"
                            To="#ffff66"
                            Duration="0:0:1"
                            BeginTime="0:0:2"/>
                        <ColorAnimation
                            Storyboard.TargetProperty="Fill.Color"
                            From="#ffff66"
                            To="#ffc966"
                            Duration="0:0:1"
                            BeginTime="0:0:3" />
                        <ColorAnimation
                            Storyboard.TargetProperty="Fill.Color"
                            From="#ffc966"
                            To="#ff4c4c"
                            Duration="0:0:1"
                            BeginTime="0:0:4" />
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
    
        </Style.Triggers>
    </Style>
    

    Usage:

        <Rectangle Style="{StaticResource ColorShiftRectangle}" />