Microsoft Surface: When is Storyboard#completed called? - c#

I have the following code:
private void Package_ContactDown(object sender, ContactEventArgs e)
{
ScatterViewItem svi = new ScatterViewItem();
svi.Orientation = 0;
removeShadow(svi);
svi.IsActive = true;
PackageView view = new PackageView(sourceFile, this);
view.setScatterViewItem(svi);
svi.Width = 1024;
svi.Height = 768;
svi.Center = new Point(512, 384);
Viewbox box = new Viewbox();
box.Name = "box";
box.Child = view;
this.RegisterName(box.Name, box);
Viewbox boxSmall = new Viewbox();
boxSmall.Name = "boxSmall";
this.RegisterName(boxSmall.Name, boxSmall);
TextBlock txt = new TextBlock();
txt.Foreground = Brushes.White;
txt.Text = "Package of class";
boxSmall.Child = txt;
boxSmall.Opacity = 0;
boxSmall.IsHitTestVisible = false;
Rectangle border = new Rectangle();
border.Name = "border";
this.RegisterName(border.Name, border);
border.Fill = Brushes.Transparent;
border.Stroke = Brushes.White;
border.StrokeThickness = 2;
border.Opacity = 0;
Grid g = new Grid();
g.Background = this.FindResource("WindowBackground") as ImageBrush;
g.Children.Add(box);
g.Children.Add(boxSmall);
g.Children.Add(border);
svi.Content = g;
window.IconDisplay.Items.Add(svi);
DoubleAnimation animation = new DoubleAnimation();
animation.From = 0.0;
animation.To = 1.0;
animation.Duration = new Duration(TimeSpan.FromSeconds(3));
animation.AutoReverse = false;
Storyboard storyboard = new Storyboard();
storyboard.Children.Add(animation);
Storyboard.SetTargetName(animation, boxSmall.Name);
Storyboard.SetTargetProperty(animation, new PropertyPath(Viewbox.OpacityProperty));
DoubleAnimation animation2 = new DoubleAnimation();
animation2.From = 1.0;
animation2.To = 0.0;
animation2.Duration = new Duration(TimeSpan.FromSeconds(3));
animation2.AutoReverse = false;
Storyboard storyboard2 = new Storyboard();
storyboard2.Children.Add(animation2);
Storyboard.SetTargetName(animation2, box.Name);
Storyboard.SetTargetProperty(animation2, new PropertyPath(Viewbox.OpacityProperty));
DoubleAnimation animation3 = new DoubleAnimation();
animation3.From = 0.0;
animation3.To = 1.0;
animation3.Duration = new Duration(TimeSpan.FromSeconds(3));
animation3.AutoReverse = false;
Storyboard storyboard3 = new Storyboard();
storyboard3.Children.Add(animation3);
Storyboard.SetTargetName(animation3, border.Name);
Storyboard.SetTargetProperty(animation3, new PropertyPath(Rectangle.OpacityProperty));
svi.SizeChanged += delegate(object s, SizeChangedEventArgs args)
{
if (args.NewSize.Width < 150 && args.NewSize.Height < 150 && !isSmall)
{
svi.CanScale = false;
storyboard.Begin(this);
storyboard2.Begin(this);
storyboard3.Begin(this);
storyboard3.Completed += delegate(object sender2, EventArgs args2)
{
Console.WriteLine("Storyboard completed");
svi.CanScale = true;
};
isSmall = true;
}
if (args.NewSize.Width > 150 && args.NewSize.Height > 150 && isSmall)
{
isSmall = false;
}
};
}
And I noticed that the Storyboard#completed Event is never triggered. Why? And an additional question... Is there any way to reverse all these 3 animations? If I want to display the animations the other way round?

The completed event will not fire the first time around because it is not set before you call the begin method. Set the completed handler then call the begin and you should see the handler get called.
Is there a reason that you have three storyboards? Storyboards can contain multiple animations and you could just put all the animations into one storyboard. This would simplify reversing the storyboard.
DoubleAnimation animation = new DoubleAnimation();
animation.From = 0.0;
animation.To = 1.0;
animation.Duration = new Duration(TimeSpan.FromSeconds(3));
animation.AutoReverse = false;
DoubleAnimation animation2 = new DoubleAnimation();
animation2.From = 1.0;
animation2.To = 0.0;
animation2.Duration = new Duration(TimeSpan.FromSeconds(3));
animation2.AutoReverse = false;
DoubleAnimation animation3 = new DoubleAnimation();
animation3.From = 0.0;
animation3.To = 1.0;
animation3.Duration = new Duration(TimeSpan.FromSeconds(3));
animation3.AutoReverse = false;
Storyboard storyboard = new Storyboard();
storyboard.AutoReverse = true;
storyboard.Children.Add(animation);
Storyboard.SetTargetName(animation, boxSmall.Name);
Storyboard.SetTargetProperty(animation, new PropertyPath(Viewbox.OpacityProperty));
storyboard.Children.Add(animation2);
Storyboard.SetTargetName(animation2, box.Name);
Storyboard.SetTargetProperty(animation2, new PropertyPath(Viewbox.OpacityProperty));
storyboard.Children.Add(animation3);
Storyboard.SetTargetName(animation3, border.Name);
Storyboard.SetTargetProperty(animation3, new PropertyPath(Rectangle.OpacityProperty));
svi.SizeChanged += delegate(object s, SizeChangedEventArgs args)
{
if (args.NewSize.Width < 150 && args.NewSize.Height < 150 && !isSmall)
{
svi.CanScale = false;
storyboard.Completed += (o, s) =>
{
Console.WriteLine("Storyboard completed");
svi.CanScale = true;
};
storyboard.Begin(this);
isSmall = true;
}
if (args.NewSize.Width > 150 && args.NewSize.Height > 150 && isSmall)
{
isSmall = false;
}
};

Related

WPF: How to draw a circle?

I have the following code:
using Microsoft.Expression.Shapes;
Arc a = new Arc();
a.ArcThicknessUnit = Microsoft.Expression.Media.UnitType.Pixel;
a.ArcThickness = 10;
a.StrokeThickness = 1;
a.Fill = new SolidColorBrush(Colors.Aqua);
a.Stroke = new SolidColorBrush(Colors.DarkMagenta);
a.StartAngle = 0;
a.EndAngle = 360;
a.Width = 200;
a.Height = 200;
a.Stretch = Stretch.None;
_myStackPanel.Children.Add(a);
DispatcherTimer dt = new DispatcherTimer();
dt.Interval = TimeSpan.FromMilliseconds(1);
dt.Tick += (s, e) => { a.StartAngle = a.StartAngle + 4; };
dt.Start();
When it is executed I get an "erasable circle". I need to get the opposite effect. Сircle drawing effect. How can I change the code to get a "drawn circle"? Circle Drawning Example
Change the EndAngle instead of the StartAngle:
using Microsoft.Expression.Shapes;
Arc a = new Arc();
a.ArcThicknessUnit = Microsoft.Expression.Media.UnitType.Pixel;
a.ArcThickness = 10;
a.StrokeThickness = 1;
a.Fill = new SolidColorBrush(Colors.Aqua);
a.Stroke = new SolidColorBrush(Colors.DarkMagenta);
a.StartAngle = 0;
a.EndAngle = 0;//Start as 0
a.Width = 200;
a.Height = 200;
a.Stretch = Stretch.None;
_myStackPanel.Children.Add(a);
DispatcherTimer dt = new DispatcherTimer();
dt.Interval = TimeSpan.FromMilliseconds(1);
dt.Tick += (s, e) => { a.EndAngle = a.EndAngle + 4; };//update EndAngle
dt.Start();

Live Tile like Animation

I'm trying to recreate the slide-up part animation of the notification-queue on Windows 10 live tiles with an image I have ontop of another image. Below I have a "slide-up" storyboard working...but it's not the same.
Is the live tile ani actually growing in height as it slides up over the first?
I can't "see/figure" what it's doing.
public static async Task SlideUp(FrameworkElement element, double duration, int to = 0)
{
var tempTransform = new TranslateTransform();
element.RenderTransform = tempTransform;
var animation = new DoubleAnimation
{
From = element.ActualHeight * 2,
To = to,
Duration = TimeSpan.FromSeconds(duration),
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
};
Storyboard.SetTargetProperty(animation, "Y");
Storyboard.SetTarget(animation, tempTransform);
var sb = new Storyboard();
sb.Duration = animation.Duration;
sb.Children.Add(animation);
await sb.BeginAsync();
}
The flip part of the animations would be nice too.
You could take a look at the DoubleAnimationUsingKeyFrames class:
https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.media.animation.doubleanimationusingkeyframes.aspx
Update: You can also animate the Height of the image
public static async Task SlideUp(FrameworkElement element, double duration, int to = 0)
{
var trTransform = new TranslateTransform();
element.RenderTransform = trTransform;
double from = element.ActualHeight;
duration *= 1.5;
var animation = new DoubleAnimationUsingKeyFrames();
animation.KeyFrames.Add(new DiscreteDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0,0,0,0)),
Value = from / 2
});
var ks = new KeySpline { ControlPoint1 = new Point(0.0, 0.0), ControlPoint2 = new Point(0.9, 0.1) };
animation.KeyFrames.Add(new SplineDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(duration*1000/2)),
KeySpline = ks,
Value = (from - to) / 3 + to
});
var ks2 = new KeySpline { ControlPoint1 = new Point(0.1, 0.9), ControlPoint2 = new Point(0.2, 1.0) };
animation.KeyFrames.Add(new SplineDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(duration)),
KeySpline = ks2,
Value = to
});
Storyboard.SetTargetProperty(animation, "Y");
Storyboard.SetTarget(animation, trTransform);
var sb = new Storyboard();
sb.Duration = animation.Duration;
sb.Children.Add(animation);
DoubleAnimationUsingKeyFrames resizeHeightAnimation = new DoubleAnimationUsingKeyFrames()
{
EnableDependentAnimation = true
};
resizeHeightAnimation.KeyFrames.Add(new DiscreteDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0)),
Value = 0
});
var heightSpline1 = new KeySpline { ControlPoint1 = new Point(0.0, 0.0), ControlPoint2 = new Point(0.9, 0.1) };
resizeHeightAnimation.KeyFrames.Add(new SplineDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(duration * 1000 / 2)),
KeySpline = heightSpline1,
Value = from / 3
});
var heightSpline2 = new KeySpline { ControlPoint1 = new Point(0.1, 0.9), ControlPoint2 = new Point(0.2, 1.0) };
resizeHeightAnimation.KeyFrames.Add(new SplineDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(duration)),
KeySpline = heightSpline2,
Value = from
});
Storyboard.SetTarget(resizeHeightAnimation, element);
Storyboard.SetTargetProperty(resizeHeightAnimation, "RenderTransform.Height");
sb.Children.Add(resizeHeightAnimation);
sb.Begin();
}
If that looks to laggy for you, you can also try setting the VerticalAlignment of the element to Bottom and remove the position animation part of the method.

Making a user control pulse

What I'm trying to do is make my control "pulse" (as in enlarge a bit, then shrink a bit, then back to its normal size). The following code does exactly what I want, but it just seems like there should be a simpler way. It has to be done in code behind because this is an abstract class that most of our controls derive from. I don't have much experience with animation controls and story boards.
So specifically my question is, is there a simpler way to get the desired results than the following method (while still being done in the code-behind)?
public void Pulse()
{
var storyboard = new Storyboard
{
FillBehavior = FillBehavior.Stop,
RepeatBehavior = new RepeatBehavior(2)
};
double timeIncrement = .15;
double changePercent = 20;
var firstTime = TimeSpan.FromSeconds(timeIncrement);
var secondTime = TimeSpan.FromSeconds(timeIncrement * 3);
var thirdTime = TimeSpan.FromSeconds(timeIncrement * 4);
var scale = new ScaleTransform(1.0, 1.0);
RenderTransformOrigin = new Point(.5, .5);
RenderTransform = scale;
//enlarge
{
DoubleAnimation growX = new DoubleAnimation
{
Duration = firstTime,
To = 1 + changePercent
};
storyboard.Children.Add(growX);
Storyboard.SetTargetProperty(growX, new PropertyPath("RenderTransform.ScaleX"));
DoubleAnimation growY = new DoubleAnimation
{
Duration = firstTime,
To = 1 + changePercent
};
storyboard.Children.Add(growY);
Storyboard.SetTargetProperty(growY, new PropertyPath("RenderTransform.ScaleY"));
}
//shrink
{
DoubleAnimation shrinkX = new DoubleAnimation
{
Duration = secondTime,
To = 1 - changePercent
};
storyboard.Children.Add(shrinkX);
Storyboard.SetTargetProperty(shrinkX, new PropertyPath("RenderTransform.ScaleX"));
DoubleAnimation shrinkY = new DoubleAnimation
{
Duration = secondTime,
To = 1 - changePercent
};
storyboard.Children.Add(shrinkY);
Storyboard.SetTargetProperty(shrinkY, new PropertyPath("RenderTransform.ScaleY"));
}
//back to normal
{
DoubleAnimation normX = new DoubleAnimation
{
Duration = thirdTime,
To = 1
};
storyboard.Children.Add(normX);
Storyboard.SetTargetProperty(normX, new PropertyPath("RenderTransform.ScaleX"));
DoubleAnimation normY = new DoubleAnimation
{
Duration = thirdTime,
To = 1
};
storyboard.Children.Add(normY);
Storyboard.SetTargetProperty(normY, new PropertyPath("RenderTransform.ScaleY"));
}
BeginStoryboard(storyboard, HandoffBehavior.SnapshotAndReplace, false);
}
You could perhaps simplify things a little by moving your grow/shrink logic into its own method. This reduces duplication and provides a potentially more reusable code kibble.
public void Pulse()
{
var storyboard = new Storyboard
{
FillBehavior = FillBehavior.Stop,
RepeatBehavior = new RepeatBehavior(2)
};
double timeIncrement = .15;
double growPercent = 20;
double shrinkPercent = -20;
var firstTime = TimeSpan.FromSeconds(timeIncrement);
var secondTime = TimeSpan.FromSeconds(timeIncrement * 3);
var thirdTime = TimeSpan.FromSeconds(timeIncrement * 4);
var scale = new ScaleTransform(1.0, 1.0);
RenderTransformOrigin = new Point(.5, .5);
RenderTransform = scale;
storyboard = AddSizeChange(firstTime, growPercent, storyboard);
storyboard = AddSizeChange(secondTime, shrinkPercent, storyboard);
storyboard = AddSizeChange(thirdTime, growPercent, storyboard);
BeginStoryboard(storyboard, HandoffBehavior.SnapshotAndReplace, false);
}
public Storyboard AddSizeChange(TimeSpan animTime, double changePercent, Storyboard storyboard)
{
DoubleAnimation growX = new DoubleAnimation
{
Duration = animTime,
To = 1 + changePercent
};
storyboard.Children.Add(growX);
Storyboard.SetTargetProperty(growX, new PropertyPath("RenderTransform.ScaleX"));
DoubleAnimation growY = new DoubleAnimation
{
Duration = animTime,
To = 1 + changePercent
};
storyboard.Children.Add(growY);
Storyboard.SetTargetProperty(growY, new PropertyPath("RenderTransform.ScaleY"));
return storyboard;
}
That's a fine way of doing it, WPF does not provide methods or events to automatically pulse. Yours is simple and as you've created it, you know how to modify it as you wish.

Multiple Animations Windows Phone 7

I am trying to make one-hand machine game. To animate falling down Images I am using storyboard. My question is if someone know how to make multiple images. In this code I have just on image falling down. Somone know how to make e.g 100 images animation in the storyboard?
private Storyboard CreateStoryBoard()
{
Storyboard sb = new Storyboard();
DoubleAnimation firstAnimation = new DoubleAnimation();
firstAnimation.SpeedRatio = 8;
firstAnimation.From = 0;
firstAnimation.To = 600;
firstAnimation.Duration = new Duration(TimeSpan.FromSeconds(5));
Storyboard.SetTarget(firstAnimation, Okejka);
Storyboard.SetTargetProperty(firstAnimation, new PropertyPath("(Canvas.Top)"));
sb.Children.Add(firstAnimation);
return sb;
}
private void SpinButton_Click(object sender, RoutedEventArgs e)
{
Storyboard sb = CreateStoryBoard();
sb.Begin();
}
For each object you need create DoubleAnimation, each DoubleAnimation add to one Storyboard and then play it.
This is one method of my AnimationHelper, I modified it for this case.
public static void Animate(List<DependencyObject> objects, EventHandler onComplete = null)
{
Storyboard sb = new Storyboard();
foreach (DependencyObject obj in objects)
{
DoubleAnimation da = new DoubleAnimation();
da.From = FromValue; // Set you From value
da.To = ToValue; // Set your To value
da.Duration = new Duration(TimeSpan.FromSeconds(2)); // Set your Duration
// a.EasingFunction = anim.Func; Easing function
// da.BeginTime = anim.BeginTime; Begin time for each DA
Storyboard.SetTarget(da, obj);
Storyboard.SetTargetProperty(da, new PropertyPath(/* this your Property path */));
sb.Children.Add(da);
}
if (onComplete != null)
sb.Completed += onComplete;
sb.Begin();
}
UPDATE #1 The next code is Button.Click event handler, this code creates 20 Images and adds it to Canvas, next step is create animations for each Image using one instance of Storyboard.
private async void b1_Click(object sender, RoutedEventArgs e)
{
CanvasContainer.Children.Clear();
_images = new List<Image>();
// load bitmap
BitmapImage bmp = new BitmapImage(new Uri("Assets/appbar/appbar.italic.png", UriKind.Relative));
// create 20 Image instance
for (int i = 0; i < 20; i++)
{
Image img = new Image();
img.Source = bmp;
img.Stretch = Stretch.Fill;
img.Width = 20;
img.Height = 20;
_images.Add(img);
Canvas.SetTop(img, 0);
Canvas.SetLeft(img, i * 20 + 5);
CanvasContainer.Children.Add(img);
}
// Simulate some delay or any task (3 sec)
await Task.Delay(3000);
Storyboard sb = new Storyboard();
// delay animation time for each object
TimeSpan beginTime = TimeSpan.FromMilliseconds(0);
foreach (Image img in _images)
{
DoubleAnimation da = new DoubleAnimation();
da.From = 0; // Set start value to 0 px
da.To = 700; // Set end value to 700 px
da.Duration = new Duration(TimeSpan.FromSeconds(2)); // Set animation time to 2 sec
da.BeginTime = beginTime; // Set delay for each Image
beginTime += TimeSpan.FromMilliseconds(100);
Storyboard.SetTarget(da, img);
Storyboard.SetTargetProperty(da, new PropertyPath("(Canvas.Top)"));
sb.Children.Add(da);
}
sb.Begin();
}
Code result:

Animating the height and width of an object in C#

I am trying to get these ellipses to grow but I cannot figure out how to start the animation. This is my first attempt at WPF animation and I don't quite understand how it all works.
private void drawEllipseAnimation(double x, double y)
{
StackPanel myPanel = new StackPanel();
myPanel.Margin = new Thickness(10);
Ellipse e = new Ellipse();
e.Fill = Brushes.Yellow;
e.Stroke = Brushes.Black;
e.Height = 0;
e.Width = 0;
e.Opacity = .8;
canvas2.Children.Add(e);
Canvas.SetLeft(e, x);
Canvas.SetTop(e, y);
DoubleAnimation myDoubleAnimation = new DoubleAnimation();
myDoubleAnimation.From = 0;
myDoubleAnimation.To = 10;
myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(5));
myStoryboard = new Storyboard();
myStoryboard.Children.Add(myDoubleAnimation);
Storyboard.SetTargetName(myDoubleAnimation, e.Name);
Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath(Ellipse.HeightProperty));
Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath(Ellipse.WidthProperty));
}
You don't need a Storyboard here. Just do
e.BeginAnimation(Ellipse.WidthProperty, myDoubleAnimation);
e.BeginAnimation(Ellipse.HeightProperty, myDoubleAnimation);
If you really need to do it with a Storyboard, you will have to add separate animations, one per animated property, to the Storyboard. And you have to call SetTarget instead of SetTargetName when you don't apply a name. Finally you'll need to start the Storyboard by calling Begin:
DoubleAnimation widthAnimation = new DoubleAnimation
{
From = 0,
To = 10,
Duration = TimeSpan.FromSeconds(5)
};
DoubleAnimation heightAnimation = new DoubleAnimation
{
From = 0,
To = 10,
Duration = TimeSpan.FromSeconds(5)
};
Storyboard.SetTargetProperty(widthAnimation, new PropertyPath(Ellipse.WidthProperty));
Storyboard.SetTarget(widthAnimation, e);
Storyboard.SetTargetProperty(heightAnimation, new PropertyPath(Ellipse.HeightProperty));
Storyboard.SetTarget(heightAnimation, e);
Storyboard s = new Storyboard();
s.Children.Add(widthAnimation);
s.Children.Add(heightAnimation);
s.Begin();

Categories

Resources