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
Related
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();
}
I need to make a border slide from the bottom of the screen into view, however i'm having issues getting the ActualHeight of my border control. Because this is animation code i'm putting it in the code-behind as it's the Views responsibility.
My Border has got it's Loaded event tied to this:
private void NotifcationWindow_Loaded(object sender, RoutedEventArgs e)
{
SlideFromBottom(sender);
}
The Sender is the Border object, so the SlideFromBottom method SHOULD be able to use this object and get it's height as it has already been rendered.
public void SlideFromBottom(object sender)
{
//The Notification container
Border notification = (sender as Border);
//The screen size
var workingArea = SystemParameters.WorkArea;
Storyboard sb = new Storyboard();
var animation = new DoubleAnimation()
{
BeginTime = TimeSpan.FromSeconds(0),
Duration = TimeSpan.FromSeconds(5),
// Get the height and turn it to a negative value, and add screen height.
From = (notification.ActualHeight * -1),
//Slide the border into view
To = notification.ActualHeight
};
Storyboard.SetTarget(animation, notification);
Storyboard.SetTargetProperty(animation, new PropertyPath("(Margin.Bottom)"));
sb.Begin();
}
I'm not receiving any errors, but the animation isn't playing, have I got something wrong? The Border is Vertically Aligned to the bottom, so the negative margin should take it off the screen.
The reason why you're not seeing anything is that you haven't added the animation to the storyboard:
sb.Children.Add(animation);
Then there are some more problems. Such that a part of the margin cannot be animated separately. You would need a ThicknessAnimation.
But there is an easier solution. Use the RenderTransform. If you give your border the following render transform:
<Border>
<Border.RenderTransform>
<TranslateTransform/>
</Border.RenderTransform>
</Border>
, then you can animate it as follows:
// ...
var animation = new DoubleAnimation()
{
BeginTime = TimeSpan.FromSeconds(0),
Duration = TimeSpan.FromSeconds(5),
From = notification.ActualHeight,
To = 0
};
Storyboard.SetTarget(animation, notification);
Storyboard.SetTargetProperty(animation, new PropertyPath("RenderTransform.Y"));
sb.Children.Add(animation);
// ...
So my question is pretty simple:
I have a StackPanel with two objects: a button and an rectangle (which is filled with an image by clicking the button). Now, an additional thing that has to happen is when the button is clicked, the entire stackpanel has to be flipped upside down AND it has to stay in the same place.
I have tried with the RenderTransformOrigin-property set on "0.5,0.5"
but I haven't got any luck with this .. either the stackpanel moved to another location or it disappeared (out of bounds)
<Window x:Class="WpfApplication1.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">
<Grid>
<StackPanel Name="pnlFlip" RenderTransformOrigin="0.5,0.5">
<Button Content="Test" Margin="200,78,197,-78" Name="btnTest" Click="btnTest_Click" Height="30"/>
<Rectangle Margin="175,146,162,-239" Name="rectTest" Fill="Red" Height="127"/>
</StackPanel>
</Grid>
</Window>
The code for my buttons is like this:
private int scale = 1;
private int angle = 180;
private void btnTest_Click(object sender, RoutedEventArgs e)
{
ImageBrush img = new ImageBrush();
img.ImageSource = new BitmapImage(new Uri("pack://application:,,,/WpfApplication1;component/Resources/Images/logo.jpg"));
rectTest.Fill = img;
//Trying a ScaleTranfsformObject
ScaleTransform st = new ScaleTransform();
if(scale == 1)
{
scale = -1;
st.ScaleY = scale;
}
else
{
scale = 1;
st.ScaleY = scale;
}
//Trying a RotateTransform Object
RotateTransform rt = new RotateTransform();
if(angle == 180)
{
rt.Angle = angle;
angle += 180;
}
else
{
rt.Angle = angle;
angle -= 180;
}
pnlFlip.RenderTransform = rt;
}
So what am I doing wrong/how do I fix this?
Using storyboard is better approach than setting this directly in code in click handler. You will get smooth changes in UI.
Following will achieve your objective with XAML only, without any code behind. Setting RenderTransformOrigin in the StackPanel is what keeps it in the center after running the rotate transform.
To activate the animation on click, we just add a Button.Trigger handler which has storyboard on DoubleAnimation to change the angle to 180 degree in the StackPanel. Rest of the XAML is what you had before.
<StackPanel x:Name="pnlFlip" RenderTransformOrigin="0.5,0.5">
<StackPanel.RenderTransform>
<RotateTransform />
</StackPanel.RenderTransform>
<Button Content="Test" Margin="200,78,197,-78" Name="btnTest" Height="30">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="180" Storyboard.TargetName="pnlFlip" Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<Rectangle Margin="175,146,162,-239" Name="rectTest" Fill="Red" Height="127"/>
</StackPanel>
This works fine on my computer (minus the code for the image in your Resources.)
Here's how it looks when the application launches:
And here's how it looks after I click the button:
Which part of this falls short of your intentions?
Note that you can use a ternary operator to remove your conditional branches and cut down on code, like so:
angle = angle == 180 ? 0 : 180;
rt.Angle = angle;
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.
I have a rectangle and on my MouseMove event I want to transform the rectangle whenever the rectangle's width has changed.
I have code sorta like this:
private Rectangle _rectangle;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_rectangle = GetTemplatedChild("PART_RangeRectangle") as Rectangle;
if(_rectangle != null)
{
_rectangle.MouseMove += new MouseEventHandler(_rectangle_MouseMove);
}
}
private void _rectangle_MouseMove(object sender, MouseEventArgs e)
{
if(e.LeftButton == MouseButtonState.Pressed && _rectangle != null)
{
_rectangle.Width += 50;
_rectangle.RenderTransform = new TranslateTransform(-10, 0);
}
}
My Xaml looks sorta like this:
<Grid>
<Canvas>
<Rectangle Name="PART_RangeRectangle" StrokeThickness="5"
RenderTransformOrigin="0.5, 0.5" />
<Canvas>
</Grid>
When I first trigger the MouseMove event the translation occurs as expected. But this only occurs once. I am getting into that block of code and the width of the rectangle is updating fine, but I've yet to figure out why the transform is not updating.
You're replacing the old transform with an identical transform. You need to modify the existing transform and use += like you do with Width.
Example:
if (_rectangle.RenderTransform is TranslateTransform)
{
(_rectangle.RenderTransform as TranslateTransform).X -= 10;
}
else _rectangle.RenderTransform = new TranslateTransform(-10, 0);
You're not changing your transform. Assigning a RenderTransform does not move the rectangle, it sets an offset. You're not changing that offset after your first assignment.