Kieren Johnstone
Kieren Johnstone

Reputation: 41983

Storyboards with bound properties (custom control: animate colour change)

To put it simply, I have this within a ControlTemplate.Triggers condition EnterAction:

<ColorAnimation To="#fe7" Storyboard.TargetProperty="Background.Color" Duration="00:00:00.1" Storyboard.TargetName="brd"/>

But I want the 'to' colour (#fe7) to be customisable. This is a control derived from ListBox. I can create a DependencyProperty, but of course, I cannot bind the To property of the ColorAnimation to it because the Storyboard has to be frozen and you can't freeze something with bindings (as I understand it).

I tried using a {StaticResource} within the To, then populating the resource in the code-behind when the DependencyProperty was changed, by setting this.Resources["ItemColour"] = newValue; for instance. That didn't work perhaps obviously, it's a static resource after all: no new property values were picked up. DynamicResource gave the same problem relating to inability to freeze.

The property is only set once when the control is created, I don't have to worry about it changing mid-animation.

Is there a nice way of doing this? Do I have to resort to looking for property changes myself, dynamically invoking and managing storyboards at that point? Or overlaying two versions of the control, start and end colour, and animating Opacity instead? Both seem ludicrous..

Upvotes: 1

Views: 816

Answers (4)

Kieren Johnstone
Kieren Johnstone

Reputation: 41983

It turns out this is simply not possible.

Upvotes: 0

TCM
TCM

Reputation: 16900

Kieren,

Will this serve your purpose?

I have extended the Grid class called CustomGrid and created a TestProperty whose value when changed will change the background color of Grid:

 public class CustomGrid : Grid
    {
        public bool Test
        {
            get
            {
                return (bool)GetValue(TestProperty);
            }
            set
            {
                SetValue(TestProperty, value);
            }
        }
        public static readonly DependencyProperty TestProperty =
            DependencyProperty.Register("Test", typeof(bool), typeof(CustomGrid),
                new PropertyMetadata(new PropertyChangedCallback
                    ((obj, propChanged) =>
                    {

                        CustomGrid control = obj as CustomGrid;
                        if (control != null)
                        {
                            Storyboard sb = new Storyboard() { Duration = new Duration(TimeSpan.FromMilliseconds(500)) };

                            Random rand = new Random();
                            Color col = new Color()
                            {
                                A = 100,
                                R = (byte)(rand.Next() % 255),
                                G = (byte)(rand.Next() % 255),
                                B = (byte)(rand.Next() % 255)
                            };


                            ColorAnimation colAnim = new ColorAnimation();

                            colAnim.To = col;
                            colAnim.Duration = new Duration(TimeSpan.FromMilliseconds(500));


                            sb.Children.Add(colAnim);
                            Storyboard.SetTarget(colAnim, control);
                            Storyboard.SetTargetProperty(colAnim, new PropertyPath("(Panel.Background).(SolidColorBrush.Color)"));

                            sb.Begin();

                        }
                    }
                )));
    }

This is the button click event that changes the color:

 private void btnClick_Click(object sender, RoutedEventArgs e)
        {
            gridCustom.Test = (gridCustom.Test == true) ? false : true;
        }

I am changing the background color of Grid because I don't have your Listbox.

Finally this is the xaml:

 <Grid x:Name="grid" Background="White">
        <local:CustomGrid x:Name="gridCustom" Background="Pink" Height="100" Margin="104,109,112,102" >

        </local:CustomGrid>
        <Button Content="Click Me" x:Name="btnClick" Height="45" HorizontalAlignment="Left" Margin="104,12,0,0"  VerticalAlignment="Top" Width="145" Click="btnClick_Click" />
    </Grid>

Will this serve your purpose? Let me know or I misunderstood the question?

EDIT:

See this code:

ColorAnimation's To property cannot be bound as you probably guessed. But that doesn't mean you can't change it's value. You can always get a reference to the ColorAnimation and change it's To value and it will all work out well. So from WPF world of binding we need to change a bit and bind the data how we used to do it in Winforms :). As an example see this:

This is the xaml:

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero" x:Class="ControlTemplateTriggers.MainWindow"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>

        <Storyboard x:Key="Storyboard">
            <ColorAnimation From="Black" To="Red" Duration="00:00:00.500" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="gridCustom" />
        </Storyboard>
    </Window.Resources>

    <Grid x:Name="grid" Background="White">
        <Grid x:Name="gridCustom" Background="Pink" Height="100" Margin="104,109,112,102" />

        <Button Content="Click Me" x:Name="btnClick" Height="45" HorizontalAlignment="Left" Margin="104,12,0,0"  VerticalAlignment="Top" Width="145" Click="btnClick_Click" />
    </Grid>
</Window>

This is the code behind:

using System.Windows;
using System.Windows.Media.Animation;
using System.Windows.Media;
using System;

namespace Sample    {

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;

        }

        private void btnClick_Click(object sender, RoutedEventArgs e)
        {
            Storyboard sb = this.Resources["Storyboard"] as Storyboard;
            if (sb != null)
            {
                ColorAnimation frame = sb.Children[0] as ColorAnimation;
                Random rand = new Random();
                Color col = new Color()
                              {
                                  A = 100,
                                  R = (byte)(rand.Next() % 255),
                                  G = (byte)(rand.Next() % 255),
                                  B = (byte)(rand.Next() % 255)
                              };
                frame.To = col;
                sb.Begin();
            }
        }
    }


}

As you can see I am getting a reference to the storyboard and changing it's To property. Your approach to StaticResource obviously wouldn't work. Now what you can do is, in your DependencyProperty callback somehow get a reference to the Timeline that you want to animate and using VisualTreeHelper or something and then set it's To property.

This is your best bet.

Let me know if this solved your issue :)

Upvotes: 2

Learner
Learner

Reputation: 1542

Surely not.. What i understood is that u want color A on the Condition A and Color B on some other condition B....so if there's a property with multiple options u can put datatriggers for those condition only...like if Job done = Red, Half done = Green like wise.. If i misunderstood the problem please correct me..

I think i got ur question ...UR control is user configurable so what ever user select , control's background needs to be set to that color with animation right?

Upvotes: 0

Learner
Learner

Reputation: 1542

can u put multiple DataTriggers with each having respective color for the "To" property...

Upvotes: 0

Related Questions