So I have a project where I am making a list of items that can be expanded to reveal additional information. So far I have come up with a way to rotate the angles 180 degrees to indicate if the information panel is open or closed, but I feel it looks a little bit artificial.
Gif demonstration here. Ideally, I would like my angle (<) to morph into pointing the opposite way. So let's say the coordinates are -5,0 0,5 and 5,0 (Red) I would like to animate them going to -5,5 0,0 and 5,5 (Blue).
I have been googling about this for a while, using keywords like animation / path / morph / storyboard. My theory now is that there could be a way where you could name each point in the Path data individually and add styles to them where they will move positions based on a common data trigger. Currently, it's all connected like shown below, and it's all linked up to a style that is attached to the Path. I would prefer to do all of this in XAML, so any help is appreciated.
<DataTrigger Binding="{Binding RevealAdditionalInformation}" Value="False">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="ArrowPointUp"/>
<BeginStoryboard Name="ArrowPointDown">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(LayoutTransform).(RotateTransform.Angle)" From="180" To="0" Duration="0:0:0.6"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
Update:
I've been trying to find a new way to draw up my Path rather than just
<Path Data="m 0 0 5 5 5 -5">. So now I have the same shape written in a different way. Perhaps this would be easier to manipulate each point separately. What's problematic here is that I am not allowed to name any of these points with Name="Something", so they will be hard to access.
<Path Stroke="Black" StrokeThickness="1" Grid.Column="1">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure IsClosed="False" StartPoint="0,0">
<PathFigure.Segments>
<PathSegmentCollection>
<LineSegment Point="5,5" />
<LineSegment Point="10,0" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
Update 2:
<DataTrigger Binding="{Binding RevealAdditionalInformation}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Name="AnimateTrue">
<Storyboard>
<PointAnimationUsingPath Duration="0:0:0.5" Storyboard.TargetProperty="Figures">
<PointAnimationUsingPath.PathGeometry>
<PathGeometry Figures="m -5 0 0 5 5 0"/>
</PointAnimationUsingPath.PathGeometry>
</PointAnimationUsingPath>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
This is another take I've been experimenting with, the problem here seems to be The Storyboard.Target property, not perhaps a different sort of Animation is needed. The path I am experimenting with this on is this, where FiguresTest is the Style that uses the above Data Trigger.
<Path x:Name="_testPath" Stroke="Black" StrokeThickness="1" Style="{StaticResource FiguresTest}">
<Path.Data>
<PathGeometry Figures="m -5 5 0 0 5 5"/>
</Path.Data>
</Path>
You can animate individual points on the Path. In order to do that, you need to define your Path in a manner in which each point you want to animate is a property of a named element. This of course results in more verbose definition, but gets you what you're after. For example:
<Path Stroke="Black" StrokeThickness="1">
<Path.Data>
<PathGeometry>
<PathFigure x:Name="Figure" StartPoint="-5,5">
<LineSegment x:Name="Line1" Point="0,0" />
<LineSegment x:Name="Line2" Point="5,5" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
Then the animation could look like this:
<BeginStoryboard Name="AnimateTrue">
<Storyboard>
<PointAnimation To="-5,0" Duration="0:0:0.5"
Storyboard.TargetName="Figure"
Storyboard.TargetProperty="StartPoint" />
<PointAnimation To="0,5" Duration="0:0:0.5"
Storyboard.TargetName="Line1"
Storyboard.TargetProperty="Point" />
<PointAnimation To="5,0" Duration="0:0:0.5"
Storyboard.TargetName="Line2"
Storyboard.TargetProperty="Point" />
</Storyboard>
</BeginStoryboard>
EDIT
Since you're struggling with getting things working, below is an example in which the Path is part of ToggleButton template. It seems to be a good choice given it will be used to toggle expansion state.
<ToggleButton>
<ToggleButton.Template>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<ControlTemplate.Resources>
<Duration x:Key="AnimationDuration">0:0:0.2</Duration>
</ControlTemplate.Resources>
<Border Background="Transparent">
<Path Stroke="Black" StrokeThickness="1" Width="10" Height="5">
<Path.Data>
<PathGeometry>
<PathFigure x:Name="Figure" StartPoint="0,0">
<LineSegment x:Name="Line1" Point="5,5" />
<LineSegment x:Name="Line2" Point="10,0" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Name="ToggleOn">
<Storyboard>
<PointAnimation To="0,5" Duration="{StaticResource AnimationDuration}"
Storyboard.TargetName="Figure"
Storyboard.TargetProperty="StartPoint" />
<PointAnimation To="5,0" Duration="{StaticResource AnimationDuration}"
Storyboard.TargetName="Line1"
Storyboard.TargetProperty="Point" />
<PointAnimation To="10,5" Duration="{StaticResource AnimationDuration}"
Storyboard.TargetName="Line2"
Storyboard.TargetProperty="Point" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard Name="ToggleOff">
<Storyboard>
<PointAnimation Duration="{StaticResource AnimationDuration}"
Storyboard.TargetName="Figure"
Storyboard.TargetProperty="StartPoint" />
<PointAnimation Duration="{StaticResource AnimationDuration}"
Storyboard.TargetName="Line1"
Storyboard.TargetProperty="Point" />
<PointAnimation Duration="{StaticResource AnimationDuration}"
Storyboard.TargetName="Line2"
Storyboard.TargetProperty="Point" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
Note that the animations are triggered only by ToggleButton.IsChecked property (as opposed to data triggers bound to view-model properties), so this control is potentially reusable (I suggest you move the template to a style first though). The only thing to do is to properly bind the ToggleButton.IsChecked property.
Related
Microsoft gives a nice example of a button spinning in place when clicked. I want to achieve the same effect with a path but following the same approach does not have the same result.
For instance this path bellow,
<Path x:Name="path"
Stretch="None"
Stroke="Black"
Fill="Blue"
RenderTransformOrigin="0.5,0.5"
Margin="100">
<Path.Data>
<PathGeometry Figures="M10,0 A10,10 0 0 1 7,7 L71,71 A100,100 0 0 0 100,0Z" />
</Path.Data>
<Path.RenderTransform>
<RotateTransform x:Name="MyPathTransform"
Angle="0" />
</Path.RenderTransform>
<Path.Triggers>
<EventTrigger RoutedEvent="Path.MouseDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="MyPathTransform"
Storyboard.TargetProperty="(RotateTransform.Angle)"
From="0.0"
To="360"
Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Path.Triggers>
</Path>
Invoking a click to that path does not make the latter spin in place, like the button does. Why does this happen and how may I fix it?
From your follow up comment, I understand the issue is that it does animate, but it does not spin in place like the button did in the tutorial you followed.
The rotation is very dependent on whatever your RenderTransformOrigin is set to, and this value is a percentage of the animating control's width and height. In your case, your Pathboundaries are actually somewhat large. The Path is filling the container except for the 100 Marign. Your animation origin is set to 0.5, 0.5 (the center). So it's in effect rotating around the center on the window. You can play around with that origin until you get in the center of your geometry, but an easier way to get and what you want would be to put your path in a Canvas. That way the Canvas will take up all the space and your Path will only be as large as your geometry. Then using a 0.5, 0.5 origin will do what you want.
<Canvas Margin="100">
<Path x:Name="path"
Stretch="None"
Stroke="Black"
Fill="Blue"
RenderTransformOrigin="0.5,0.5">
<Path.Data>
<PathGeometry Figures="M10,0 A10,10 0 0 1 7,7 L71,71 A100,100 0 0 0 100,0Z" />
</Path.Data>
<Path.RenderTransform>
<RotateTransform x:Name="MyPathTransform"
Angle="0" />
</Path.RenderTransform>
<Path.Triggers>
<EventTrigger RoutedEvent="Path.MouseDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="MyPathTransform"
Storyboard.TargetProperty="(RotateTransform.Angle)"
From="0.0"
To="360"
Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Path.Triggers>
</Path>
</Canvas>
Animate the Transform property of the Geometry. Thus you have more precise control over the pivot point of the rotation.
<Path x:Name="path" Stretch="None" Stroke="Black" Margin="100">
<Path.Data>
<PathGeometry Figures="M10,0 A10,10 0 0 1 7,7 L71,71 A100,100 0 0 0 100,0Z">
<PathGeometry.Transform>
<RotateTransform x:Name="rotateTransform"/>
</PathGeometry.Transform>
</PathGeometry>
</Path.Data>
<Path.Triggers>
<EventTrigger RoutedEvent="MouseDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="rotateTransform"
Storyboard.TargetProperty="Angle"
To="360" FillBehavior="Stop" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Path.Triggers>
</Path>
I strted to play around with shapes in wpf, I need the following: I have an image and I draw some shape, I want the the image would walk on the lines of this shape.
I mean like:
For example with the shape:
<Path Stroke="Black" StrokeThickness="5" Fill="Goldenrod">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure StartPoint="100,50" IsClosed="True">
<LineSegment Point="140,60"/>
<LineSegment Point="150,100"/>
<LineSegment Point="125,120"/>
<LineSegment Point="90,110"/>
<LineSegment Point="80,80"/>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
And here is the moving image:
<UserControl ...
xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options" ..>
<UserControl.Resources>
<PathGeometry x:Key="AnimationPath"
Figures="M 10,100 C 40,0 300,0 300,300 0,300 285,200 300,300 "
PresentationOptions:Freeze="True" />
</UserControl.Resources>
<Image
Source="/Resources/Myimage.png"
Width="200" >
<Image.RenderTransform>
<TranslateTransform x:Name="AnimatedTranslateTransform" />
</Image.RenderTransform>
<Image.Triggers>
<EventTrigger RoutedEvent="Path.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<!-- Animates the rectangle horizotally along the path. -->
<DoubleAnimationUsingPath
Storyboard.TargetName="AnimatedTranslateTransform"
Storyboard.TargetProperty="X"
PathGeometry="{StaticResource AnimationPath}"
Source="X"
Duration="0:0:5"
AutoReverse="True"
/>
<!-- Animates the rectangle vertically along the path. -->
<DoubleAnimationUsingPath
Storyboard.TargetName="AnimatedTranslateTransform"
Storyboard.TargetProperty="Y"
PathGeometry="{StaticResource AnimationPath}"
Source="Y"
Duration="0:0:5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
Related links: link1, link2
I have tried like:
Figures="M 10,100 C 100,50 140,60 150,100 125,120 90,110 80,80 "
i.e start at point 100,50 --> 140,60 and so on...
but it doesnt go exactly on this path
Your sketch seems to indicate that you want to animate kind of a red arrow along the path, including a rotation to the tangent angle of the current path segment.
You could achieve this by animating the Matrix property of a MatrixTransform with a MatrixAnimationUsingPath. The example below uses an additional TranslateTransform to center the image. As there is a DrawingImage in the Source property of the Image element, you may as well use another Path instead of an Image.
<Window.Resources>
<PathGeometry x:Key="AnimationPath"
Figures="M100,50 L140,60 150,100 125,120 90,110 80,80Z"/>
</Window.Resources>
<Canvas>
<Path Stroke="Black" StrokeThickness="5" Fill="Goldenrod"
Data="{StaticResource AnimationPath}"/>
<Image>
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<GeometryDrawing Geometry="M0,0 L10,8 0,16">
<GeometryDrawing.Pen>
<Pen Thickness="3" Brush="Red"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
<Image.RenderTransform>
<TransformGroup>
<TranslateTransform X="-5" Y="-8"/>
<MatrixTransform x:Name="AnimatedTransform"/>
</TransformGroup>
</Image.RenderTransform>
<Image.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<MatrixAnimationUsingPath
Storyboard.TargetName="AnimatedTransform"
Storyboard.TargetProperty="Matrix"
Duration="0:0:5"
DoesRotateWithTangent="True"
PathGeometry="{StaticResource AnimationPath}"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
</Canvas>
I have defined a QuadraticBezierSegment object as the Data property of a Path object:
<Path Stroke="Red" StrokeThickness="5">
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="450,250" IsClosed="False">
<QuadraticBezierSegment Point1="245,-50" Point2="0,25" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
It is shown below in red, and shows the curve that I expected.:
However, when this same curve is used in a path animation, the path of the animated element is NOTHING like the path of the line shown in the image above. [Please note that this is only part of the animation that I've been creating.]
<Ellipse Width="50" Height="50" Fill="#FF01ADEF" Stroke="Black" StrokeThickness="3">
<Ellipse.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<Storyboard Storyboard.TargetProperty="(Canvas.Top)" BeginTime="0:0:3">
<DoubleAnimationUsingPath Duration="0:0:1.5"
AccelerationRatio="0.2">
<DoubleAnimationUsingPath.PathGeometry>
<PathGeometry>
<PathFigure StartPoint="450,250" IsClosed="False">
<QuadraticBezierSegment
Point1="245,-50" Point2="0,25" />
</PathFigure>
</PathGeometry>
</DoubleAnimationUsingPath.PathGeometry>
</DoubleAnimationUsingPath>
</Storyboard>
<Storyboard Storyboard.TargetProperty="(Canvas.Left)"
BeginTime="0:0:3" >
<DoubleAnimation Duration="00:00:1.5" From="400" To="0"
DecelerationRatio="0.2" />
</Storyboard>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Ellipse.Triggers>
</Ellipse>
Does anybody know why that is and what I have to do to make the animation path actually follow the path shown in the image?
I just needed to add another DoubleAnimationUsingPath to animate the Canvas.Left property and set the Source property on both DoubleAnimationUsingPath elements, to specify whether they should use the X and Y parts of the QuadraticBezierSegment object. This is what the code looks like now:
<Ellipse Width="50" Height="50" Fill="#FF01ADEF" Stroke="Black" StrokeThickness="3">
<Ellipse.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<Storyboard Storyboard.TargetProperty="(Canvas.Top)"
BeginTime="0:0:3">
<DoubleAnimationUsingPath Duration="0:0:1.5"
AccelerationRatio="0.2" Source="Y">
<DoubleAnimationUsingPath.PathGeometry>
<PathGeometry>
<PathFigure StartPoint="400,200" IsClosed="False">
<QuadraticBezierSegment Point1="245,-50"
Point2="0,0" />
</PathFigure>
</PathGeometry>
</DoubleAnimationUsingPath.PathGeometry>
</DoubleAnimationUsingPath>
</Storyboard>
<Storyboard Storyboard.TargetProperty="(Canvas.Left)"
BeginTime="0:0:3" >
<DoubleAnimationUsingPath Duration="0:0:1.5"
AccelerationRatio="0.2" Source="X">
<DoubleAnimationUsingPath.PathGeometry>
<PathGeometry>
<PathFigure StartPoint="400,200" IsClosed="False">
<QuadraticBezierSegment Point1="245,-50"
Point2="0,0" />
</PathFigure>
</PathGeometry>
</DoubleAnimationUsingPath.PathGeometry>
</DoubleAnimationUsingPath>
</Storyboard>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Ellipse.Triggers>
</Ellipse>
I have a canvas that contains a couple other elements. I need this canvas to animate continuously along a circular (elliptical) path.
How can I do this in XAML?
not sure if this is doing it the hard way or not but this works...
<Canvas>
<Rectangle Height="30" Width="30" Fill="Blue">
<Rectangle.RenderTransform>
<MatrixTransform x:Name="elementMatrixTransform">
<MatrixTransform.Matrix >
<Matrix />
</MatrixTransform.Matrix>
</MatrixTransform>
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard>
<MatrixAnimationUsingPath
Storyboard.TargetName="elementMatrixTransform"
Storyboard.TargetProperty="Matrix"
Duration="0:0:10"
RepeatBehavior="Forever">
<MatrixAnimationUsingPath.PathGeometry>
<PathGeometry>
<PathFigure StartPoint="0,50">
<ArcSegment Point="50,0" Size="50,50" SweepDirection="Clockwise"></ArcSegment>
<ArcSegment Point="100,50" Size="50,50" SweepDirection="Clockwise"></ArcSegment>
<ArcSegment Point="50,100" Size="50,50" SweepDirection="Clockwise"></ArcSegment>
<ArcSegment Point="0,50" Size="50,50" SweepDirection="Clockwise"></ArcSegment>
</PathFigure>
</PathGeometry>
</MatrixAnimationUsingPath.PathGeometry>
</MatrixAnimationUsingPath>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Canvas>
On a canvas I have an ellipse rotated by a RotateTransform through animation. I wish to add a line with one end attached to a point on the ellipse. Can I somehow bind to a point on the ellipse?
You can animate both the Ellipse and the line together, like so:
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas.Resources>
<PathGeometry x:Key="lineEndPath">
<PathFigure StartPoint="25,50">
<ArcSegment IsLargeArc="True" Point="100,50" Size="25,25" SweepDirection="Clockwise"/>
<ArcSegment IsLargeArc="True" Point="25,50" Size="25,25" SweepDirection="Clockwise"/>
</PathFigure>
</PathGeometry>
</Canvas.Resources>
<Canvas.Triggers>
<EventTrigger RoutedEvent="Canvas.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:5" From="0" RepeatBehavior="Forever" Storyboard.TargetName="rotTF" Storyboard.TargetProperty="Angle" To="360"/>
<PointAnimationUsingPath Duration="0:0:5" PathGeometry="{StaticResource lineEndPath}" RepeatBehavior="Forever" Storyboard.TargetName="lineEndPoint" Storyboard.TargetProperty="Point"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Canvas.Triggers>
<Ellipse Width="75" Height="50" Canvas.Left="25" Canvas.Top="25" Stroke="Black">
<Ellipse.RenderTransform>
<RotateTransform x:Name="rotTF" CenterX="37.5" CenterY="25"/>
</Ellipse.RenderTransform>
</Ellipse>
<Path Stroke="Black" StrokeThickness="1.0">
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="0,0">
<LineSegment x:Name="lineEndPoint"/>
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
<Path Data="{StaticResource lineEndPath}" Stroke="Black" StrokeDashArray="2,0,0" StrokeThickness="1.0"/>
</Canvas>
We animate one end of a LineSegment with a PointAnimationUsingPath, and set the path to a circle (shown by the dotted line).
I'm not sure what the problem is. You can add another element into your Canvas that lines up correctly and apply the transform to the canvas which will rotate both elements?
If you're asking if there's any way to say "line up with this" on the line then no, you can't do that as far as I now. For complicated layouts like this you can either trial and error with KaXaml/Bland, or use Illustrator to lay it out and then export to XAML.
Assuming I understand correctly, you're going to have to figure out the math to change your line's end point. Sorry, don't know the formula offhand, but you'll basically find your point on the ellipse, then figure out it's position given the angle of the rotation, and then change your line's end point using that information.
In order to connect two elements at their edges by a Line, I use a bounding box method as explained in Connecting two WPF canvas elements by a line, without using anchors?.