Custom Animation Port to Universal App from WP7 fails with SetTargetProperty - c#

Porting custom animation code from WP7 to store app. WP7 code successfully performed a page flip animation of a border object with a bunch of text boxes on it (that was a page to be flipped.) In the below code Storyboard.SetTargetProperty does not compile complaining that it wants a string:
DoubleAnimation anima = new DoubleAnimation
{
To = pageHostBaseIndex + 1,
Duration = CalculateFractionalDuration(pageHostBaseIndex + 1)
};
Storyboard storyboard = new Storyboard();
Storyboard.SetTarget(anima, this.PageTransition);
Storyboard.SetTargetProperty(anima, new PropertyPath(PageTransition.FractionalBaseIndexProperty));
storyboard.Children.Add(anima);
storyboard.Completed += Storyboard_Completed;
storyboard.Begin();
PageTransition derives from DependencyObject, it contains a DependencyProperty called FractionalBaseIndexProperty.
I tried putting in the string "PageTransition.FractionalBaseIndexProperty" as well as constructing a PropertyPath string. I also tried "(PageTransition).(FractionalBaseIndexProperty)" these all compile but fail with the exception:
No installed components were detected.
Cannot resolve TargetProperty PageTransition.FractionalBaseIndexProperty on specified object.
at Windows.UI.Xaml.Media.Animation.Storyboard.Begin()
I also tried EnableDependentAnimation = true, and making PageTransition derive from Timeline instead of DependencyObeject but these had no effect (same error.)
The eventual animation is a little complex but I don't think it's getting that far. Seems like a Silverlight to Universal difference in objects acceptable for binding to a storyboard or in something with the path. I'll bet there's a more XAML friendly way to do this now but at this point I'm trying to minimize the port and I'd like to keep the feel of the currently animation.
Thoughts?

After hours of tinkering and searching and then reverting some of my previous tinkerings I finally got past this. This thread got me thinking:
Windows 8 - Animating custom property in code-behind.
My border objects were already declared in XAML but my storyboard, animation and PageTransition objs weren’t.
So I added the following XAML in my UserControl.Resources:
<Storyboard x:Name="storyboard">
<DoubleAnimation x:Name="anima">
</DoubleAnimation>
</Storyboard>
<local:FlipTransition x:Key="Foo"></local:FlipTransition>
I used FlipTransition because in my case the PageTransition is an abstract class (so XAML wouldn’t let me instantiate it.) FlipTranstion derives from PageTransition.
Along with that I had to re-create new storyboards and animation objects (see below) with the same names as the ones in the above XAML (I don’t know why the ones I instantiated in XAML wouldn’t work.)
I also had to set EnableDependentAnimation = true
And last, the Path in SetTargetProperty had to change to PageTransition. FractionalBaseIndex (instead of the original PageTransition.FractionalBaseIndexProperty.)
DoubleAnimation anima = new DoubleAnimation
{
To = pageHostBaseIndex + 1,
Duration = CalculateFractionalDuration(pageHostBaseIndex + 1),
EnableDependentAnimation = true
};
Storyboard storyboard = new Storyboard();
Storyboard.SetTarget(anima, this.PageTransition);
Storyboard.SetTargetProperty(anima, "PageTransition.FractionalBaseIndex");
When all these came together it worked.

Related

WPF Animate Grid.VisibilityProperty in code

What animation class would allow me to change the Visibility (not opacity) of a Grid object with a Storyboard instance in code (not XAML)?
So that I can set the to, from, and duration properties before adding it to the storyboard.
You can use an ObjectAnimationUsingKeyFrames with some DiscreteObjectKeyFrame.
You can find an example here. The only work to do is translating that to C# code. (Which shouldn't be a huge problem.)
This is the code necessary to animate the visibility.
DiscreteObjectKeyFrame dk;
ObjectAnimationUsingKeyFrames ok;
ok = new ObjectAnimationUsingKeyFrames();
dk = new DiscreteObjectKeyFrame();
Storyboard.SetTarget(ok, myGrid);
Storyboard.SetTargetProperty(ok, new PropertyPath(Grid.VisibilityProperty));
dk.KeyTime = TimeSpan.FromSeconds(0.1);
dk.Value = Visibility.Hidden;
ok.KeyFrames.Add(dk);
sb.Children.Add(ok);

Why does my Storyboard only run once?

I have a storyboard that I would like to run multiple times. Unfortunately, after it runs the first time, even when this code is hit, it doesn't animate again. It only animates the first time. I'm setting an initial value....even when I set the value before I run it again.
DoubleAnimationUsingKeyFrames animateTransformX = new DoubleAnimationUsingKeyFrames();
EasingDoubleKeyFrame initialTransformXKeyframe = new EasingDoubleKeyFrame();
initialTransformXKeyframe.KeyTime = TimeSpan.FromSeconds(0);
//Resetting the value before running it again.
initialTransformXKeyframe.Value = 0;
animateTransformX.KeyFrames.Add(initialTransformXKeyframe);
EasingDoubleKeyFrame animateTransformXKeyframe = new EasingDoubleKeyFrame();
animateTransformXKeyframe.KeyTime = TimeSpan.FromSeconds(0.5);
animateTransformXKeyframe.Value = aDynamicValueGoesHere;
animateTransformX.KeyFrames.Add(animateTransformXKeyframe);
Storyboard.SetTarget(animateTransformX, image);
Storyboard.SetTargetProperty(animateTransformX, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)"));
Storyboard myStoryboard = new Storyboard();
myStoryboard.Children.Add(animateTransformX);
myStoryboard.Begin(this);
I feel like it is very simple but for the life of me I can't understand why. Any help would be appreciated.
EDIT
The reason I was doing it from codebehind is because storyboards are freezable and I have had trouble in the past with dynamic values in them. For simplicity I didn't put that I was using a dynamic value in the code; I updated the code above to show I'm using a dynamic value.
I tried adding the storyboard as XAML inside a resource with the use of a dynamic value. It works fine (and replays) if a non-dynamic value is used but it only plays once (the first time) when a dynamic value is used.
So, if {Binding OffsetX} were replaced with 50, for example, this works fine, and repeatedly.OffsetX gets updated through a property that implements INotifyPropertyChanged. Even if it were the same, if it were never updated, I would think it should still animate, just to the same value it animated to before (like it does with 50). But that isn't the case; it just doesn't animate again. So now I'm really lost.
<Storyboard x:Key="InitialAnimate">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)" Storyboard.TargetName="StoryImage">
<EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="{Binding OffsetX}"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
EDIT 2
I didn't fix the problem but I found a workaround that works for my particular situation. I was getting this error in my output window:
System.Windows.Media.Animation Warning: 6 : Unable to perform action because the specified Storyboard was never applied to this object for interactive control.; Action='Stop'; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='28715394'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='6255521'; TargetElement.Type='System.Windows.Controls.Grid'
I found a few StackOverflow answers regarding it, including how to find what Storyboard caused the issue and another unanswered question.
I am still not sure how to fix my original issue, but here's my workaround. Since I only needed the animation to run when you restarted (an event that happens in my viewmodel), I use an event raised in the viewmodel and consumed in the parent view to remove and then re-add the UserControl that contains the animation.
In that UserControl, I trigger the storyboard through a ControlStoryboardAction tied to a Loaded EventTrigger. This makes the animation run when it loads, and it only ever runs the animation once during the life of the UserControl. I am able to use the dynamic values in XAML. Here's how I'm removing and readding the view from it's parent:
Parent View:
<grid x:Name="imageHolder></grid>
Parent Codebehind:
private void CreateAnimations(object sender, EventArgs e)
{
imageHolder.Children.RemoveAt(0);
Views.Image image = new Image();
image.Name = "image";
imageHolder.Children.Add(image);
}
You can't change the Value property at runtime the way you're doing after the edit.
This is because Animations are freezable objects. There is more
information in the MSDN Documentation, but basically it means you
can't use binding because properties in the frozen object (i.e. the
animation) cannot change.
To get around this limitation, you will need to do some or all of the
work in code-behind.
See Why Storyboard value wouldn't get from a resource or binding?
So You probably want to go back to the codebehind method.
So as a final answer, what I would do is when I want to run it again:
Remove the previous storyboard
create a whole brand new Storyboard(or just the animation) and run that.

How to set a transition property using Transitionals library

I am trying to incorporate the Transitionals library into my window.
I am going to use two transitions:
If button one is pressed the transition is going to be the ExplosionTransition.
If button two is pressed the transition is going to be the RotateTransition to the left.
If button two is pressed again the transition is going to be the RotateTransition to the right.
XAML:
<transc:TransitionElement x:Name="TransitionBox" Transition="{Binding TransitionToUse}" />
ViewModel:
private Transition explosion_transition = new ExplosionTransition();
private Transition rotate_transition = new RotateTransition();
What I am wondering is how do you set the direction property for rotate_direction?
Initialize like so:
private RotateTransition rotate_transition = new RotateTransition();
instead of in the question places it into a Transition object. Then you can access the individual transitions properties, like so:
rotate_transition.Direction = RotateDirection.Right;
TransitionToUse = rotate_transition;

How to complete an animation instantaneously if it has not finished?

I have started an Animation in a method and later I will have to stop it and complete it immediately if it has not completed then.
Vector diff = ...
DoubleAnimation aniX = new DoubleAnimation(diff.X, TimeSpan.FromSeconds(0.4));
DoubleAnimation aniY = new DoubleAnimation(diff.Y, TimeSpan.FromSeconds(0.4));
aniX and aniY to be stopped, if they have not completed.
I think you should use XAML code with some attached Trigger to stop it. To stop the animation programmatically. You can try using Begin to begin the Storyboard and Stop to stop it (don't use BeginAnimation):
Storyboard st = new Storyboard();
Storyboard.SetTarget(aniX, yourObject);
Storyboard.SetTargetProperty(aniX, yourPropertyPath);
st.Children.Add(aniX);
Storyboard.SetTarget(aniY, yourObject);
Storyboard.SetTargetProperty(aniY, yourPropertyPath);
st.Children.Add(aniY);
//begin
st.Begin();
//stop
st.Stop();
NOTE: The code above supposes you declare all the DoubleAnimations and Storyboard in procedural code (not in XAML code). I think you should declare them in XAML code to make it more concise, many class and stuff in WPF are optimized for XAML, so sometimes you can feel it's a little weird to look them in procedural code.

Storyboard.begin() throws an InvalidOperationException

I am not sure what is going on here. I am following the example provided by Microsoft. Everything is done on the back end because I need to decide if you user should enter stuff into a text field or should the text field value be displayed as normal text. The code is as follows:
nameInput.Name = "inputName";
nameInput.Text = "Journey Name";
nameInput.KeyUp += onNameInput;
ColorAnimation animation = new ColorAnimation();
animation.From = Colors.Blue;
animation.To = Colors.White;
animation.Duration = new Duration(TimeSpan.FromMilliseconds(100));
animation.RepeatBehavior = RepeatBehavior.Forever;
Storyboard.SetTarget(animation, nameInput);
Storyboard.SetTargetProperty(animation, new PropertyPath(TextBlock.ForegroundProperty));
storyBoard.Children.Add(animation);
journeyStackPanel.Children.Add(nameInput);
ClockState state = storyBoard.GetCurrentState();
storyBoard.Begin(); //<---Crashes here
I am following the
http://msdn.microsoft.com/en-us/library/cc672995(v=vs.95).aspx
example. I am not sure what is going on, unfortunately the debugger does not spit out any more information. Maybe I am missing a step? I am sorry that I am being a little vague but this is all the information I have on the issue.
Any help is greatly appreciated!!
I was able to replicate this problem on the latest WP8 SDK, with the following error message generated:
ColorAnimation cannot be used to animate property Foreground due to
incompatible type.
I believe this is because you're trying to change the Foreground property of the TextBox to a Color object, but Foreground is actually a Brush object, hence the Type Mismatch error. Instead, you have to change the Color property of the Foreground object.
Try this instead:
Storyboard.SetTargetProperty(animation, new PropertyPath("(Foreground).(Color)"));

Categories