5

I'm messing around with some Storyboards in a Metro XAML app. I have to create a Storyboard in code. I'd like to set the Storyboard.TargetProperty to CompositeTransform.Rotation

It seems impossible...

My Storyboard in XAML looks like this:

<Storyboard>
    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" Storyboard.TargetName="grid">
        <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="60"/
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

I'd like to create something similar.
Important: I am not trying to recreate this exact Storyboard. I am inside the code of a custom ContentControl, so this is the Control, and there's no "grid" to target the animation to. The target is the control itself, which has CompositeTransform previously set.

My code so far is like this:

var turnSB = new Storyboard();

var doubleAnim = new DoubleAnimationUsingKeyFrames();
doubleAnim.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = TimeSpan.FromMilliseconds(0), Value = currentAngle });
doubleAnim.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = TimeSpan.FromMilliseconds(500), Value = targetAngle });

turnSB.Children.Add(doubleAnim);

Storyboard.SetTarget(doubleAnim, this.RenderTransform);
Storyboard.SetTargetProperty(doubleAnim, "(CompositeTransform.Rotation)");

turnSB.Begin();

As soon as it hits the Begin method I get an Exception saying that (CompositeTransform.Rotation) cannot be resolved. So I'm guessing I didn't got the property path quite right. I've tried different variations, but according to PropertyPaths, this should be the correct one, shouldn't it? :S

If this is an unsolvable problem, I'm open to suggestions on a workaround...

EDIT:

I think I have solved the problem for now. I have some interesting findings though...

If I make a UserControl I can do practically anything. Everything works, I can set the Storyboard.Targetproperty, and the animation plays correctly.

However if I use a custom control, or inherit from another control (say ContentControl), I can't start a Storyboard from code, only in some cases.

For example: If I make a storyboard (defined in XAML) to animate Rotation (or any transformation property for that matter) and try to start from code, I get the above exception. But If I animate a simple property, say Opacity, it works fine.
(I did the same with a UserControl, and it worked.)

Can someone explain this?

Tenshiko
  • 1,450
  • 3
  • 17
  • 33
  • I am having the same Problem, having a class inherit from the Grid control, storyboards simply won't start. – sharp johnny Jan 16 '13 at 09:40
  • One thing to note is that Grid is not inherited from Control, so it's not really a custom control you are creating. – Justin XL Jan 18 '13 at 05:27
  • I wasn't using Grid, I was inheriting from ContentControl. The xaml was from a previous try, where I had a UserControl that had a Grid in it, and that was the target of the animation. But as for this situation, the target should be the control itself. Which - if you read carefully - I described in the 'Important' section. – Tenshiko Jan 29 '13 at 16:37
  • Tenshiko, sorry I was referring @sharpjohnny's comment... – Justin XL Jan 30 '13 at 03:39

3 Answers3

4

From the MSDN docs it looks like you need to set the entire string path. So for the animation described in your xaml, you would need to set the TargetProperty as such

Storyboard.SetTargetProperty(doubleAnim, "(UIElement.RenderTransform).(CompositeTransform.Rotation)");

UPDATE: Found this blog post which adds the Timeline as a child of the storyboard. Try the following:

Storyboard.SetTarget(doubleAnim, this.RenderTransform);
Storyboard.SetTargetProperty(doubleAnim, "Rotation"); // maybe "CompositeTransform.Rotation"
storyboard.Children.Add(doubleAnim);
Shawn Kendrot
  • 12,425
  • 1
  • 25
  • 41
  • I've tried every combination of these. Results were always the same. – Tenshiko Aug 18 '12 at 19:18
  • I've come to the conclusion, that the problem is not with the Storyboard setting. In fact your first suggestion works now, but only with UserControls. I'd like to know more about the subject, maybe someone can shed some light on it. But if not, I'll accept your answer. Thx :) – Tenshiko Aug 18 '12 at 19:36
  • So this is within a custom control? – Shawn Kendrot Aug 19 '12 at 04:48
  • Actually I first made a control that inherited from ContentControl. It didn't work. Then I changed it to custom Control. Didn't work either. Now I'm using a UserControl, and it works. – Tenshiko Aug 21 '12 at 07:50
4

I think the reason that you get this error is because you didn't instantiate the RenderTransform property of the custom control.

public class CustomControl2 : Control
{
    public CustomControl2()
    {
        this.DefaultStyleKey = typeof(CustomControl2);
    }

    protected override void OnApplyTemplate()
    {
        base.OnApplyTemplate(); 
    }

    public void RunAnimation()
    {
        //this.RenderTransform = new CompositeTransform();
        this.Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x33, 0xC8, 0x9C));

        var turnSB = new Storyboard();

        var doubleAnim = new DoubleAnimationUsingKeyFrames();
        doubleAnim.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = TimeSpan.FromMilliseconds(0), Value = 10 });
        doubleAnim.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = TimeSpan.FromMilliseconds(500), Value = 30 });

        turnSB.Children.Add(doubleAnim);

        Storyboard.SetTarget(doubleAnim, this.RenderTransform);
        Storyboard.SetTargetProperty(doubleAnim, "(CompositeTransform.Rotation)");

        turnSB.Begin();
    }
}

Note in the code above, if I comment out the first line under the method RunAnimation, it will throw me the same error you are getting.

I then created this control in my main page, and also created a Button to kick off the animation.

private void Button_Click_1(object sender, RoutedEventArgs e)
{
    this.MyControl.RunAnimation();
}

I tested above code and it worked fine.

Justin XL
  • 38,763
  • 7
  • 88
  • 133
  • I see why this should be working, but as I said in my original post I DID set up the Transformation (please read 'Important' section again). And what more, I was able to reproduce the same effect with a simpler solution. Let me check it again and then get back to you. – Tenshiko Jan 29 '13 at 16:30
  • One thing I can think of is, maybe you start the anmiation before `OnApplyTemplate` is called? – Justin XL Jan 30 '13 at 03:49
1

SOLVED IT

The problem is in the path to the element youre using, its has to derive from the parent class its extending to the property itself. I have it working in my own control so made a small example you can copy paste(UNTESTED CODE):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;

namespace CustomControls
{
    /// <summary>
    /// Author: Frank Wolferink
    /// Note: example is untested and comes as is. I hope it save's you the time i lost figering this out
    /// </summary>
    public class CustomControl : Control
    {

        private Storyboard _compositeTransformExampleStoryBoard;

        private const string TRANSLATE_X_TARGET = "Control.RenderTransform.CompositeTransform.TranslateX";
        private const string TRANSLATE_Y_TARGET = "Control.RenderTransform.CompositeTransform.TranslateY";
        private const string TRANSLATE_ROTATE_TARGET = "Control.RenderTransform.CompositeTransform.Rotation";


        public CustomControl()
        {
            this.RenderTransform = new CompositeTransform();

            TimeSpan duration = new TimeSpan(0,0,0,0,500);
            double translateX = 10;
            double translateY = 10;
            double rotation = 40;

            _compositeTransformExampleStoryBoard = BuildStoryboard(duration, translateX, translateY, rotation);

            this.Loaded += CustomControl_Loaded;
        }

        void CustomControl_Loaded(object sender, RoutedEventArgs e)
        {
            _compositeTransformExampleStoryBoard.Begin();
        }


        private Storyboard BuildStoryboard(TimeSpan animationDuration, double transistionValueX, double transistionValueY, double rotation)
        {
            Storyboard storyboard = new Storyboard();

            if (transistionValueX != 0)
                CreateAnimation(storyboard, transistionValueX, animationDuration, TRANSLATE_X_TARGET);

            if (transistionValueY != 0)
                CreateAnimation(storyboard, transistionValueY, animationDuration, TRANSLATE_Y_TARGET);

            if (rotation != 0)
                CreateAnimation(storyboard, rotation, animationDuration, TRANSLATE_ROTATE_TARGET);


            return storyboard;
        }

        private void CreateAnimation(Storyboard storyboard, double transistionValue, TimeSpan animationDuration, string targetProperty)
        {
            DoubleAnimation da = CreateDoubleAnimation(transistionValue, animationDuration);
            storyboard.Children.Add(da);
            Storyboard.SetTarget(da, this);
            Storyboard.SetTargetProperty(da, targetProperty);
        }

        private DoubleAnimation CreateDoubleAnimation(double transistionValue, TimeSpan duration)
        {
            return new DoubleAnimation()
            {
                Duration = duration,
                To = transistionValue
            };
        }

    }
}

Frankie
  • 81
  • 1
  • 3