WPF trigger animation from code - c#

I was reading many similar q&a but I didn't get answer I'm searching for. So, I'm making "homework" in Microsoft Bled, I really like storyboards and I know how to trigger them with button click, but does anyone know how to start an animation in c# for example in if sentence.
Thanks for answers and time spend in advance!

You´re looking for this thread.
Another way would be:
Storyboard sb = this.FindResource("Storyboard1") as Storyboard;
if (sb != null){ BeginStoryboard(sb); }

public static class AnimationHelper
{
private static void AnimateOpacity(DependencyObject target, double from, double to)
{
var opacityAnimation = new DoubleAnimation
{
From = from,
To = to,
Duration = TimeSpan.FromMilliseconds(500)
};
Storyboard.SetTarget(opacityAnimation, target);
Storyboard.SetTargetProperty(opacityAnimation, "Opacity");
var storyboard = new Storyboard();
storyboard.Children.Add(opacityAnimation);
storyboard.Begin();
}
/// <summary>
/// Fades in the given dependency object.
/// </summary>
/// <param name="target">The target dependency object to fade in.</param>
public static void FadeIn(DependencyObject target)
{
AnimateOpacity(target, 0, 1);
}
}

You can access storyboards from code-behind by giving it a name and referencing that name to use the Begin method.
<Canvas MouseLeftButtonDown="Handle_MouseDown"
Background="Gray" Width="600" Height="500">
<Canvas.Resources>
<Storyboard x:Name="myStoryboard">
<PointAnimation
x:Name="myPointAnimation"
Storyboard.TargetProperty="Center"
Storyboard.TargetName="MyAnimatedEllipseGeometry"
Duration="0:0:2"/>
</Storyboard>
</Canvas.Resources>
<Path Fill="Blue">
<Path.Data>
<EllipseGeometry x:Name="MyAnimatedEllipseGeometry"
Center="200,100" RadiusX="15" RadiusY="15" />
</Path.Data>
</Path>
</Canvas>
Code-behind:
private void Handle_MouseDown(object sender, MouseButtonEventArgs e)
{
// Retrieve current mouse coordinates.
double newX = e.GetPosition(null).X;
double newY = e.GetPosition(null).Y;
Point myPoint = new Point();
myPoint.X = newX;
myPoint.Y = newY;
myPointAnimation.To = myPoint;
myStoryboard.Begin();
}

Related

WPF: how to detect geometry intersection on canvas?

I need to determine the intersection of objects moving on the canvas (not necessarily on the canvas, any other panel).
I'm trying to determine the intersection with FillContainsWithDetail.
It seems that the intersection ignores the relative location of objects.
XAML:
<Window x:Class="WpfCollisionTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCollisionTest"
Title="Collisions" Height="400" Width="500">
<Window.DataContext>
<local:CProvider x:Name="Provider"/>
</Window.DataContext>
<Window.Resources>
<PathGeometry x:Key="geoPoly">
<PathGeometry.Figures>
<PathFigure StartPoint="0,0" IsClosed="True">
<PolyLineSegment Points="0,0 50,0 50,50 0,50"/>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Window.Resources>
<Grid>
<Canvas Background="White" MouseMove="Canvas_OnMouseMove">
<Path Name="rectangle1" Data="{StaticResource geoPoly}" Stroke="Black"
Canvas.Left="175" Canvas.Top="100"/>
<Path Name="rectangle2" Data="{StaticResource geoPoly}" Stroke="Black"
Canvas.Left="{Binding rectLeft}" Canvas.Top="{Binding rectTop}"/>
</Canvas>
<TextBlock Text="{Binding intersectionDetail}" VerticalAlignment="Bottom"/>
</Grid>
CS:
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
namespace WpfCollisionTest
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
private void Canvas_OnMouseMove(object sender, MouseEventArgs e)
{
var pos = e.GetPosition(sender as FrameworkElement);
Provider.rectLeft = pos.X;
Provider.rectTop = pos.Y;
var g1 = rectangle1.RenderedGeometry.GetFlattenedPathGeometry();
var g2 = rectangle2.RenderedGeometry.GetFlattenedPathGeometry();
var intersect = g1.FillContainsWithDetail(g2);
Provider.intersectionDetail = intersect.ToString();
}
}
}
In this example result is always: Intersects.
What am I missing?
Recently I have had to deal with the same problem. So,
We need a method that extracts all transformations from the source object. In my application, I use only RenderTransform in Canvas. Also, make sure that this transform is applied before TranslateTransform with Canvas.Top and Canvas.Left parameters:
private static Transform GetFullTransform(UIElement e)
{
// The order in which transforms are applied is important!
var transforms = new TransformGroup();
if(e.RenderTransform != null)
transforms.Children.Add(e.RenderTransform);
var xTranslate = (double)e.GetValue(Canvas.LeftProperty);
if (double.IsNaN(xTranslate))
xTranslate = 0D;
var yTranslate = (double)e.GetValue(Canvas.TopProperty);
if (double.IsNaN(yTranslate))
yTranslate = 0D;
var translateTransform = new TranslateTransform(xTranslate, yTranslate);
transforms.Children.Add(translateTransform);
return transforms;
}
Introduce a method that converts a given object to a Geometry. Here for demonstration purpose, I convert a Shape object:
public Geometry GetGeometry(Shape s)
{
var g = s.RenderedGeometry.Clone();
g.Transform = GetFullTransform(s);
return g;
}
And finally, use FillContainsWithDetail method. The condition below returns True in 2 cases: when there is an intersection, or when one of the geometries is completely inside another:
private static bool HasIntersection(Geometry g1, Geometry g2) =>
g1.FillContainsWithDetail(g2) != IntersectionDetail.Empty;

UWP rotation button animation

I am trying to reproduce an animation for a button, like the animation of the native camera app buttons of Windows 10 Mobile, but I'm lost.
I'm using this example as a basis:
CameraStarterKit
The rotation of the buttons is already ok. o que eu gostaria de implementar era a animação.
Here is the code that the rotate buttons:
private void UpdateButtonOrientation()
{
int device = ConvertDeviceOrientationToDegress(_deviceOrientation);
int display = ConvertDisplayOrientationToDegrees(_displayOrientation);
if (_displayInformation.NativeOrientation == DisplayOrientations.Portrait)
{
device -= 90;
}
var angle = (360 + display + device) % 360;
var transform = new RotateTransform { Angle = angle };
PhotoButton.RenderTransform = transform;
}
How do I include an animation in this code.
Already grateful for any help!
Add RotateTransform property to your Button
<Button Grid.Row="1"
x:Name="PhotoButton"
Content="PhotoButton"
Click="PhotoButton_Click"
HorizontalAlignment="Center">
<Button.RenderTransform>
<RotateTransform x:Name="PhotoButtonRotateTransform"/>
</Button.RenderTransform>
</Button>
After that, you can create and manage Storyboard in code like this:
private void PhotoButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
AnimateProgressRingSlice(PhotoButtonRotateTransform.Angle + 90);
}
private void AnimateProgressRingSlice(double to, double miliseconds = 350)
{
var storyboard = new Storyboard();
var doubleAnimation = new DoubleAnimation();
doubleAnimation.Duration = TimeSpan.FromMilliseconds(miliseconds);
doubleAnimation.EnableDependentAnimation = true;
doubleAnimation.To = to;
Storyboard.SetTargetProperty(doubleAnimation, "Angle");
Storyboard.SetTarget(doubleAnimation, PhotoButtonRotateTransform);
storyboard.Children.Add(doubleAnimation);
storyboard.Begin();
}
Remark
But be careful if you don't know about RenderTransformOrigin property. Read More Info
I guess you need to use this property with 0.5, 0.5 value, so, modify your XAML and add to Button
RenderTransformOrigin="0.5, 0.5
Look the result:
You can define the animation as a Storyboard in the resources of your page:
<Page.Resources>
<Storyboard x:Name="rotate90">
... some animation that changes the RenderTransform of the button
<DoubleAnimation Storyboard.TargetName="PhotoButtonRotateTransform"
Storyboard.TargetProperty="Angle"
Value="90" x:Name="RotationAnimation" />
</Storyboard>
</Page.Resources>
And then start the animation from your code-behind:
private void UpdateButtonOrientation()
{
//here you can put some logic that
//sets the target value for the RotationAnimation's Value property
rotateButtonStoryboard.Begin();
}
For more information about animations and Storyboards check out the MSDN documentation

Analog Clock, Rotate Hands C# WPF

I am working on a WPF project, trying to create an analog clock. I have an image of a clock (without the hands) and have set it as a background. I also have images of two hands of clock, which I want to rotate keeping one end fixed (like it happens in a clock). How can I rotate the image keeping its one end fixed in C#.NET (WPF)? What I have tried is the following code:
namespace AnalogWatch
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
DispatcherTimer dispatcherTimer;
private int degrees = 0;
public MainWindow()
{
InitializeComponent();
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
degrees += 5;
if (degrees > 360)
{
degrees = 0;
}
RotateTransform transform = new RotateTransform(degrees,StickImg.Width/2,StickImg.Height/2);
//StickImg.RenderTransformOrigin = new System.Windows.Point(0, 0);
StickImg.RenderTransform = transform;
}
private void Window_ContentRendered_1(object sender, EventArgs e)
{
dispatcherTimer.Start();
}
}
}
It is rotating the image but not keeping its one end fixed. What is the problem here ?
XAML is:
<Window x:Class="AnalogWatch.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" ContentRendered="Window_ContentRendered_1">
<Grid>
<Image x:Name="StickImg" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Margin="207,70,0,0" Source="stick.png"/>
</Grid>
</Window>
Updated.
Your code should work if clock hand exactly in the center of rectangular image.
Like this one
You can do either like you did
RotateTransform transform =new RotateTransform(degrees, StickImg.Width/2,StickImg.Height/2);
or
RotateTransform transform = new RotateTransform(degrees);
StickImg.RenderTransformOrigin = new Point(0.5, 0.5);
Use the RenderTransformOrigin of the element to set the center of rotation.
Note that the coordinates are scaled to 0..1
So to rotate around the center:
RenderTransformOrigin="0.5, 0.5"
Just make sure that the pivot of the hand is in the center of the element.
here's the code i'm using and it works like charm..as qwr suggested clock hand should be exactly in the center of rectangular image
c# code
DispatcherTimer clock = new DispatcherTimer();
public AnalogClock()
{
InitializeComponent();
clock.Interval =TimeSpan.FromMilliseconds(100);
clock.Tick += clock_Tick;
clock.Start();
}
void clock_Tick(object sender, EventArgs e)
{
double milsec = DateTime.Now.Millisecond;
double sec = DateTime.Now.Second;
double min = DateTime.Now.Minute;
double hr = DateTime.Now.Hour;
seconds.LayoutTransform = new RotateTransform(((sec / 60) * 360)+((milsec/1000)*6));
minutes.LayoutTransform = new RotateTransform((min * 360 / 60)+((sec/60)*6));
hours.LayoutTransform = new RotateTransform((hr * 360 / 12)+(min/2));
}
and the XAML code for images
<Grid Margin="-100">
<Image x:Name="clockface" RenderOptions.BitmapScalingMode="HighQuality" Source="images/panel.PNG" RenderTransformOrigin="0.5,0.5" HorizontalAlignment="Center" VerticalAlignment="Center" Height="194" Margin="100" Width="194"/>
<Image x:Name="hours" RenderOptions.BitmapScalingMode="HighQuality" Source="images/hours.PNG" RenderTransformOrigin="0.5,0.5" VerticalAlignment="Center" HorizontalAlignment="Center" Height="194" Margin="100" Width="194"/>
<Image x:Name="minutes" RenderOptions.BitmapScalingMode="HighQuality" Source="images/minutes.PNG" RenderTransformOrigin="0.5,0.5" VerticalAlignment="Center" HorizontalAlignment="Center" Height="194" Margin="100" Width="194"/>
<Image x:Name="seconds" RenderOptions.BitmapScalingMode="HighQuality" Source="images/seconds.PNG" RenderTransformOrigin="0.5,0.5" HorizontalAlignment="Center" VerticalAlignment="Center" Height="194" Margin="100" Width="194"/>
</Grid>
note the i've used the code in a separate usercontrol..
just make sure that the margin between the clock hand image and the grid i'ts contained in should be enough to make room for the image to rotate else it will displace while rotating..
hope this helps..!

Animate Drawing Circle in WPF with XAML

I want to be able to smoothly draw a circle (ellipse) so that you can see it being drawn on screen.
Is there anyway possible using DoubleAnimation to do this? If not what is an alternative method?
An example of what I have:
One outer ellipse (black)
Inner ellipse (white) - this is the one I want to animate
Code:
<Ellipse Width="200" Height="200" Stroke="Black" StrokeThickness="20"/>
<Ellipse Width="190" Height="190" Stroke="White" StrokeThickness="10" Canvas.Left="5" Canvas.Top="5" x:Name="animatedEllipse">
<Ellipse.Triggers>
<EventTrigger RoutedEvent="Ellipse.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Ellipse.Triggers>
</Ellipse>
I've look at a few examples for example:
http://elegantcode.com/2009/08/21/a-simple-wpf-loading-animation/
http://www.charlespetzold.com/blog/2012/03/The-Animated-Pie-Slice-in-Windows-8.html
Ellipse Drawing WPF Animation
The first two are a bit confusimg for me being new to WPF animation. The latter has 7 votes as "Correct answer" but it is not working for me as I get error "the collection element StrokeDashArray[0] is not a dependency property" (and also I don't want dashes, Although I tried this even on an ellipse with dashes as per the above article and it still failed on this error.
Update:
A method I got sort of working using code is this:
public static class ExtensionMethods
{
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement)
{
uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
}
}
public partial class Page1 : Page
{
private void Page_Loaded_1(object sender, RoutedEventArgs e)
{
path = new Path();
group = new GeometryGroup();
path.Data = group;
path.Stroke = new SolidColorBrush(Colors.White);
path.StrokeThickness = 3;
canvas.Children.Add(path);
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
int radius = 90;
for (double i = 0.0; i < 360.0; i += 1)
{
double angle = i * System.Math.PI / 180;
double x = (int)(100 + radius * System.Math.Cos(angle));
double y = (int)(100 + radius * System.Math.Sin(angle));
canvas.Dispatcher.Invoke(new Action(delegate
{
group.Children.Add(new EllipseGeometry(new Point(x, y), 5, 5));
}));
canvas.Refresh();
System.Threading.Thread.Sleep(1);
}
}
You might need three elements:
Outer circle (fill color will be light colors).
inner circle with transparent fill color.
Arc segment will be having thickness difference of radius between them.
Arc will be positioned at 45 angle , can be animated over the both circles.
This is just an idea, I might need to test it on my own.
You might get further by using an ArcSegment instead of an ellipse:
<PathFigure StartPoint="100,100">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="100,100" IsLargeArc="True"
SweepDirection="CounterClockwise" Point="200,200" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
It needs to be part of the PathFigure - where the start point is specified.
You can then animate Point which is the end point of the arc to go from the start point through 360 degrees to the end point.

Animating/Moving a shape to specific coordinates?

I'm randomly generating two rectangles, the first rectangle stays where it is but I want to move/animate the second rectangle so that it's bottom right corner is equalto the position occupied by the top left corner of the first, atm what I've basically got is;
if(count != 300)
{
rect2.X = count + (rect1.X - 100);
rect2.Y = count + (rect1.Y - 100);
count +=2
}
This is inside a timer event, but is obviously not quite what I want, I've tried multiple variations of the above but none seem to do the job.
I'm sure there'll be an annoyingly simple answer to this.
Thanks in Advance.
Assuming top left of the screen is {0,0} and bottom right is {screenWidth, screenHeight}
I'm also assuming floating point coordinates here (i.e. Rect.X is a double or float). If they aren't, you'll need to do some work in step 2 below to make sure you move an even amount of pixels in each step.
Here's the basic algorithm, in pseudo code:
Calculate final location for your animating rectangles top-left corner: finalPos = {r1.x + r1.width, r1.y+r1.width}
Decide how many steps you want, and calculate how far the rectangle should move each step.
Let's take 100 steps for example. dx = r2.x - finalPos.x / 100, dy = r2.y - finalPos.y / 100
In each timer event, you just add dx and dy to your animating rectangle's position:
Like so:
if (currentStep < 300)
{
r2.x += dx;
r2.y += dy;
currentStep++;
}
else
{
// We're done
timer.Stop();
}
Of course, if you are using a graphical framework to draw stuff, you may already have access to an animation framework that can do the above for you.
If you are able to use WPF it is pretty straightforward:
In the window xaml:
<Window x:Class="WpfApplication6.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="600" Width="600">
<Window.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="AnimatedRect"
Storyboard.TargetProperty="(Canvas.Left)"
To="{Binding ElementName=FixedRect, Path=(Canvas.Left)}" Duration="0:0:1"/>
<DoubleAnimation
Storyboard.TargetName="AnimatedRect"
Storyboard.TargetProperty="(Canvas.Top)"
To="{Binding ElementName=FixedRect, Path=(Canvas.Top)}" Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
<Canvas>
<Rectangle Name="FixedRect"
StrokeThickness="2"
Stroke="Black"
Width="{Binding R1.Width}"
Height="{Binding R1.Height}"
Canvas.Left="{Binding R1.X}"
Canvas.Top="{Binding R1.Y}"/>
<Rectangle Name="AnimatedRect"
StrokeThickness="2"
Stroke="Black"
Width="{Binding R2.Width}"
Height="{Binding R2.Height}"
Canvas.Left="{Binding R2.X}"
Canvas.Top="{Binding R2.Y}"/>
</Canvas>
</Window>
Codebehind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Random rnd = new Random();
Rect r1 = new Rect();
r1.X = rnd.Next(500);
r1.Y = rnd.Next(500);
r1.Width = rnd.Next(50,100);
r1.Height = rnd.Next(50, 100);
R1 = r1;
Rect r2 = new Rect();
r2.X = rnd.Next(500);
r2.Y = rnd.Next(500);
r2.Width = rnd.Next(50, 100);
r2.Height = rnd.Next(50, 100);
R2 = r2;
DataContext = this;
}
public Rect R1 { get; set; }
public Rect R2 { get; set; }
}

Categories