I'm trying to rotate a line clockwise, to simulate the loading animations we can usually find in internet, but I want to do it by myself.
I want to simulate something similar to this:
But I can't even move a simple line... This is my code trying to rotate a line:
<Canvas>
<Line
Name="LineTransformation"
X1="50" Y1="50"
X2="50" Y2="0"
Stroke="Red"
StrokeThickness="2">
<Line.RenderTransform>
<RotateTransform Angle="0" CenterX="{Binding ElementName=LineTransformation.X2}" CenterY="{Binding ElementName=LineTransformation.Y2}"></RotateTransform>
</Line.RenderTransform>
</Line>
</Canvas>
How can I make something similar? I can't even get how to rotate that line! :_(
The line is not being rotated. Your image is not even working correctly. This effect is achieved by drawing all 12 lines. Then, instead of animating the position of the lines, you animate the Opacity of each line from 1.0 to 0.0 in 12 steps and then jump straight back to 1.0.
This animation should run on each line in turn and slightly after the previous line so that there is this delayed effect which makes it look like the lines are moving around. You can do this animation using a DoubleAnimationUsingKeyFrames and 12 DiscreteDoubleKeyFrame elements with equally spaced KeyTime values.
To accurately reproduce exactly what your image is doing, you'd only need two different opacity levels, but it would still be achieved in the same way.
Try popping this code into a Window and taking a look at what I have done to demonstrate how this can be achieved...
<Window.Resources>
<Storyboard x:Key="SpinStoryboard">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="LineTransformation">
<EasingDoubleKeyFrame KeyTime="0" Value="-180"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="180"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource SpinStoryboard}"/>
</EventTrigger>
</Window.Triggers>
<Grid>
<Canvas Width="50" Height="50">
<Line
Name="LineTransformation"
X1="25" Y1="50"
X2="25" Y2="0"
Stroke="Red"
StrokeThickness="2" RenderTransformOrigin="1,0.5">
<Line.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Line.RenderTransform>
</Line>
</Canvas>
</Grid>
Essentially using RotateTransform and a storyboard to spin the line, the tigger is simply there to start the animation for demonstration purposes!
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>
This is my code for rectangle :
<Rectangle x:Name="rect1" Grid.Column="1" Fill="#FF5C626C" HorizontalAlignment="Left" Height="50" Margin="36,171,0,0" StrokeThickness="2" VerticalAlignment="Top" Width="358" RadiusX="30" RadiusY="50">
<Rectangle.Triggers>
</Rectangle.Triggers>
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseEnter">
<BeginStoryboard>
<Storyboard>
<ParallelTimeline >
<ColorAnimation Storyboard.TargetProperty="Fill.Color" To="#FF767C84" />
</ParallelTimeline>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Rectangle.MouseLeave">
<BeginStoryboard>
<Storyboard>
<ParallelTimeline >
<ColorAnimation Storyboard.TargetProperty="Fill.Color" To="#FF5C626C" />
</ParallelTimeline>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
My question is how do i add a text/content in the rectangle ?
One might suggest using a code block, but if u go through my code, youu'll notice that the rectangle changes it's color on mouseover.So if i put a textblock over the rectangle,the mouseover doesn't work properly(as the textblock covers the entire rectangle).
Another suggestion would be to use a border.But i am not sure about this as i need to find the code to apply mouse over effect on a border.
The next suggestion might be to use a button instead.I would've but my rectangle has corner radius and is a bit round-shaped which, if i use a button, would be hard to achieve.
So how do i add content/text inside the rectangle?
If you feel you definitely have to use a rectangle, put it in a grid and add a TextBlock element above it.
By setting the TextBlock's IsHitTestVisible property to False all hit-testing (mouse events) will be ignored on it and fall through to your rectangle.
<Grid>
<Rectangle (...your attributes here...)>
(...your rectangle code here...)
</Rectangle>
<TextBlock Text="Hello World!" IsHitTestVisible="False" />
</Grid>
I would like to get the position of a control relative to its parent, or just any kind of position, doesn't really matter. But I want to do this only in XAML without any code behind. I have a grid 5x3, and the button is located in 3rd row and 2nd column. So, basically I would like to get the width of first two rows of the grid, and the width of the first column of the gird.
I want to know the position of the button in pixels, because I want to do translation animation like this:
<DoubleAnimation Storyboard.TargetName="Studio" Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)" From="500" To="0" Duration="0:0:2">
but instead of using hardcoded value of 500, I want it to be anything that corresponds to the button's horisontal position in pixels
Something like the XAML below might work for you. It is inspired by your idea of a transparent dummy Rectangle that fills the columns left of the Button.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle x:Name="dummy" Grid.ColumnSpan="2"/>
<Button Grid.Column="2" VerticalAlignment="Center" Content="Click">
<Button.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="-1"/>
<TranslateTransform/>
<ScaleTransform ScaleX="-1"/>
</TransformGroup>
</Button.RenderTransform>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="RenderTransform.Children[1].X"
To="{Binding ActualWidth, ElementName=dummy}"
Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</Grid>
Instead of declaring the RenderTransform as shown above you may perhaps better use a binding converter to invert the value of the dummy Rectangle's ActualWidth property.
I have a nice little popup, when it shows, I d'like it to growth from 0 to 1x scaley,
but I don't get it right, when I click multiple times, it looks like i "catch" the animation at
various states during the "growth".
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.MouseRightButtonDown"
>
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="SimplePopup"
Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Window.Triggers>
and the popup:
<Popup Name="SimplePopup"
AllowsTransparency="True"
StaysOpen="False">
<Popup.LayoutTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1" />
<SkewTransform AngleX="0" AngleY="0" />
<RotateTransform Angle="0" />
<TranslateTransform X="0" Y="0" />
</TransformGroup>
</Popup.LayoutTransform>
<Border> some Content here
</Border>
</Popup>
I'm not sure what you mean by
"catch" the animation at various states during the "growth".
Maybe that it won't finish the animation but restart when you click before storyboard is completed?
If so, you need to prevent the animation from restarting by firing action only when it's not running. I think it can be done with more complex behaviors or triggers available in Blend.
by "catch" the animation at various states during the "growth".
I mean that there is no animation at all, all I see is the static popup
scaled at different sizes, but not a visible animation.
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?.