WPF Animation - Rotate elements on button click - wpf

I want to rotate two elements from 0 to angles: 1. Element (ClampL) 45 angle and 2. Element (ClampR) -45 Angle, but they should move together. The elements are 2 clamps which are open and when they grab something, they close and this process should happen on button click.
Here is my XAML:
<UserControl x:Class="WPF_AnimatedLift.View.Prozess"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPF_AnimatedLift.View" xmlns:viewmodel="clr-namespace:WPF_AnimatedLift.ViewModel"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="1000">
<Canvas x:Name="canvas" Margin="0,0,-163,-83">
<Path x:Name="ClampR" Fill="#FF000000" StrokeThickness="2.12787" Data="M 524.2207 473.57227 L 524.2207 563.57227 L 137.14258 563.57227 L 137.14258 652.14258 L 571.42773 652.14258 L 571.42773 563.57227 L 571.36328 563.57227 L 571.36328 473.57227 L 524.2207 473.57227 z " Height="68.668" Stretch="Fill" Width="80.734" Canvas.Left="411.375" Canvas.Top="339.125" >
<Path.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ClampR"
Storyboard.TargetProperty="LayoutTransform.(RotateTransform.Angle)"
From="0"
To="360"
Duration="0:0:5"
RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Path.Triggers>
<Path.LayoutTransform>
<TransformGroup>
<ScaleTransform ScaleY="0.265" ScaleX="0.265"/>
<SkewTransform/>
<RotateTransform CenterX="0.5" CenterY="0.5" Angle="-45"/>
<TranslateTransform/>
</TransformGroup>
</Path.LayoutTransform>
</Path>
<Path x:Name="ClampL" Fill="#FF000000" StrokeThickness="2.12787" Data="M 110 344.28516 L 110 434.28516 L 110 485.71484 L 110 522.85742 L 544.28516 522.85742 L 544.28516 434.28516 L 157.14258 434.28516 L 157.14258 344.28516 L 110 344.28516 z " Height="68.668" Stretch="Fill" Width="80.734" Canvas.Left="384.508" Canvas.Top="339.166">
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleY="0.265" ScaleX="0.265"/>
<SkewTransform/>
<RotateTransform Angle="45"/>
<TranslateTransform/>
</TransformGroup>
</Path.RenderTransform>
</Path>
<Button Content="START" Canvas.Left="84" Canvas.Top="403" Width="106" Height="48" FontSize="30" FontWeight="Bold" Name="BtnStart" Command="{Binding ClickCommand, UpdateSourceTrigger=PropertyChanged}" >
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click"> //???
<BeginStoryboard Storyboard="{StaticResource Storyboard}" />
</EventTrigger>
</Button.Triggers>
</Button>
</Canvas>
<UserControl.DataContext>
<viewmodel:ProzessViewModel/>
</UserControl.DataContext>
</UserControl>
As you can see in code, I tried with Path.Trigger, DoubleAnimation, but it doesn't work, can someone help me?
Thank you.

Basically, in EventTrigger, you can only refer the elements that exist inside that FrameworkElement. The solution would be different depending on where you want to set EventTrigger.
1. Button
You can use EventTrigger in the Button, which is the source of Button.Click event, like the following. The elements are simplified to a minimum for brevity and the Storyboard.TargetProperty is modified to circumvent TransformGroup.
<Canvas x:Name="canvas">
<Path x:Name="ClampR"
Canvas.Left="411" Canvas.Top="339"
Width="80" Height="68"
Fill="Black"
Data="M 0,0 1,0 0,1 z" Stretch="Fill">
<Path.LayoutTransform>
<TransformGroup>
<ScaleTransform ScaleY="0.265" ScaleX="0.265"/>
<SkewTransform/>
<RotateTransform CenterX="0.5" CenterY="0.5" Angle="-45"/>
<TranslateTransform/>
</TransformGroup>
</Path.LayoutTransform>
</Path>
<Button x:Name="BtnStart"
Canvas.Left="84" Canvas.Top="403"
Width="106" Height="48"
Content="START">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ClampR"
Storyboard.TargetProperty="LayoutTransform.Children[2].Angle"
From="0" To="360"
Duration="0:0:5"
RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</Canvas>
2. Canvas
As Button.Click is RoutedEvent and propagated to the root, you can use EventTrigger at parent level. In this case, at Canvas.Triggers.
<Canvas x:Name="canvas">
<Canvas.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ClampR"
Storyboard.TargetProperty="LayoutTransform.Children[2].Angle"
From="0" To="360"
Duration="0:0:5"
RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Canvas.Triggers>
<Path x:Name="ClampR"
Canvas.Left="411" Canvas.Top="339"
Width="80" Height="68"
Fill="Black"
Data="M 0,0 1,0 0,1 z" Stretch="Fill">
<Path.LayoutTransform>
<TransformGroup>
<ScaleTransform ScaleY="0.265" ScaleX="0.265"/>
<SkewTransform/>
<RotateTransform CenterX="0.5" CenterY="0.5" Angle="-45"/>
<TranslateTransform/>
</TransformGroup>
</Path.LayoutTransform>
</Path>
<Button x:Name="BtnStart"
Canvas.Left="84" Canvas.Top="403"
Width="106" Height="48"
Content="START"/>
</Canvas>
3. Path
Unlike standard EventTrigger, EventTrigger of Microsoft.Xaml.Behaviors.Wpf is more robust and can refer other controls. Thus it can work in the Path.
Add xmlns:i="http://schemas.microsoft.com/xaml/behaviors" in header.
<Canvas x:Name="canvas">
<Path x:Name="ClampR"
Canvas.Left="411" Canvas.Top="339"
Width="80" Height="68"
Fill="Black"
Data="M 0,0 1,0 0,1 z" Stretch="Fill">
<i:Interaction.Triggers>
<i:EventTrigger SourceName="BtnStart" EventName="Click">
<i:ControlStoryboardAction>
<i:ControlStoryboardAction.Storyboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="Rotation"
Storyboard.TargetProperty="(RotateTransform.Angle)"
From="0" To="360"
Duration="0:0:5"
RepeatBehavior="Forever"/>
</Storyboard>
</i:ControlStoryboardAction.Storyboard>
</i:ControlStoryboardAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<Path.LayoutTransform>
<TransformGroup>
<ScaleTransform ScaleY="0.265" ScaleX="0.265"/>
<SkewTransform/>
<RotateTransform x:Name="Rotation" CenterX="0.5" CenterY="0.5" Angle="-45"/>
<TranslateTransform/>
</TransformGroup>
</Path.LayoutTransform>
</Path>
<Button x:Name="BtnStart"
Canvas.Left="84" Canvas.Top="403"
Width="106" Height="48"
Content="START"/>
</Canvas>

Related

Flicker on bars

I have the XAML code below but it's not smooth and creates flicker once in a while. How can i improve this in XAML? I have tried multiple solutions but it always seem to create flicker multiple times within a minute. Is this because of the frame rate of my monitor and the source code or? Please help. If you have examples on getting this smooth i would appreciate it.
<Window x:Class="BarsTest.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:BarsTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel Orientation="Horizontal" Background="Black">
<Rectangle Height="1080" Width="160" Fill="White">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="AnimatedTranslateTransform0" X="-160" Y="0" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Height="1080" Width="160" Fill="White">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="AnimatedTranslateTransform1" X="160" Y="0" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Height="1080" Width="160" Fill="White">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="AnimatedTranslateTransform2" X="480" Y="0" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Height="1080" Width="160" Fill="White">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="AnimatedTranslateTransform3" X="800" Y="0" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Height="1080" Width="160" Fill="White">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="AnimatedTranslateTransform4" X="1120" Y="0" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Height="1080" Width="160" Fill="White">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="AnimatedTranslateTransform5" X="1440" Y="0" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Height="1080" Width="160" Fill="White">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="AnimatedTranslateTransform6" X="1760" Y="0" />
</Rectangle.RenderTransform>
</Rectangle>
<StackPanel.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Name="myBeginStoryboard">
<Storyboard DesiredFrameRate="60">
<DoubleAnimation RepeatBehavior="Forever" Storyboard.TargetName="AnimatedTranslateTransform0" Storyboard.TargetProperty="X" From="-160" To="160" Duration="0:0:0.5"/>
<DoubleAnimation RepeatBehavior="Forever" Storyboard.TargetName="AnimatedTranslateTransform1" Storyboard.TargetProperty="X" From="0" To="320" Duration="0:0:0.5"/>
<DoubleAnimation RepeatBehavior="Forever" Storyboard.TargetName="AnimatedTranslateTransform2" Storyboard.TargetProperty="X" From="160" To="480" Duration="0:0:0.5" />
<DoubleAnimation RepeatBehavior="Forever" Storyboard.TargetName="AnimatedTranslateTransform3" Storyboard.TargetProperty="X" From="320" To="640" Duration="0:0:0.5" />
<DoubleAnimation RepeatBehavior="Forever" Storyboard.TargetName="AnimatedTranslateTransform4" Storyboard.TargetProperty="X" From="480" To="800" Duration="0:0:0.5" />
<DoubleAnimation RepeatBehavior="Forever" Storyboard.TargetName="AnimatedTranslateTransform5" Storyboard.TargetProperty="X" From="640" To="960" Duration="0:0:0.5" />
<DoubleAnimation RepeatBehavior="Forever" Storyboard.TargetName="AnimatedTranslateTransform6" Storyboard.TargetProperty="X" From="800" To="1120" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</StackPanel.Triggers>
</StackPanel>
</Grid>
</Window>
Br
ARUS

How to animate WPF rectangle Width and Fill property at the same time

I have a rectangle for which I want to animate the Width from 0 to some offset with a color say Red and the remaining width with different color say Green.
Any idea how to do it?
Example:
Animation flow ->
|start <- Red -> stop | start <- Green -> stop|
Rectangle doesnt allow you have Content in it. So, you can use Fill as a VisualBrush or DrawingBrush.
VisualBrush approach
This VisualBrush will have a StackPanel with two Rectangle objects.
Run below code as it is :
<Rectangle Height="100" Stroke="Black">
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width" From="0" To="200" Duration="0:0:10"/>
<DoubleAnimation Storyboard.TargetName="LeftRect" Storyboard.TargetProperty="Width" From="0" To="100" Duration="0:0:5"/>
<DoubleAnimation Storyboard.TargetName="RightRect" Storyboard.TargetProperty="Width" From="0" To="100" Duration="0:0:5" BeginTime="0:0:5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
<Rectangle.Fill>
<VisualBrush Stretch="None" AlignmentX="Left">
<VisualBrush.Visual>
<StackPanel Orientation="Horizontal">
<Rectangle x:Name="LeftRect" Height="100">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Fill" Value="Red"/>
<Setter Property="Width" Value="0"/>
</Style>
</Rectangle.Style>
</Rectangle>
<Rectangle x:Name="RightRect" Height="100">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Fill" Value="Green"/>
<Setter Property="Width" Value="0"/>
</Style>
</Rectangle.Style>
</Rectangle>
</StackPanel>
</VisualBrush.Visual>
</VisualBrush>
</Rectangle.Fill>
</Rectangle>
DrawingBrush approach
This DrawingBrush will have RectangleGeometry and won't use any Control.
<Rectangle Height="100" Width="300" Stroke="Black" StrokeThickness="2" RenderTransformOrigin="0,0.5">
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="LeftScaleX" Storyboard.TargetProperty="ScaleX" From="0" To="1" Duration="0:0:1"/>
<DoubleAnimation Storyboard.TargetName="RightScaleX" Storyboard.TargetProperty="ScaleX" From="0" To="1" Duration="0:0:1" BeginTime="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
<Rectangle.Fill>
<DrawingBrush AlignmentX="Left" Stretch="None">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="Red">
<GeometryDrawing.Geometry>
<RectangleGeometry x:Name="LeftRect" Rect="0 0 200 100">
<RectangleGeometry.Transform>
<TransformGroup>
<ScaleTransform x:Name="LeftScaleX" ScaleX="0" CenterY="50"/>
</TransformGroup>
</RectangleGeometry.Transform>
</RectangleGeometry>
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="Green">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="200 0 300 100">
<RectangleGeometry.Transform>
<TransformGroup>
<ScaleTransform x:Name="RightScaleX" ScaleX="0" CenterX="200"/>
</TransformGroup>
</RectangleGeometry.Transform>
</RectangleGeometry>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
A much simpler approach would be to animate ViewPort property.
<Rectangle Stroke="Black" Margin="44,67,44,0" Width="0" HorizontalAlignment="Left" VerticalAlignment="Top" Height="100">
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width" To="300" Duration="0:0:1"/>
<RectAnimation Storyboard.TargetName="Brush1" Storyboard.TargetProperty="(DrawingBrush.Viewport)" To="0,0, 300,100" Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
<Rectangle.Fill>
<DrawingBrush x:Name="Brush1" Viewport="0,0,0,100" AlignmentX="Left" Stretch="None" ViewportUnits="Absolute">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="Red">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0, 200, 100"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="Green">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="200,0, 100, 100"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>

Unable to do annimation on transform group

How can I perform transforms animation on a UIElement?
The following code moves a rectangle via annimation but does not transform it when running animation.
I have looked at tutorials and am absolutely confused as to why the following XAML does not work:
XAML:
<DoubleAnimation Storyboard.TargetName="FrontRectangle"
Storyboard.TargetProperty="(Canvas.Top)"
From="20" To="73"
Duration="0:0:.15">
</DoubleAnimation>-->
<DoubleAnimation Storyboard.TargetName="FrontRectangle"
Storyboard.TargetProperty="(Rectangle.RenderTransform).(SkewTransform.AngleX)"
From="0" To="-25"
Duration="0:0:.15">
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="FrontRectangle"
Storyboard.TargetProperty="(Rectangle.RenderTransform).(RotateTransform.Angle)"
From="0" To="-25"
Duration="0:0:.15">
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="FrontRectangle"
Storyboard.TargetProperty="(Rectangle.RenderTransform).(TranslateTransform.X)"
From="0" To="-40"
Duration="0:0:.15">
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="FrontRectangle"
Storyboard.TargetProperty="(Rectangle.RenderTransform).(TranslateTransform.Y)"
From="0" To="20"
Duration="0:0:.15">
</DoubleAnimation>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Canvas>
<Rectangle x:Name="FrontRectangle" Width="400" Height="246" Fill="Purple"
Canvas.Left="285" Canvas.Top="20" Panel.ZIndex="4" />
<Rectangle x:Name="RightRectangle" Width="100" Height="100" Fill="LightBlue"
RenderTransformOrigin="0.5,0.5" Canvas.Left="774" Canvas.Top="191" Panel.ZIndex="2">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleY="1" ScaleX="2.5"/>
<SkewTransform AngleX="60"/>
<RotateTransform Angle="-145"/>
<TranslateTransform/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="LeftRectangle" Width="100" Height="100" Fill="LightGreen"
RenderTransformOrigin="0.5,0.5" Canvas.Left="99" Canvas.Top="191" Panel.ZIndex="2" >
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleY="1" ScaleX="2.5"/>
<SkewTransform AngleX="-60"/>
<RotateTransform Angle="145"/>
<TranslateTransform/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="MidLeftRectangle" Width="350" Height="160" Fill="LightSeaGreen"
Canvas.Left="159" Canvas.Top="73" RenderTransformOrigin="0.5,0.5" Panel.ZIndex="3" >
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform AngleX="-25"/>
<RotateTransform Angle="-25"/>
<TranslateTransform X="-40" Y="20"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="MidRightRectangle" Width="350" Height="160" Fill="LightCoral"
Canvas.Left="559" Canvas.Top="73" RenderTransformOrigin="0.5,0.5" Panel.ZIndex="3" >
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform AngleX="25"/>
<RotateTransform Angle="25"/>
<TranslateTransform X="-40" Y="20"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
</Grid>
Code-behind:
private void OnSwipedLeft()
{
var visualState = "SwipeLeftState";
var success = VisualStateManager.GoToElementState(MyGrid, visualState, true);
Debug.Assert(success);
}
I had to insert a "(TransformGroup.Children)[some_index]" between RenderTransform and the actual type of transform.
<Grid x:Name="MyGrid">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="SwipeLeftGroup">
<VisualState x:Name="SwipeLeftState">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="FrontRectangle"
Storyboard.TargetProperty="(Canvas.Left)"
From="285" To="159"
Duration="0:0:.15">
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="FrontRectangle"
Storyboard.TargetProperty="(Canvas.Top)"
From="20" To="73"
Duration="0:0:.15">
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="FrontRectangle"
Storyboard.TargetProperty="(Rectangle.RenderTransform).(TransformGroup.Children)[1].(SkewTransform.AngleX)"
From="0" To="-25"
Duration="0:0:.15">
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="FrontRectangle"
Storyboard.TargetProperty="(Rectangle.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)"
From="0" To="-25"
Duration="0:0:.15">
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="FrontRectangle"
Storyboard.TargetProperty="(Rectangle.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"
From="0" To="-40"
Duration="0:0:.15">
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="FrontRectangle"
Storyboard.TargetProperty="(Rectangle.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)"
From="0" To="20"
Duration="0:0:.15">
</DoubleAnimation>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Canvas>
<Rectangle x:Name="FrontRectangle" Width="400" Height="246" Fill="Purple"
Canvas.Left="290" Canvas.Top="20" Panel.ZIndex="4">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="RightRectangle" Width="100" Height="100" Fill="LightBlue"
RenderTransformOrigin="0.5,0.5" Canvas.Left="774" Canvas.Top="191" Panel.ZIndex="2">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleY="1" ScaleX="2.5"/>
<SkewTransform AngleX="60"/>
<RotateTransform Angle="-145"/>
<TranslateTransform/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="LeftRectangle" Width="100" Height="100" Fill="LightGreen"
RenderTransformOrigin="0.5,0.5" Canvas.Left="99" Canvas.Top="191" Panel.ZIndex="2" >
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleY="1" ScaleX="2.5"/>
<SkewTransform AngleX="-60"/>
<RotateTransform Angle="145"/>
<TranslateTransform/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="MidLeftRectangle" Width="350" Height="160" Fill="LightSeaGreen"
Canvas.Left="159" Canvas.Top="73" RenderTransformOrigin="0.5,0.5" Panel.ZIndex="3" >
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform AngleX="-25"/>
<RotateTransform Angle="-25"/>
<TranslateTransform X="-40" Y="20"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="MidRightRectangle" Width="350" Height="160" Fill="LightCoral"
Canvas.Left="559" Canvas.Top="73" RenderTransformOrigin="0.5,0.5" Panel.ZIndex="3" >
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform AngleX="25"/>
<RotateTransform Angle="25"/>
<TranslateTransform X="-40" Y="20"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
</Grid>

RenderTransform in WPF causing unexpected layout changes

I am trying to animate a number of shapes within a visualbrush but when I perform a rotation the shapes 'pulse'. I am assuming that as the shapes rotate the bounding boxes are forcing a layout pass. However since I am using a RenderTransform I wasn't expecting this to trigger layout changes.
This code illustrates the problem:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="200" Width="200">
<StackPanel>
<Border BorderBrush="Red" BorderThickness="1"
Height="100" Width="100">
<Border.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="inner_Ellipse"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)">
<LinearDoubleKeyFrame KeyTime="0:0:3" Value="-360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="outer_Ellipse"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)">
<LinearDoubleKeyFrame KeyTime="0:0:3" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
<Border.Background>
<VisualBrush Stretch="Uniform">
<VisualBrush.Visual>
<Canvas Width="20" Height="20">
<Ellipse x:Name="outer_Ellipse"
Stroke="Blue" StrokeThickness="1"
Width="20" Height="20"
RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<RotateTransform/>
</Ellipse.RenderTransform>
</Ellipse>
<Ellipse x:Name="inner_Ellipse"
Stroke="Red" StrokeThickness="1"
Width="18" Height="18"
Margin="1,1,0,0"
RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<RotateTransform/>
</Ellipse.RenderTransform>
</Ellipse>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</Border.Background>
</Border>
</StackPanel>
This is a simple sample of a much more complicated application where I am using the Visual Brushes to decorate 2d planes being manipulated in 3d. It all works well until I try and animate the brushes. I have tried several different approaches but always seem to run into this layout issue.
Any suggestions appreciated.
Thanks
Rob
I was able to track down the cause of your problem. It has to do with the Stretch="Uniform" Property setting on your VisualBrush. It appears the framework is computing a bounding rectangle on your VisuaBrush.Visual, and then stretching it to fit Border.Background. The following code should illustrate the behavior. I took out your inner_Ellipse and added an outer_Rectangle that should simulate the bounding rectangle being stretched:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="200" Width="200">
<StackPanel>
<Border BorderBrush="Red" BorderThickness="1"
Height="100" Width="100">
<Border.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="outer_Rectangle"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)">
<LinearDoubleKeyFrame KeyTime="0:0:6" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="outer_Ellipse"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)">
<LinearDoubleKeyFrame KeyTime="0:0:6" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
<Border.Background>
<VisualBrush Stretch="Uniform">
<VisualBrush.Visual>
<Canvas Width="20" Height="20">
<Ellipse x:Name="outer_Ellipse"
Stroke="Blue" StrokeThickness="1"
Width="20" Height="20"
RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<RotateTransform/>
</Ellipse.RenderTransform>
</Ellipse>
<Rectangle x:Name="outer_Rectangle"
Stroke="Blue" StrokeThickness="1"
Width="20" Height="20"
RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform/>
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</Border.Background>
</Border>
</StackPanel>
</Window>
As to solving the problem, I am not sure. One way would be to use Stretch="None" on your VisualBrush, but that doesn't seem ideal because it then falls on you to deal with the size of your VisualBrush.Visual contents.
Well after a good deal of trial and error I have a working solution. The contents of the VisualBrush correctly scale and don't cause the bounding box problem:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight" >
<StackPanel>
<Border BorderBrush="Black" BorderThickness="1"
Height="400" Width="400">
<Border.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="inner_Ellipse"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)">
<LinearDoubleKeyFrame KeyTime="0:0:3" Value="-360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="outer_Ellipse"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)">
<LinearDoubleKeyFrame KeyTime="0:0:3" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
<Border.Background>
<VisualBrush Stretch="UniformToFill">
<VisualBrush.Visual>
<Border BorderBrush="Transparent" BorderThickness="1"
Width="200" Height="200">
<StackPanel Width="200" Height="200">
<Canvas>
<Rectangle x:Name="outer_Ellipse"
Stroke="Blue" StrokeThickness="1"
Width="20" Height="200"
Canvas.Left="40"
RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform/>
</Rectangle.RenderTransform>
</Rectangle>
<Ellipse x:Name="inner_Ellipse"
Stroke="Red" StrokeThickness="1"
StrokeDashArray="2"
Canvas.Top="30"
Canvas.Left="20"
Width="200" Height="200"
RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<RotateTransform/>
</Ellipse.RenderTransform>
</Ellipse>
</Canvas>
</StackPanel>
</Border>
</VisualBrush.Visual>
</VisualBrush>
</Border.Background>
</Border>
</StackPanel>
</Window>
The 'secret' appears to be wrapping the canvas containing the brush contents in a StackPanel and wrapping this in a Border. (a Grid, DockPanel and WrapPanel will also work in place of the StackPanel).
<Border BorderBrush="Transparent" BorderThickness="1"
Width="200" Height="200">
<StackPanel Width="200" Height="200">
The Border must have a BorderBrush set and a BorderThickness. Also they must both have an explicit width and height. Here I have set values appropriate to the content so it scales correctly.
Without all 3 of these components the bounding box issue re occurs. Clearly something about the layout policies of the containers makes a difference but I have no idea what or why.
Not at all a satisfying solution and I would appreciate it if anyone can shine a light on what's going on here.
At least it works, both in the above demo and my main application, so far anyway!
Rob

ScaleTransform for Image

I am trying to increase the size of the image by 20. So I am using ScaleTransform as shown below.. but the following code doesn't do any scale Tranform.. Any help would be appreciated...
<Grid>
<Canvas>
<Canvas Height="50" Width="50" Canvas.Top="10" Canvas.Left="100"
Visibility="Visible">
<Image Name="Img" Source="Help.PNG" Canvas.Left="0" Canvas.Top="0">
</Image>
</Canvas>
<Button Canvas.Left="100" Canvas.Top="100" Height="42.5" Name="button3"
Width="100" Visibility="Visible">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard Name="MoveBox">
<DoubleAnimation Storyboard.TargetName="Img"
Storyboard.TargetProperty="(Image.RenderTransform).(ScaleTransform.ScaleX)"
From="1" To="20" BeginTime="0:0:3.75" Duration="0:0:1.25" />
<DoubleAnimation Storyboard.TargetName="Img"
Storyboard.TargetProperty="(Image.RenderTransform).(ScaleTransform.ScaleY)"
From="1" To="20" BeginTime="0:0:3.75" Duration="0:0:1.25" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</Canvas>
</Grid>
Have you tried setting a <RenderTransform> on the image? Something like this:
<Image Name="Img" Source="Help.PNG" Canvas.Left="0" Canvas.Top="0">
<Image.RenderTransform>
<ScaleTransform x:Name="scale" ScaleX="1" ScaleY="1"
CenterX="0.5" CenterY="0.5" />
</Image.RenderTransform>
</Image>
This initialises the RenderTransform so that you can refer to it from elsewhere.
I've had to do this with Silverlight.

Resources