How do you animate a line on a canvas in C#?
You will need to use a Storyboard
and animate the Line.X2
and Line.Y2
Properties. See if this works for you.
MainWindow.xaml
<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">
<Canvas Name="myCanvas">
<Button Canvas.Left="248" Canvas.Top="222" Content="Button" Height="23" Name="button1" Width="75" Click="button1_Click" />
</Canvas>
</Window>
Button Click Event
private void button1_Click(object sender, RoutedEventArgs e)
{
Line line = new Line();
myCanvas.Children.Add(line);
line.Stroke = Brushes.Red;
line.StrokeThickness = 2;
line.X1 = 0;
line.Y1 = 0;
Storyboard sb = new Storyboard();
DoubleAnimation da = new DoubleAnimation(line.Y2 , 100, new Duration(new TimeSpan(0, 0, 1)));
DoubleAnimation da1 = new DoubleAnimation(line.X2, 100, new Duration(new TimeSpan(0, 0, 1)));
Storyboard.SetTargetProperty(da, new PropertyPath("(Line.Y2)"));
Storyboard.SetTargetProperty(da1, new PropertyPath("(Line.X2)"));
sb.Children.Add(da);
sb.Children.Add(da1);
line.BeginStoryboard(sb);
}
Animating a line while drawing in wpf
Your code could be simplified like shown below. In particular you should add handlers for the MouseLeftButtonDown
and MouseLeftButtonUp
events instead of the more general MouseDown
and MouseUp
events. Note also that you would typically capture the mouse, and that the only purpose for having a mouse up handler is to release the mouse capture.
XAML:
<Canvas Background="Transparent"
MouseLeftButtonDown="Canvas_MouseLeftButtonDown"
MouseLeftButtonUp="Canvas_MouseLeftButtonUp"
MouseMove="Canvas_MouseMove"/>
Code behind:
private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var canvas = (Canvas)sender;
if (canvas.CaptureMouse())
{
var startPoint = e.GetPosition(canvas);
var line = new Line
{
Stroke = Brushes.Blue,
StrokeThickness = 3,
X1 = startPoint.X,
Y1 = startPoint.Y,
X2 = startPoint.X,
Y2 = startPoint.Y,
};
canvas.Children.Add(line);
}
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
var canvas = (Canvas)sender;
if (canvas.IsMouseCaptured && e.LeftButton == MouseButtonState.Pressed)
{
var line = canvas.Children.OfType<Line>().LastOrDefault();
if (line != null)
{
var endPoint = e.GetPosition(canvas);
line.X2 = endPoint.X;
line.Y2 = endPoint.Y;
}
}
}
private void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
((Canvas)sender).ReleaseMouseCapture();
}
How can I animate an image from one point to another programmatically using a WPF canvas?
i continued to searching and i found the solution.
I used to DoubleAnimation instance.
The working code is below.
public void move( Image target, double oldX, double oldY, double newX,
double newY,int time)
{
TranslateTransform trans = new TranslateTransform();
target.RenderTransform = trans;
DoubleAnimation anim1 = new DoubleAnimation(oldY, newY,
TimeSpan.FromSeconds(time));
anim1.RepeatBehavior = RepeatBehavior.Forever;
trans.BeginAnimation(TranslateTransform.YProperty, anim1);
DoubleAnimation anim2 = new DoubleAnimation(oldX, newX,
TimeSpan.FromSeconds(time));
anim2.RepeatBehavior = RepeatBehavior.Forever;
trans.BeginAnimation(TranslateTransform.XProperty, anim2);
}
Trying to Animate a group of lines
Your code is mostly fine. I only had to make a minor change to get it working. I created a new window called new_Window
to test in and created a Canvas
object on it called canMain
.
Then I just added one line of code to yours: Inside your for
loop I had to add line[i] = new Line();
because the line objects were null at that point.
public new_Window()
{
InitializeComponent();
AnimateThis();
}
private void AnimateThis()
{
canMain.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
canMain.Margin = new Thickness(50, 0, 0, 0);
Line[] line = new Line[6];
Storyboard sb;
DoubleAnimation da, da1;
for (int i = 0; i < line.Count(); i++)
{
line[i] = new Line();
line[i].Stroke = Brushes.Red;
line[i].StrokeThickness = 1;
line[i].X1 = i+11;
line[i].Y1 = i+11;
canMain.Children.Add(line[i]);
sb = new Storyboard();
da = new DoubleAnimation(line[i].Y1, 30, new Duration(TimeSpan.FromSeconds(0.5)));
da1 = new DoubleAnimation(line[i].X1, 30, new Duration(TimeSpan.FromSeconds(0.5)));
Storyboard.SetTargetProperty(da, new PropertyPath("(Line.Y2)"));
Storyboard.SetTargetProperty(da1, new PropertyPath("(Line.X2)"));
sb.Children.Add(da);
sb.Children.Add(da1);
line[i].BeginStoryboard(sb);
}
}
I added the Canvas
to the window first because I wanted to make sure that there was a surface ready to be rendered on.
C# Windows 10 App - Draw Line Slowly From X1, Y1 to X2,Y2
Disclaimer: I don't WPF, so this was cobbled together from examples found elsewhere.
Using a storyboard you can animate the X2
and Y2
properties like this:
<Storyboard>
<DoubleAnimation Storyboard.TargetName="line1" Storyboard.TargetProperty="X2" From="10" To="100" Duration="0:0:1.0"/>
<DoubleAnimation Storyboard.TargetName="line1" Storyboard.TargetProperty="Y2" From="10" To="100" Duration="0:0:1.0"/>
</Storyboard>
How you kick that off is up to you. I just added it to a button control's Button.Click
event trigger.
What this does is the equivalent of a linear interpolation across time of the target property, so in this example over the course of a second the values of X1
and Y1
will vary smoothly between 11
and 100
. This will give the appearance of the line extending from its origin point (at 10,10
in my test version) along the line towards the final endpoint at 100,100
.
Here's the full XAML of my test. Since I don't WPF I expect that there are probably several things I could have done better :)
<Window x:Class="AnimTest.MainWindow"
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:AnimTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Canvas HorizontalAlignment="Left" Height="300" Margin="10,10,0,0" VerticalAlignment="Top" Width="426">
<Line Name="line1" X1="10" Y1="10" X2="10" Y2="10" StrokeThickness="3" Stroke="Black"/>
</Canvas>
<Button Margin="441,279,10,10">
OK
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="line1" Storyboard.TargetProperty="X2" From="10" To="100" Duration="0:0:1.0"/>
<DoubleAnimation Storyboard.TargetName="line1" Storyboard.TargetProperty="Y2" From="10" To="100" Duration="0:0:1.0"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</Grid>
</Window>
I installed the Universal App tools and tried it out. Turns out they changed a whole bunch of things in Universal Apps as compared to WPF, including pretty much scrapping the concept of routed events in XAML. That sounds like a great way to annoy a vast number of developers, huh?
So now instead of being able to give a XAML-only answer that works for WPF, here's a two-part XAML + Code answer for Universal (on W10, VS2015):
First the XAML:
<Page
x:Class="UniversalApp1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UniversalApp1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<Storyboard x:Name="btnClick_SB" >
<DoubleAnimation Storyboard.TargetName="line1" Storyboard.TargetProperty="X2" From="10" To="400" Duration="0:0:1.0" EnableDependentAnimation="True"/>
<DoubleAnimation Storyboard.TargetName="line1" Storyboard.TargetProperty="Y2" From="10" To="400" Duration="0:0:1.0" EnableDependentAnimation="True"/>
</Storyboard>
</Grid.Resources>
<Canvas HorizontalAlignment="Left" Height="300" Margin="10,50,0,0" VerticalAlignment="Top" Width="426">
<Line Name="line1" X1="10" Y1="10" X2="10" Y2="10" StrokeThickness="3" Stroke="Black"/>
</Canvas>
<Button Margin="441,279,10,10" Click="Button_Click">OK</Button>
</Grid>
</Page>
And the (relevant part of the) code:
private void Button_Click(object sender, RoutedEventArgs e)
{
btnClick_SB.Stop();
btnClick_SB.Begin();
}
The btnClick_SB.Stop()
call ensures that you don't invoke the storyboard while it is already running, which is apparently a forbidden action that will throw an exception.
Animation(move Image) to right edge of Canvas
This is a pretty common problem. You can use the data bindings for the From
and To
properties of an animation only if it is not in an ControlTemplate
or a Style
. In these cases, the Animation
needs to be sealed (frozen), that's obviously not possible if some its properties change their values dynamically.
There is a workaround solution for this. It isn't neat, but it works. You just have to introduce an intermediate scale factor that you'll be animating with a hard-coded Animation
. But the actual value will be then calculated using a multiplying IMultiValueConverter
on a MultiBinding
.
Here is an example.
The converter could look like this:
public class MultiplyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values.OfType<double>().Aggregate(1.0, (current, t) => current * t);
}
// ConvertBack omitted...
}
And in XAML, you can use the Tag
of your object for holding the animated scale factor.
Something like this:
<Canvas x:Name="Canvas" xmlns:l="Your_converter_namespace" xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Canvas.Resources>
<l:MultiplyConverter x:Key="MultiplyConverter"/>
</Canvas.Resources>
<Rectangle Height="100" Width="100" Fill="Blue">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<DataTrigger Binding="{Binding IsAnimationStarted}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="Storyboard1">
<Storyboard TargetProperty="Tag">
<DoubleAnimation From="0" To="1" Duration="0:0:5"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="Storyboard1"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
<Rectangle.Tag>
<sys:Double>0</sys:Double>
</Rectangle.Tag>
<Canvas.Left>
<MultiBinding Converter="{StaticResource MultiplyConverter}">
<Binding Path="ActualWidth" ElementName="Canvas"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</Canvas.Left>
</Rectangle>
</Canvas>
You'll see that resizing the Canvas
won't cause the animation to start over again, but rather it will seamlessly continue.
WPF: Canvas, animating rectangle
You would simply animate the Canvas.Top
property.
The following example would animate the Top value from 100 to 200 within 1 second, then back from 200 to 100, forever.
<Canvas>
<Rectangle x:Name="rectangle" Canvas.Left="100" Canvas.Top="100"
Width="100" Height="100" Fill="Red"/>
</Canvas>
In code behind:
var topAnimation = new DoubleAnimation
{
From = 100,
To = 200,
Duration = TimeSpan.FromSeconds(1),
AutoReverse = true,
RepeatBehavior = RepeatBehavior.Forever
};
rectangle.BeginAnimation(Canvas.TopProperty, topAnimation);
Related Topics
Solution for Overloaded Operator Constraint in .Net Generics
Win32 API Function to Programmatically Enable/Disable Device
What Does the '=>' Syntax in C# Mean
Changing the User Agent of the Webbrowser Control
Random String Generator Returning Same String
How to Get the Exif Data from a File Using C#
C# Sort and Orderby Comparison
How to Verify If a Windows Service Is Running
How to Parse JSON Without JSON.Net Library
Differencebetween File.Readlines() and File.Readalllines()
Calling a Method Every X Minutes
Asynchronous Locking Based on a Key
What Exactly Are Unmanaged Resources