I am struggling with this problem that should be fairly easy to solve. I want to simply specify the X1 and Y1 and X2 and Y2 coordinates for a line on a canvas and have a line drawn between those points over a period of 1 second.
My canvas name is cnvML and the name of the line is L1. This needs to be done at runtime, so I would prefer to use C#, but if I can modify a named storyboard that is created in XAML, that is fine. I am assuming I need a Storyboard, although I haven't been able to get that to work. Here is what I have tried over the last 2 days:
Scoured google for lots of examples. I have been able to animate lines, rectangles, and ellipses across the canvas by changing the X and Y properties of the Animation. I have not been able to figure out how to transform the X2 and Y2 properties of the line itself. I have seen some examples of this for older frameworks, but the constructors for some of the methods are different and will not work for Windows 10 apps.
I have tried building an animation in Blend, but it only works for animating lines that are built at design time. It gives me an error if I don't specify the Target of the animation. Also, doing it in Blend isn't really accomplishing the transform the way I want. If I start at time 0 with a very short line, then at 1 second, I stretch the line out to the desired length, it is changing the scale of the line, thereby changing the apparent stroke thickness of the path object.
I would really appreciate it if someone can show me how to do this, maybe even without using a storyboard. And I would like it to be all in C#. The canvas, named cnvML will exist at design time, but the lines will be created at run time.
Again, I would like to just pass to a method the X1, Y1, X2, and Y2 to a method. Then the line would be drawn and take 1 second to be drawn between those 2 points.
Thanks, David
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.