I want create a floating animated clickable control like a helium balloon that moves sometimes to right or left too in my WPF application.
The helium balloons likes go up! but also they moves right or left if
we tap on them or by wind.
In advance cases, sometimes they turn to right or left
....................................................................
So i searched the web but i didn't find any usefull sample project or library or styles.
How i can create style and animation in WPF to show an image or
control buoyant or suspended in air.?
Have you any suggestions to implement this idea simply...?
Edit:
What is your suggestion for a random and infinite smooth turns to right and left. for example 51degrees to left then 163degree to right and.... i want keep my balloon in my window and often top of the window.
Edit:
I created an animation base on these answers but more complex by manipulating them and adding multiple parallel animation on balloon image and its container. ;)
Thanks all...
Now my primary result is like this:
You can achieve this with a DoubleAnimation and a RotateTransform like #Ega mentioned. To answer your "random and infinite" turns to right and left, here's how you could do it (it's very basic but does the job).
private async void Button_Click(object sender, RoutedEventArgs e)
{
var rnd = new Random();
var direction = -1;
while (true)
{
var percent = rnd.Next(0, 100);
direction *= -1; // Direction changes every iteration
var rotation = (int)((percent / 100d) * 45 * direction); // Max 45 degree rotation
var duration = (int)(750 * (percent / 100d)); // Max 750ms rotation
var da = new DoubleAnimation
{
To = rotation,
Duration = new Duration(TimeSpan.FromMilliseconds(duration)),
AutoReverse = true // makes the balloon come back to its original position
};
var rt = new RotateTransform();
Balloon.RenderTransform = rt; // Your balloon object
rt.BeginAnimation(RotateTransform.AngleProperty, da);
await Task.Delay(duration * 2); // Waiting for animation to finish, not blocking the UI thread
}
}
edit for .NET 3.5
private void Button_Click(object sender, RoutedEventArgs e)
{
var thread = new Thread(this.AnimateBalloon);
thread.Start();
}
public void AnimateBalloon()
{
var rnd = new Random();
var direction = -1;
while (true)
{
var percent = rnd.Next(0, 100);
direction *= -1; // Direction changes every iteration
var rotation = (int)((percent / 100d) * 45 * direction); // Max 45 degree rotation
var duration = (int)(750 * (percent / 100d)); // Max 750ms rotation
Balloon.Dispatcher.BeginInvoke(
(Action)(() =>
{
var da = new DoubleAnimation
{
To = rotation,
Duration = new Duration(TimeSpan.FromMilliseconds(duration)),
AutoReverse = true // makes the balloon come back to its original position
};
var rt = new RotateTransform();
Balloon.RenderTransform = rt; // Your balloon object
rt.BeginAnimation(RotateTransform.AngleProperty, da);
}));
Thread.Sleep(duration * 2);
}
}
Farseer Physics Engine for C# will allow you to create a real world simulation. You can control gravity, inertia, force and even other physical stuff.
World world = new World(new Vector2(0f, 9.82f));
//We create a body object and make it dynamic (movable)
Body myBody = world.CreateBody();
myBody.BodyType = BodyType.Dynamic;
//We create a circle shape with a radius of 0.5 meters
CircleShape circleShape = new CircleShape(0.5f);
//We fix the body and shape together using a Fixture object
Fixture fixture = myBody.CreateFixture(circleShape);
For this scenario you may need to create a world with less gravity and objects will fly. You can apply force from either side of the object to move it.
in C# DoubleAnimationis a powerful Class Support Animation inside System.Windows.Media.Animation; NameSpace so this class have some Methods and Properties let me to tell you it
DoubleAnimation ANobj = new DoubleAnimation();//make object from DoubleAnimation
ANobj.From=0 //the 0 is point you want to star animation
ANobj.To=270//the end pont of moving
// that values is dependent on your self as you want
ANobj.Duration = new Duration(TimeSpan.FromSeconds(60)); //specify the duration of moving
ANobj.RepeatBehavior = RepeatBehavior.Forever;//RepeatBehavior as you want
also use RotateTransform class to Rotate C# Componets in WPF
RotateTransform RTobj=new RotateTransform();//make object from RotateTransform
RTobj.CenterX = 277;//X point of your Element(Componet)
RTobj.CenterY = 245;//Y point of your Element(Componet)
RTobj.Angle = 360;//angle between
RTobj.BeginAnimation(RotateTransform.AngleProperty,ANobj);
also after doing that configures set this obj to RenderTransform property from your Element like it
yourElementName.RenderTransform=RTobj
if you want read more at MSDN about System.Windows.Media.Animation
May This help You Here,I take polygon and One image to left to right you can set by Your choice :)
<Window x:Class="Animation.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="XAML Animation - Spinning Stars" Height="300" Width="355"
>
<Grid Name="myGrid">
<Canvas Margin="15,18,18,26" MinHeight="50" MinWidth="50" Name="canvas1">
<!-- Invisible element just to host composite transform -->
<Polygon
Name ="mypolygon1"
Stroke="Blue"
StrokeThickness="1.0"
Points="176.5,50 189.2,155.003 286.485,113.5 201.9,177 286.485,240.5
189.2,198.997 176.5,304 163.8,198.997 66.5148,240.5 151.1,177
66.5148,113.5 163.8,155.003">
<Polygon.RenderTransform>
<TransformGroup>
<RotateTransform x:Name="xformRotate" CenterX="176" CenterY="145" />
<TranslateTransform x:Name="xformTranslate" X ="-50" Y="-50" />
<ScaleTransform x:Name ="xformScale" ScaleX=".25" ScaleY=".25" />
</TransformGroup>
</Polygon.RenderTransform>
<Polygon.Fill>
<SolidColorBrush Color="Blue">
<!--<ColorAnimation From="Yellow" To="Blue" Duration="7"
RepeatCount="500" AutoReverse="True"/>-->
</SolidColorBrush>
</Polygon.Fill>
<Polygon.Triggers>
<EventTrigger RoutedEvent="Polygon.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<!-- RotateTransform angle from 0 to 360, repeated -->
<DoubleAnimation Storyboard.TargetName="xformRotate"
Storyboard.TargetProperty="Angle"
From="0" To="360" Duration="0:0:01"
RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetName="xformTranslate"
Storyboard.TargetProperty="X"
From="1" To="750" Duration="0:0:14"
AutoReverse ="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Polygon.Triggers>
</Polygon>
<Polygon
Name ="mypolygon2"
Stroke="Red"
StrokeThickness="1.0"
Points="176.5,50 189.2,155.003 286.485,113.5 201.9,177 286.485,240.5
189.2,198.997 176.5,304 163.8,198.997 66.5148,240.5 151.1,177
66.5148,113.5 163.8,155.003">
<Polygon.RenderTransform>
<TransformGroup>
<RotateTransform x:Name="xformRotateIt" CenterX="176" CenterY="145" />
<ScaleTransform ScaleX=".25" ScaleY=".25" />
<TranslateTransform x:Name="xformTranslateIt" X="0" Y="100" />
</TransformGroup>
</Polygon.RenderTransform>
<Polygon.Fill>
<SolidColorBrush x:Name ="mybrush" Color="Red" />
</Polygon.Fill>
<Polygon.Triggers>
<EventTrigger RoutedEvent="Polygon.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard x:Name="myStoryBoard" Completed="OnCompletedAnimation">
<!-- RotateTransform angle from 0 to 360, repeated -->
<DoubleAnimation Storyboard.TargetName="xformRotateIt"
Storyboard.TargetProperty="Angle"
From="0" To="360" Duration="0:0:01"
RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetName="xformTranslateIt"
Storyboard.TargetProperty="X"
From="1" To="100" Duration="0:0:14"
AutoReverse ="True" RepeatBehavior="Forever" />
<ColorAnimation Storyboard.TargetName="mybrush" Storyboard.TargetProperty="Color" From="Red" To="Blue" Duration="0:0:7"
RepeatBehavior="Forever" AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Polygon.Triggers>
</Polygon>
<Image Margin="75,61,0,119" Name="image1" Source="Images\KENNY.bmp" HorizontalAlignment="Left" Width="72">
<Image.RenderTransform>
<TransformGroup>
<RotateTransform x:Name="KennyRotateIt" CenterX="50" CenterY="50" Angle="45" />
<ScaleTransform x:Name="KennyScaleIt" ScaleX=".75" ScaleY=".75" />
<TranslateTransform x:Name="KennyTranslateIt" X="0" Y="100" />
</TransformGroup>
</Image.RenderTransform>
<Image.Triggers>
<EventTrigger RoutedEvent="Image.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard x:Name="myStoryBoard1" Completed="OnCompletedAnimation">
<!-- RotateTransform angle from 0 to 360, repeated -->
<DoubleAnimation Storyboard.TargetName="KennyRotateIt"
Storyboard.TargetProperty="Angle"
From="0" To="360" Duration="0:0:01"
RepeatBehavior="Forever" />
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="KennyTranslateIt"
Storyboard.TargetProperty="X"
Duration="0:0:10" AutoReverse="True" RepeatBehavior="Forever">
<!-- These KeyTime properties are specified as TimeSpan values
which are in the form of "hours:minutes:seconds". -->
<LinearDoubleKeyFrame Value="10" KeyTime="0:0:3" />
<LinearDoubleKeyFrame Value="100" KeyTime="0:0:5" />
<LinearDoubleKeyFrame Value="200" KeyTime="0:0:10" />
</DoubleAnimationUsingKeyFrames>
<!-- DoubleAnimation Storyboard.TargetName="KennyTranslateIt"
Storyboard.TargetProperty="X"
From="-50" To="100" Duration="0:0:14"
AutoReverse ="True" RepeatBehavior="Forever" / -->
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Image.Triggers>
</Image>
</Canvas>
</Grid>
</Window>
Here is one I did in high school. Probably very outdated now but just for interest sake.
//the animation thread
System.Threading.Thread thr;
thr = new System.Threading.Thread(annimate);
thr.Start();
the animation method
void annimate()
{
try
{
double angleToAnimateTo = 20;
double CurrentAngle = 0;
bool increment = false;
while (IsAnimating)
{
if (increment)
CurrentAngle++;
else
CurrentAngle--;
if (CurrentAngle == angleToAnimateTo )
increment = false;
if (CurrentAngle == (angleToAnimateTo * -1))
increment = true;
this.Dispatcher.Invoke((Action)(() =>
{
this.imgBallon.RenderTransform = new RotateTransform(CurrentAngle);
}));
System.Threading.Thread.Sleep(100);
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
imgBalloon is a simple image in wpf. Here is the xaml..
<Image x:Name="imgBallon"
Source="/img/balloon_blue.png" Width="50"> </Image>
Related
I can create animation to create an object to move along the motion path. But I wonder, is it doable to have multiple object/path like moving by cloning itself?
Assume this is my map:-
I want to go from current location to the playground. I have a path and assign it to be motion path for my object(navigation arrow) to move alongside the line path. And for the curiosity, I want it to be like below:-
Currently, using the motion path, the arrow can moving in single object:-
UPDATED
Code:
<Grid>
<Path x:Name="SuperPath" Stroke="Black">
<Path.Data>
<PathGeometry Figures="M277,66 C277,66 427,87 447,153 466,219 396,241 397,297 399,352 439,376 439,376" />
</Path.Data>
</Path>
<Path
x:Name="Arrowhead"
Width="23"
Height="19.5"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Data="M94,29 L117,39 95,49 99,38 z"
Fill="#ff387cc0"
Stretch="Fill">
<Path.RenderTransform>
<TransformGroup>
<TranslateTransform X="-10" Y="-10" />
<MatrixTransform x:Name="rectangleTransform">
<MatrixTransform.Matrix>
<Matrix />
</MatrixTransform.Matrix>
</MatrixTransform>
</TransformGroup>
</Path.RenderTransform>
<Path.Triggers>
<EventTrigger RoutedEvent="Path.Loaded">
<BeginStoryboard>
<Storyboard>
<MatrixAnimationUsingPath
DoesRotateWithTangent="True"
RepeatBehavior="Forever"
Storyboard.TargetName="rectangleTransform"
Storyboard.TargetProperty="Matrix"
Duration="00:00:3">
<MatrixAnimationUsingPath.PathGeometry>
<PathGeometry PresentationOptions:Freeze="True" Figures="M277,66 C277,66 427,87 447,153 466,219 396,241 397,297 399,352 439,376 439,376" />
</MatrixAnimationUsingPath.PathGeometry>
</MatrixAnimationUsingPath>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Path.Triggers>
</Path>
</Grid>
How do each time arrowhead moving through path, it can leave a copy of the arrow trail like a navigation trail so it will be something for example 30 arrowhead with distance between it along the trail path.
First of all, simplify the XAML a bit:
<Canvas x:Name="canvas">
<Canvas.Resources>
<PathGeometry
x:Key="pathToFollow"
Figures="M73.517,48.931 C73.517,48.931 253.690,80.828 245.931,161.000 C238.172,241.172 114.897,261.000 221.793,350.655"/>
<Storyboard x:Key="arrowheadAnimation">
<MatrixAnimationUsingPath
Storyboard.TargetName="arrowhead"
Storyboard.TargetProperty="RenderTransform.Matrix"
PathGeometry="{StaticResource pathToFollow}"
DoesRotateWithTangent="True"
Duration="0:0:5"/>
</Storyboard>
</Canvas.Resources>
<Path Data="{StaticResource pathToFollow}"
Stroke="#ff818181" StrokeThickness="6.0" StrokeDashArray="4.33"
StrokeStartLineCap="Round" StrokeEndLineCap="Round"/>
<Path x:Name="arrowhead"
Data="M0,0 L-10,-10 30,0 -10,10 Z"
Fill="#ff387cc0">
<Path.RenderTransform>
<MatrixTransform/>
</Path.RenderTransform>
</Path>
</Canvas>
Now there may be the following code behind that starts the animation in a Loaded event handler. In addition, it creates a timer that creates copies of the arrow head at certain times.
private readonly Storyboard storyboard;
private readonly DispatcherTimer timer;
public MainWindow()
{
InitializeComponent();
storyboard = (Storyboard)canvas.Resources["arrowheadAnimation"];
timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(0.5) };
timer.Tick += OnTimerTick;
Loaded += OnWindowLoaded;
}
private void OnWindowLoaded(object sender, RoutedEventArgs e)
{
storyboard = (Storyboard)Resources["arrowheadAnimation"];
storyboard.Begin();
timer.Start();
}
private void OnTimerTick(object sender, EventArgs e)
{
if (storyboard.GetCurrentState() != ClockState.Active)
{
timer.Stop();
return;
}
var transform = (MatrixTransform)arrowhead.RenderTransform;
var arrowheadCopy = new Path
{
Data = arrowhead.Data,
Fill = arrowhead.Fill,
RenderTransform = new MatrixTransform(transform.Matrix)
};
canvas.Children.Add(arrowheadCopy);
}
I can't get my animation to start and stop, with code behind (c #)
I have tried MyStoryboard.stop() and change duration for the animation and it stops, but only when I change content and go back.
When I start it again my animations run at strange speeds and again only if I change content
when I change content there is a new speed and if I keep doing it, it bugs
Xaml:
<Image Grid.Row="0" MaxHeight="50" Source="..\Images\Propeller.png"
RenderTransformOrigin=".5,.5">
<Image.RenderTransform>
<RotateTransform x:Name="MyAnimation" Angle="0" />
</Image.RenderTransform>
<Image.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard x:Name="MyStoryboard">
<DoubleAnimation x:Name="Prope11"
Storyboard.TargetName="MyAnimation"
To="360"
Duration="0:0:0.6"
RepeatBehavior="Forever"
FillBehavior="Stop" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
Code behind:
TimeSpan ts = TimeSpan.FromMilliseconds(600);
MyStoryboard.Stop();
Prope11.Duration = ts;
MyStoryboard.Bigin();
You had not assigned the TargetName Property of the Storyboard to start the animation.
Well, there are two problems in your xaml to make the animation start.
1) Storyboard.TargetName="MyAnimation", Target name must be controls name. In your case, you have provided but you have given or assigned it to RotateTransform, which comes under the Image control, but not the image.
<Image x:Name="ImageControl" Grid.Row="0" MaxHeight="50" Source="C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg"
RenderTransformOrigin=".5,.5" >
and in DoubleAnimation provide TargetName as the Image control's name
Storyboard.TargetName="ImageControl"
2) Another problem is you have not provided the Target Property on which your double animation is dependent.
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
Your stop code didn't work because, it was not started. Adding this below xaml will solve your problem.
Change in the speed is because you have given 600, for 6 Seconds it must be 6000 and not 600.
This is the change i have done. On Mouse Down event I have given to Stop StoryBoard.
<Image.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard x:Name="BeginImageRotateAni" >
<Storyboard x:Name="MyStoryboard">
<DoubleAnimation x:Name="Prope11"
Storyboard.TargetName="ImageControl"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
To="360"
Duration="0:0:6"
RepeatBehavior="Forever"
FillBehavior="Stop"
>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseDown">
<EventTrigger.Actions>
<StopStoryboard BeginStoryboardName="BeginImageRotateAni"/>
</EventTrigger.Actions>
</EventTrigger>
</Image.Triggers>
and subscribed for MouseDownEvent in Constructor
ImageControl.MouseDown += ImageControl_MouseDown;
and write the below code inside the event.
bool IsAnimationstarted = true;
private void ImageControl_MouseDown(object sender, MouseButtonEventArgs e)
{
if (IsAnimationstarted)
{
MyStoryboard.Stop();
IsAnimationstarted = false;
}
else
{
TimeSpan ts = TimeSpan.FromMilliseconds(6000);
Prope11.Duration = ts;
MyStoryboard.Begin();
IsAnimationstarted = true;
}
}
}
Alternate solution:- by creating the animation in the resource and then utilizing it in the codebehind.
<Window.Resources>
<Storyboard x:Name="MyStoryboard" x:Key="Animation1" >
<DoubleAnimation x:Name="Prope11"
Storyboard.TargetName="ImageControl"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
To="360"
Duration="0:0:6"
RepeatBehavior="Forever"
FillBehavior="Stop"
>
</DoubleAnimation>
</Storyboard>
</Window.Resources>
In Code Behind:-
bool IsAnimationstarted = true;
private void ImageControl_MouseDown(object sender, MouseButtonEventArgs e)
{
Storyboard board = (Storyboard)this.FindResource("Animation1");
if (IsAnimationstarted)
{
IsAnimationstarted = false;
board.Begin();
}
else
{
IsAnimationstarted = true;
board.Pause();
}
}
I don't know why are changing the speed or not sure about your intention. What I have done here is when the user clicks on the image control, it will start the animation and pause again clicked and this goes on.
Let's imagine we have an animation that changes a Rectangle's width from 50 to 150 when user moves the cursor over it and back from 150 to 50 when user moves the cursor away. Let the duration of each animation be 1 second.
Everything is OK if user moves the cursor over the rectangle, then waits for 1 second for animation to finish, and then moves the cursor away from the rectangle. Two animations take 2 seconds totally and their speed is exactly what we are expecting to be.
But if user moves the cursor away before the first animation (50 -> 150) is finished, the second animation's duration will be 1 second but the speed of it will be very slow. I mean, the second animation will animate the rectangle's width not from 150 to 50 but from let's say 120 to 50 or from 70 to 50 if you take cursor away very fast. But for the same 1 second!
So what I want to understand is how to make the duration of the "backwards" animation to be dynamic. Dynamic based on at what point the first animation was stopped. Or, if I specify From and To values to 150 and 50, Duration to 1 seconds and rectangle width will be 100 – WPF would calculate by itself that animation is 50% done.
I was testing different ways to use animation like Triggers, EventTrigger in style, and VisualStateGroups but no success.
Here is a XAML sample that shows my problem. If you want to see by yourself what I am talking about, move the cursor over the rectangle, wait for 1-2 seconds (first animation is finished) then move the cursor away. After that, move the cursor over the rectangle for like 0.25 seconds and move it away. You will see that the rectangle's width changes very slowly.
<Rectangle Width="50"
Height="100"
HorizontalAlignment="Left"
Fill="Black">
<Rectangle.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width"
To="150"
Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
Also, I would like to explain what calculation I am expecting to see.
So let's imagine another animation that goes from 100 to 1100 and has a duration of 2 seconds. If we stop it at the point of 300, it must start to go back to 100 but not for 2 seconds but for:
((300 - 100) / (1100 - 100)) * 2s = 0.4s
If we stop at 900, duration will be:
((900 - 100) / (1100 - 100)) * 2s = 1.6s
And if we let animation finish normally it will be:
((1100 - 100) / (1100 - 100)) * 2s = 2s
WPF provides the possibility to write custom animations.
You may create one with a MinSpeed property:
public class MinSpeedDoubleAnimation : DoubleAnimation
{
public static readonly DependencyProperty MinSpeedProperty =
DependencyProperty.Register(
nameof(MinSpeed), typeof(double?), typeof(MinSpeedDoubleAnimation));
public double? MinSpeed
{
get { return (double?)GetValue(MinSpeedProperty); }
set { SetValue(MinSpeedProperty, value); }
}
protected override Freezable CreateInstanceCore()
{
return new MinSpeedDoubleAnimation();
}
protected override double GetCurrentValueCore(
double defaultOriginValue, double defaultDestinationValue,
AnimationClock animationClock)
{
var destinationValue = To ?? defaultDestinationValue;
var originValue = From ?? defaultOriginValue;
var duration = Duration != Duration.Automatic ? Duration :
animationClock.NaturalDuration;
var speed = (destinationValue - originValue) / duration.TimeSpan.TotalSeconds;
if (MinSpeed.HasValue && Math.Abs(speed) < MinSpeed)
{
speed = Math.Sign(speed) * MinSpeed.Value;
}
var value = originValue + speed * animationClock.CurrentTime.Value.TotalSeconds;
return speed > 0 ?
Math.Min(destinationValue, value) :
Math.Max(destinationValue, value);
}
}
and use it like this:
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Width"
To="150" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<local:MinSpeedDoubleAnimation
Storyboard.TargetProperty="Width"
To="50" MinSpeed="100"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
For completeness here is also the most simple code-behind solution without any Triggers and Storyboards:
<Rectangle Width="50" Height="100" HorizontalAlignment="Left" Fill="Black"
MouseEnter="RectangleMouseEnter" MouseLeave="RectangleMouseLeave"/>
with these event handlers:
private void RectangleMouseEnter(object sender, MouseEventArgs e)
{
var element = (FrameworkElement)sender;
var animation = new DoubleAnimation(150, TimeSpan.FromSeconds(1));
element.BeginAnimation(FrameworkElement.WidthProperty, animation);
}
private void RectangleMouseLeave(object sender, MouseEventArgs e)
{
var element = (FrameworkElement)sender;
var duration = (element.ActualWidth - 50) / 100;
var animation = new DoubleAnimation(50, TimeSpan.FromSeconds(duration));
element.BeginAnimation(FrameworkElement.WidthProperty, animation);
}
Unfortunately, Storyboards are compiled code, meaning you can't use binding in the elements to improve their abilities.
I've had to do something similar and the only way I found to achieve it was to access the storyboard property that needed to be dynamic at the time that I needed to touch it, and set the property as needed in code behind.
So it's not ideal to have to touch and trigger from code behind, but hey look at it this way, it's not business logic, it's strictly presentation layer so code behind is typically acceptable practice.
So handle your trigger in code behind, calculate your numbers, update the storyboard properties and then start it. That's probably going to be your best option for now.
I would love to see storyboards become part of the logical tree with dependency properties to dynamically modify values, so maybe request the feature :).
An alternative to a custom animation could be an attached property that is bound to the target element's ActualWidth, which dynamically adjusts the animation's Duration.
The attached property could look like this:
public static class AnimationEx
{
public static readonly DependencyProperty DynamicDurationProperty =
DependencyProperty.RegisterAttached(
"DynamicDuration", typeof(double), typeof(AnimationEx),
new PropertyMetadata(DynamicDurationPropertyChanged));
public static double GetDynamicDuration(DoubleAnimation animation)
{
return (double)animation.GetValue(DynamicDurationProperty);
}
public static void SetDynamicDuration(DoubleAnimation animation, double value)
{
animation.SetValue(DynamicDurationProperty, value);
}
private static void DynamicDurationPropertyChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var animation = o as DoubleAnimation;
if (animation != null && animation.To.HasValue)
{
animation.Duration = TimeSpan.FromSeconds(
Math.Abs((double)e.NewValue - animation.To.Value) / 100);
}
}
}
where there might be another attached property for the speed factor, which is hardcoded as 100 above.
The property would be used like this:
<Rectangle.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width"
To="150" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width" To="50"
local:AnimationEx.DynamicDuration="{Binding Path=ActualWidth,
RelativeSource={RelativeSource AncestorType=Rectangle}}" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
A possible drawback might be the fact that the Duration is constantly reset when the ActualWidth changes. However, I could not notify any visual effect.
I have a background image in my wpf application, I want to achieve a effect of the background image continuous move. The following code can achieve a simple effect, but when the time of the end of the picture moves over again, I want to make this picture the effect of the movement has been the phenomenon does not come to a halt.
void StartBackgroudMove()
{
Storyboard sb = new Storyboard();
DoubleAnimation TranslateXExtendAnimation = new DoubleAnimation() { From = 0, To = -100, Duration = TimeSpan.FromMilliseconds(2500) };
Storyboard.SetTarget(TranslateXExtendAnimation, BackgroundImage);
Storyboard.SetTargetProperty(TranslateXExtendAnimation, new PropertyPath("(FrameworkElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"));
sb.Children.Add(TranslateXExtendAnimation);
sb.FillBehavior = FillBehavior.Stop;
sb.Completed += (s, args) =>
{
this.StartBackgroudMove();
};
sb.Begin();
}
You may use an ImageBrush with TileMode set to Tile and animate its Viewport property:
<Grid>
<Grid.Background>
<ImageBrush ImageSource="C:\Users\Public\Pictures\Sample Pictures\Desert.jpg"
Stretch="Fill" TileMode="Tile"
ViewportUnits="Absolute" Viewport="0,0,1024,768"/>
</Grid.Background>
<Grid.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<RectAnimation Storyboard.TargetProperty="Background.Viewport"
To="-1024,0,1024,768" Duration="0:0:10"
RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
</Grid>
The following alternative solution places two adjacent images in a Canvas and animates the Canvas' RenderTransform property.
<Grid>
<Canvas>
<Canvas.RenderTransform>
<TranslateTransform x:Name="translation"/>
</Canvas.RenderTransform>
<Image Stretch="None"
Source="C:\Users\Public\Pictures\Sample Pictures\Desert.jpg"/>
<Image Canvas.Left="1024" Stretch="None"
Source="C:\Users\Public\Pictures\Sample Pictures\Desert.jpg"/>
</Canvas>
<Grid.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="X"
Storyboard.TargetName="translation"
To="-1024" Duration="0:0:10"
RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
</Grid>
You can set the Storyboard.RepeatBehavior to RepeatBehavior.Forever
Example:
void StartBackgroudMove()
{
Storyboard sb = new Storyboard();
DoubleAnimation TranslateXExtendAnimation = new DoubleAnimation() { From = 0, To = -100, Duration = TimeSpan.FromMilliseconds(2500) };
Storyboard.SetTarget(TranslateXExtendAnimation, BackgroundImage);
Storyboard.SetTargetProperty(TranslateXExtendAnimation, new PropertyPath("(FrameworkElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"));
sb.Children.Add(TranslateXExtendAnimation);
sb.FillBehavior = FillBehavior.Stop;
sb.RepeatBehavior = RepeatBehavior.Forever;
sb.Begin();
}
I am a beginner in Silverlight. I took a border control, on mouse over of which I want to increase its width, but slowly in an animated way, and on mouse out back to normal
<Border BorderBrush="#528CC1" Background="#164A7A" MouseEnter="bHome_MouseEnter"
MouseLeave="bHome_MouseLeave" Opacity="0.75" BorderThickness="1" Height="75" Width="110"
Grid.Column="1" x:Name="bProduct" Margin="0,0,0,0" Cursor="Hand" VerticalAlignment="Top">
private void bHome_MouseEnter(object sender, MouseEventArgs e)
{
Border border = (Border)sender;
border.Width = 160;
border.Opacity = 100;
}
private void bHome_MouseLeave(object sender, MouseEventArgs e)
{
Border border = (Border)sender;
border.Width = 110;
border.Opacity = 0.75;
}
If you really want to do it with code (it's way way easier to do with visual states, even the mouse over / out is handled for you out of the box, just have to set starting and ending parameters in XAML, however if the values are dynamic, it's not possible, you can't do binding in the VisualStateManager markup as it's not part of the visual tree), here's an example (from http://msdn.microsoft.com/en-us/library/cc189069(VS.95).aspx#procedural_code ):
The following example shows how to create an animation that animates Canvas.Top and Canvas.Left attached properties of a rectangle.
private void Create_And_Run_Animation(object sender, EventArgs e)
{
// Create a red rectangle that will be the target
// of the animation.
Rectangle myRectangle = new Rectangle();
myRectangle.Width = 200;
myRectangle.Height = 200;
Color myColor = Color.FromArgb(255, 255, 0, 0);
SolidColorBrush myBrush = new SolidColorBrush();
myBrush.Color = myColor;
myRectangle.Fill = myBrush;
// Add the rectangle to the tree.
LayoutRoot.Children.Add(myRectangle);
// Create a duration of 2 seconds.
Duration duration = new Duration(TimeSpan.FromSeconds(2));
// Create two DoubleAnimations and set their properties.
DoubleAnimation myDoubleAnimation1 = new DoubleAnimation();
DoubleAnimation myDoubleAnimation2 = new DoubleAnimation();
myDoubleAnimation1.Duration = duration;
myDoubleAnimation2.Duration = duration;
Storyboard sb = new Storyboard();
sb.Duration = duration;
sb.Children.Add(myDoubleAnimation1);
sb.Children.Add(myDoubleAnimation2);
Storyboard.SetTarget(myDoubleAnimation1, myRectangle);
Storyboard.SetTarget(myDoubleAnimation2, myRectangle);
// Set the attached properties of Canvas.Left and Canvas.Top
// to be the target properties of the two respective DoubleAnimations.
Storyboard.SetTargetProperty(myDoubleAnimation1, new PropertyPath("(Canvas.Left)"));
Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(Canvas.Top)"));
myDoubleAnimation1.To = 200;
myDoubleAnimation2.To = 200;
// Make the Storyboard a resource.
LayoutRoot.Resources.Add("unique_id", sb);
// Begin the animation.
sb.Begin();
}
EDIT See comment below - this is the WPF way not the Silverlight way, so you might want to disregard this answer!!
Don't do it through code; that's not the Silverlight / WPF way. Instead use the XAML eg:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<!-- Defines a Button style. -->
<Style TargetType="{x:Type Button}" x:Key="MyButtonStyle">
<Style.Triggers>
<!-- Animate the button width on mouse over. -->
<EventTrigger RoutedEvent="Button.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Button.Width)"
To="200" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<!-- Returns the button's width to 50 when the mouse leaves. -->
<EventTrigger RoutedEvent="Button.MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Button.Width)"
To="50" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</Page.Resources>
<StackPanel Margin="20">
<Button Style="{StaticResource MyButtonStyle}" Width="50" >Click Me</Button>
</StackPanel>
</Page>
For more info see here: Animation and Timing How-to Topics