UWP How to animate movement? - c#

I have a method that animates the position of a marker on a boardgame. If players rolls the dice and the dice return 4, then marker is moved to a specific position on the board. The animation works fine.
However, the marker moves in a straight line - I would like the marker to move through all the position until it reach the end position.
Here is my int[,]
this.Path = new int[,] { { 0, 0 },{ 40, 50 }, { 95, 45 }, {130,0 }, { 110,-60 }, { 60, -100 }, { 0,-140 }, { -40, -200 }, { -30,-280 }};
It holds the X and Y position relative to startposition(margin).
And the animation method - how can I change it so the player rolls eg. 2 it should animate position 0 and 1 on the array.
private void AnimatePlayerMovement(Player player, int eyes)
{
//This will hold hour animation
Piece.RenderTransform = new CompositeTransform();
//New storyboard
Storyboard storyboard = new Storyboard();
//New DoubleAnimation - Y
DoubleAnimation translateYAnimation = new DoubleAnimation();
translateYAnimation.From = this.Path[player.position - eyes , 1];
translateYAnimation.To = this.Path[player.position , 1];
translateYAnimation.EasingFunction = new ExponentialEase();
translateYAnimation.EasingFunction.EasingMode = EasingMode.EaseOut;
translateYAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(500));
Storyboard.SetTarget(translateYAnimation, Piece);
Storyboard.SetTargetProperty(translateYAnimation, "(UIElement.RenderTransform).(CompositeTransform.TranslateY)");
storyboard.Children.Add(translateYAnimation);
//New DoubleAnimation - X
DoubleAnimation translateXAnimation = new DoubleAnimation();
translateXAnimation.From = this.Path[player.position - eyes, 0];
translateXAnimation.To = this.Path[player.position, 0];
//translateXAnimation.From = this.Path[player.position - eyes, 0];
//translateXAnimation.To = this.Path[player.position, 0];
translateXAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(500));
Storyboard.SetTarget(translateXAnimation, Piece);
Storyboard.SetTargetProperty(translateXAnimation, "(UIElement.RenderTransform).(CompositeTransform.TranslateX)");
storyboard.Children.Add(translateXAnimation);
//executing the storyboard
storyboard.Begin();
}

Well, here's an ugly solution. It will work playing animation sequentially, one after one.
Let's assume the Marker is a Rectangle, so, we add some animation on it's TranslateX and TranslateY:
<Page.Resources>
<Storyboard x:Name="PathAnimationStory"
Completed="Story_Completed">
<DoubleAnimation x:Name="anim_x"
Duration="0:0:0.5"
Storyboard.TargetName="TargetTranform"
Storyboard.TargetProperty="X"/>
<DoubleAnimation x:Name="anim_y"
Duration="0:0:0.5"
Storyboard.TargetName="TargetTranform"
Storyboard.TargetProperty="Y"/>
</Storyboard>
</Page.Resources>
<Rectangle
Width="50"
Height="50"
Fill="Red"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="TargetTranform" X="0" Y="0"/>
</Rectangle.RenderTransform>
</Rectangle>
Notice the Completed event handled in the Storyboard. We need to chain the animation. To do that, handle the Completed event when the animation stops, it will check if any other points are to be gone through and start the animation again if any:
private void MoveMarker(params double[] co_ordinates)
{
PathAnimationStory.Stop();
if (co_ordinates.Length == 0)
return;
/// the number of co_ordinates must be even
if (co_ordinates.Length % 2 == 1)
return;
Co_ordinates = co_ordinates;
remaining_nodes = co_ordinates.Length / 2;
Story_Completed(default, default);
}
private void Story_Completed(object sender, object e)
{
if(remaining_nodes > 0)
{
int next_node = (int)(Co_ordinates.Length / 2 - remaining_nodes);
anim_x.To = Co_ordinates[2 * next_node];
anim_y.To = Co_ordinates[2 * next_node + 1];
remaining_nodes--;
PathAnimationStory.Begin();
}
}
All done, now let's check our code:
MoveMarker(100, 0, 100, 100, 0, 100, 100, 0);
gives this output:
Which I think somewhat meets your requirement.
Hope that helps.

Related

C# WPF) DropShadowEffect is sometimes missing when dynamically adding a control

foreach (DataRow dr in dt.Rows)
{
Rectangle rectangle_timeline = new Rectangle();
rectangle_timeline.Height = 19;
rectangle_timeline.Cursor = Cursors.Hand;
rectangle_timeline.Effect = new DropShadowEffect
{
Color = new Color { A = 255, R = 0, G = 0, B = 0 },
Direction = 315,
ShadowDepth = 5,
Opacity = 1
};
Grid_Timeline.Children.Add(rectangle_timeline);
}
I dynamically add a Rectangle with above simple code as shown image.
However, sometimes, randomly, there're rectangles without DropShadowEffect like yellow rectangles and 1 blue rectangle at the lowest.
As you see the code, if a rectangle is added, the code for DropShadowEffect should have to be worked.
I'm wondering why this is happening.
Thank you !
XAML code added-
<Grid x:Name="Grid_Timeline" ScrollViewer.VerticalScrollBarVisibility="Auto" UseLayoutRounding="True" Width="1159" HorizontalAlignment="Left" VerticalAlignment="Top" SnapsToDevicePixels="True">
</Grid>
Minimal code to re-produce is added-
private void Window_Loaded(object sender, RoutedEventArgs e)
{
int count_each_category = 0;
string current_therapeutic_category = String.Empty;
foreach (DataRow dr_test in dt.Rows)
{
if (dr_test != null)
{
if (current_category == String.Empty)
{
count_each_category++;
current_category = dr_test["category"].ToString();
}
else
{
if (current_category == dr_test["category"].ToString())
{
// empty
}
else
{
count_each_category++;
current_category = dr_test["category"].ToString();
}
}
Rectangle rectangle_test = new Rectangle();
rectangle_test.HorizontalAlignment = HorizontalAlignment.Left;
rectangle_test.VerticalAlignment = VerticalAlignment.Top;
rectangle_test.Margin = new Thickness(119 + (((Convert.ToDateTime(dr_test["date"]) - DateTime.Today.AddYears(-10)).TotalDays) * 0.27), count_each_category * 50, 0, 0);
rectangle_test.Width = 0.27 * Convert.ToInt32(dr_test["howlong"]);
rectangle_test.Height = 19;
rectangle_test.Effect = new DropShadowEffect
{
Color = new Color { A = 255, R = 0, G = 0, B = 0 },
Direction = 315,
ShadowDepth = 5,
Opacity = 1
};
rectangle_test.Fill = Brushes.LightGreen;
Grid_Timeline.Children.Add(rectangle_test);
}
}
}
XAML code of WPF Window
<Window x:Class="OperationSWdummy.Green_Timeline"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:OperationSWdummy"
mc:Ignorable="d"
Title="Green_Timeline" Width="1165" Background="White" ResizeMode="CanMinimize" ScrollViewer.VerticalScrollBarVisibility="Auto" UseLayoutRounding="True" SnapsToDevicePixels="True" Loaded="Window_Loaded">
<Grid x:Name="Grid_Timeline" ScrollViewer.VerticalScrollBarVisibility="Auto" UseLayoutRounding="True" Width="1159" HorizontalAlignment="Left" VerticalAlignment="Top" SnapsToDevicePixels="True">
</Grid>
</Window>
Raw data of dt (DataTable)
date datetime
category NVARCHAR
howlong int
date category howlong
2015-01-25 HHH 60
2014-04-03 AAA 60
2015-01-25 DDD 60
2014-04-03 UUU 60
2015-01-25 CCC 60
2015-11-07 PPP 30
2015-01-25 TTT 60
2015-11-07 MMM 30
2015-02-22 MMM 30
2015-11-07 VVV 8
Result of above minimal code
Another minimal code to create rectangles randomly-
for (int k = 0; k < 191; k++)
{
Rectangle rec_test = new Rectangle();
rec_test.Margin = new Thickness(random_margin.Next(100, 1000), 29 * (k % 10), 0, 0);
rec_test.Width = random_width.Next(10, 40);
rec_test.HorizontalAlignment = HorizontalAlignment.Left;
rec_test.VerticalAlignment = VerticalAlignment.Top;
rec_test.Height = 14;
rec_test.Cursor = Cursors.Hand;
rec_test.Effect = new DropShadowEffect
{
Color = new Color { A = 255, R = 0, G = 0, B = 0 },
Direction = 315,
ShadowDepth = 5,
Opacity = 1
};
rec_test.Fill = Brushes.Green;
Grid_Timeline.Children.Add(rec_test);
}
Have you tried to use Canvas to place your rectangles?
Having Canvas to place your shapes such as rectangles, circles, or even graphical paths with random locations is better than placing on fixed layout container such as Grid or StackPanel.
It is quite known that if you just placed shapes with effects such as DropShadow on fixed layout (with arbitrary X,Y locations) such as Grid and StackPanel, your shapes may be clipped. Because the shapes edges will always be checked for pixel boundaries at render time, and often this is caused by overlapped rendering calculations when it tries to recalculate edges of fixed layout controls.
Could be a driver problem with your graphics hardware. Try disabling hardware acceleration and see if it helps.
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly;
}
}
In general I advise against using the DropShadowEffect as it will have a significantly negative impact on rendering performance.

Drawing line attached to two shapes

NOTE: I'm not looking for a XAML Solution.
I'm having trouble figuring out how to attach a line to two shapes. The best visible representation of what I'm looking for would be two balls attached to both ends of a straight stick. The problem I'm having is on how to display the line which is dependent on both the positions of ball01's and ball02's center position. As of now, both balls display as I want it, but when ball02 moves away from ball01 (ball02 starts off centered on ball01), the line is not visible.
ball01 = new Ellipse() { Height = BIG_SIZE, Width = BIG_SIZE };
ball01.Fill = baseBrush;
ball01.SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased);
setBall01X(e.GetPosition(canvas).X - (BIG_SIZE / 2));
setBall01Y(e.GetPosition(canvas).Y - (BIG_SIZE / 2));
Canvas.SetLeft(ball01, getBall01X());
Canvas.SetTop(ball01, getBall01Y());
canvas.Children.Add(ball01);
ball02 = new Ellipse() { Height = SMALL_SIZE, Width = SMALL_SIZE };
ball02.Fill = childBrush;
ball02.SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased);
setBall02X(e.GetPosition(canvas).X - (SMALL_SIZE / 2));
setBall02Y(e.GetPosition(canvas).Y - (SMALL_SIZE / 2));
Canvas.SetLeft(ball02, getBall02X());
Canvas.SetTop(ball02, getBall02Y());
canvas.Children.Add(ball02);
// line's X's and Y's are to point to the center of both balls
// Regardless of where the balls move.
line01 = new Line()
{
X1 = getBall01X() + (BIG_SIZE / 2),
Y1 = getBall01Y() + (BIG_SIZE / 2),
X2 = getBall02X() + (SMALL_SIZE / 2),
Y2 = getBall02Y() + (SMALL_SIZE / 2)
};
line01.Fill = baseBrush;
line01.SnapsToDevicePixels = true;
line01.SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased);
line01.StrokeThickness = 2;
// Canvas.Set???
canvas.Children.Add(line01);
Instead of using Ellipse and Line controls and positioning them by Canvas.Left and Canvas.Top you may prefer to use three Path controls with appropriate geometries. Especially the EllipseGeometry provides far easier handling of its center point, compared to an Ellipse control.
private EllipseGeometry ball1Geometry = new EllipseGeometry();
private EllipseGeometry ball2Geometry = new EllipseGeometry();
private LineGeometry lineGeometry = new LineGeometry();
public MainWindow()
{
InitializeComponent();
canvas.Children.Add(new Path
{
Stroke = Brushes.Black,
Data = ball1Geometry
});
canvas.Children.Add(new Path
{
Stroke = Brushes.Black,
Data = ball2Geometry
});
canvas.Children.Add(new Path
{
Stroke = Brushes.Black,
Data = lineGeometry
});
}
...
private void UpdateDrawing(
Point ball1Position, double ball1Radius,
Point ball2Position, double ball2Radius)
{
ball1Geometry.RadiusX = ball1Radius;
ball1Geometry.RadiusY = ball1Radius;
ball1Geometry.Center = ball1Position;
ball2Geometry.RadiusX = ball2Radius;
ball2Geometry.RadiusY = ball2Radius;
ball2Geometry.Center = ball2Position;
lineGeometry.StartPoint = ball1Position;
lineGeometry.EndPoint = ball2Position;
}
Then you may also prefer to do it the WPF way and create the Paths in XAML:
<Canvas>
<Path Stroke="Black">
<Path.Data>
<EllipseGeometry x:Name="ball1Geometry"/>
</Path.Data>
</Path>
<Path Stroke="Black">
<Path.Data>
<EllipseGeometry x:Name="ball2Geometry"/>
</Path.Data>
</Path>
<Path Stroke="Black">
<Path.Data>
<LineGeometry x:Name="lineGeometry"/>
</Path.Data>
</Path>
</Canvas>
I think you'd better draw in two steps :
1) add the 3 figures and store them (when building your window).
2) update the coordinates in an animating loop.
It will be faster / handier than clearing/filling the canvas on each frame.
For your line issue : hook it on circle 1's center, and have it go to circle 2's center :
// new line coordinates :
X1 = Y1 = 0
X2 = Balle02X - Balle01X + ( SMALL_SIZE / 2 )
Y2 = Balle02Y - Balle01Y + ( SMALL_SIZE / 2 )
Canvas.SetTop ( line01, Balle01X + (BIG_SIZE / 2) )
Canvas.SetLeft( line01, Balle01Y + (BIG_SIZE / 2) )

Animate Grid from one position to another

I have a grid of images and buttons, and I want to animate motion from one position to another (actually a few spaces to the left) automatically, but it hasn't worked. I've tried using a storyboard in xaml and programatically as in the code below, but its now working. Please help!!!
public static void MoveTo(Grid target)
{
Canvas.SetLeft(target, 0);
var top = Canvas.GetTop(target);
var left = Canvas.GetLeft(target);
TranslateTransform trans = new TranslateTransform();
target.RenderTransform = trans;
double newX = (double)(left - 300);
double newY = (double)top;
DoubleAnimation anim1 = new DoubleAnimation(top, -15, TimeSpan.FromSeconds(10));
//DoubleAnimation anim1 = new DoubleAnimation(top, newY - top, TimeSpan.FromSeconds(10));
DoubleAnimation anim2 = new DoubleAnimation(left, newX - left, TimeSpan.FromSeconds(10));
anim1.AutoReverse = true;
anim1.RepeatBehavior = RepeatBehavior.Forever;
trans.BeginAnimation(TranslateTransform.XProperty, anim1);
trans.BeginAnimation(TranslateTransform.YProperty, anim2);
}
totally TranslateTransform isnt good for that you want .better to use thinkness animation
i advise you dont use canavas and change it to grid but if you use canavas use this code
private void Animationsss(Grid grd)
{
//create an animation
DoubleAnimation da = new DoubleAnimation();
//set from animation to start position
//dont forget set canvas.left for grid if u dont u will get error
da.From = Canvas.GetLeft(grd);
//set second position of grid
da.To = -100;
//set duration
da.Duration = new Duration(TimeSpan.FromSeconds(2));
//run animation if u want stop ,start etc use story board
grd.BeginAnimation(Canvas.LeftProperty, da);
}
if you use grid code is this :
private void Animation(Grid grd)
{
ThicknessAnimation ta = new ThicknessAnimation();
//your first place
ta.From = grd.Margin;
//this move your grid 1000 over from left side
//you can use -1000 to move to left side
ta.To = new Thickness(1000, 0, 0, 0);
//time the animation playes
ta.Duration = new Duration(TimeSpan.FromSeconds(10));
//dont need to use story board but if you want pause,stop etc use story board
grd.BeginAnimation(Grid.MarginProperty, ta);
}
you can use opacity animation for fade your grid ... it show good if move and fade !
private void Animationsss(Grid grd)
{
DoubleAnimation da = new DoubleAnimation(1, 0, new Duration(TimeSpan.FromSeconds(2)));
grd.BeginAnimation(Grid.OpacityProperty, da);
}
if there isnt any reason u can change canavas to grid and also better use TranslateTransform for resize controls like code below this code resize control if mouse enter in it :
private void grid1_MouseEnter(object sender, MouseEventArgs e)
{
//at first its normal size
ScaleTransform st = new ScaleTransform(1, 1);
//animation size to 1.25 persent of real size
DoubleAnimation da = new DoubleAnimation(1,1.25, new Duration(TimeSpan.FromSeconds(2)));
//set transform to control
grid1.RenderTransform = st;
//animation transform now From Y And X
st.BeginAnimation(ScaleTransform.ScaleXProperty, da);
st.BeginAnimation(ScaleTransform.ScaleYProperty, da);
}
if you animation for width or height do same work like scale transform :)
hope i can help you ...:))
leave comment for me please
You can also use xaml for this:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="TestBed.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Window.Resources>
<Storyboard x:Key="MoveGrid">
<ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)" Storyboard.TargetName="grid">
<EasingThicknessKeyFrame KeyTime="0:0:1" Value="-300,0,0,0"/>
</ThicknessAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button">
<BeginStoryboard x:Name="MoveGrid_BeginStoryboard" Storyboard="{StaticResource MoveGrid}"/>
</EventTrigger>
</Window.Triggers>
<Canvas>
<Grid x:Name="grid" Height="300" Width="300" Background="Black">
<Button x:Name="button" Content="Move the gird!" Height="30" Margin="10,0" />
</Grid>
</Canvas>
</Window>

restoring image back to same position after tombstoning in WP7

I m using MouseDragElementBehavior in my WP7 application to drag an image down the canvas. I m able to get the coordinates (X,Y positions) after the image dragging. But I want to retain the same image position after tombstoning also.
private void MouseDragElementBehavior_Dragging(object sender, MouseEventArgs e)
{
Point currentPos = e.GetPosition(image1);
if (currentPos.X < 190)
{
double targetOffset = 700;
DoubleAnimation animation = new DoubleAnimation();
animation.EasingFunction = new CircleEase();
animation.Duration = new Duration(new TimeSpan(0, 0, 10));
animation.From = TextScroll.AnimatableOffset;
animation.To = targetOffset;
Storyboard.SetTarget(animation, TextScroll);
Storyboard.SetTargetProperty(animation, new PropertyPath("(AnimatableScrollViewer.AnimatableOffset)"));
Storyboard storyboard = new Storyboard();
storyboard.Children.Add(animation);
storyboard.Begin();
}
App app = (App)Application.Current;
app.current_X = currentPos.X.ToString();
app.current_Y = currentPos.Y.ToString();
TextScroll.AnimatableOffset = -700;
}
I have stored and retrived the values from isolated storage for tombstoning.
private void LoadSettings()
{
var settings = IsolatedStorageSettings.ApplicationSettings;
current_X = settings["Xpos"].ToString();
current_Y = settings["Ypos"].ToString();
}
private void SaveSettings()
{
var settings = IsolatedStorageSettings.ApplicationSettings;
settings.Add("Xpos", current_X);
settings.Add("Ypos",current_Y);
settings.Save();
}
Now I would like to use the values to position the image at the same coordinates as before tombstoning. I dont know how to position the image with the X and Y coordinates provided.
Here is the XAML code where I use the image.
<Canvas Margin="12,0,3,-834" Grid.Row="1">
<Image Height="800" Source="37.jpg" Stretch="Fill" Width="480" Canvas.Left="-11" x:Name="image1">
<i:Interaction.Behaviors>
<el:MouseDragElementBehavior ConstrainToParentBounds="True" Dragging="MouseDragElementBehavior_Dragging" />
</i:Interaction.Behaviors>
</Image>
</Canvas>
First, Point currentPos = e.GetPosition(image1); is getting the position of the mouse relative to the image. Maybe you want to get the position relative to the canvas instead?
Or, you can use this to get the position within the canvas:
canvas1.GetLeft(image1);
canvas1.GetTop(image1);
Then, you can set the position of something within a canvas like this:
canvas1.SetLeft(image1, x);
canvas1.SetTop(image1, y);
To do that, you would need to name your Canvas:
<Canvas x:Name="canvas1" Margin="12,0,3,-834" Grid.Row="1">

How to create a changing button based on an ellipse in Silverlight?

I have the following problem to solve: I have some ellipses in my xaml that work as buttons, and some of them may open in 2 new buttons when clicked. I put them in separate canvas, in a way that this buttons to be generated already exist with opacity 0. What I want is to have an effect to set this buttons opacity to 1 when I click their parent button, in a transition. How can I achieve that?
C#
private void ExpandHarborButtons(object sender, MouseButtonEventArgs e)
{
Ellipse thisPath = (Ellipse)sender;
String test = (String)thisPath.DataContext;
for(int i = 0; i < DoubleHarbors.Children.Count; i++)
{
Ellipse button = (Ellipse)VisualTreeHelper.GetChild(DoubleHarbors, i);
if (test.Contains((String)button.DataContext))
{
button.Opacity = 1;
}
}
}
That's the way I'm doing right now, but it doesn't work as I want. The buttons are shown, but not with the effect I told before.
Create a DoubleAnimation and start it from the click. Something like this:
<Storyboard x:Name="fadeIn">
<DoubleAnimation Storyboard.TargetName="ButtonName" From="0.0" To="0.1" Duration="0:0:0.5"
Soryboard.TargetProperty="Opacity"/>
</Storyboard>
Then in Code:
fadeIn.Begin();
--EDIT--
Here's how to do an animation in C#. It's actually easier to define in XAML, but if this is really what you want, this is a way to do it.
Storyboard sb = new Storyboard();
DoubleAnimation da = new DoubleAnimation();
da.From = 0;
da.To = 1.0;
da.Duration = new Duration(new TimeSpan(0, 0, 0, 0, 500));
sb.Children.Add(da);
Storyboard.SetTarget(sb, sender as Button);
Storyboard.SetTargetProperty(Button1, new PropertyPath("Opacity"));
sb.Begin();

Categories

Resources