How to Animate a Line on a Canvas in C#

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



Leave a reply



Submit