scale animation for wpf popup - wpf

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.

Related

One Storyboard for multiple triggers

Context: I have two Storyboards in my UserControl. One of them is for sliding the UserControl in and one is for sliding it out.
All I'm doing for the slide in/out is setting the margin to a negative value for sliding out and to zero for sliding in.
Now, I want the sbShowLeftMenu storyboard to be executed when the UserControl's visibility is set to Visible. Also, I want to be able to manually slide the UserControl in/out using the Buttons BtnHide & BtnShow.
Now if the UserControl becomes visible the sbShowLeftMenu is activated and the UserControls gets moved in. Switching the visibility between Collapsed and Visible this behaviour continues, as I want it to.
Now if I hit the BtnHide to move the UserControl out of viewport everything works fine until I start switching the Visibility of the UserControl again. Now the Storyboard doesn't work anymore. I can still move the UserControls in/out with the Buttons but the 'Visible' Trigger does not start the Storyboard.
Here are gif examples of the behavior:
With the click on 'Database Search' I set the Visibility of the UserControl to Visible (because its bound to the 'IsExpanded' property of the ExpanderControl) and it works just fine:
Here I demonstrate what happens after I manually click the 'BtnHide':
This is the code for the UserControl:
<UserControl.Resources>
<Style x:Key="TextBlockStyle">
<Setter Property="TextBlock.FontSize" Value="10"></Setter>
<Setter Property="TextBlock.Margin" Value="1"></Setter>
<Setter Property="TextBlock.VerticalAlignment" Value="Center"></Setter>
</Style>
<Storyboard x:Key="sbShowLeftMenu">
<ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetName="BtnShow" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0.01" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetName="BtnHide" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0.01" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<ThicknessAnimation Storyboard.TargetProperty="Margin" Storyboard.TargetName="pnlLeftMenu" From="-650,0,0,0" To="0,0,0,0" DecelerationRatio=".9" Duration="0:0:1" />
</Storyboard>
<Storyboard x:Key="sbHideLeftMenu">
<ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetName="BtnHide" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0.01" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetName="BtnShow" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0.01" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<ThicknessAnimation Storyboard.TargetProperty="Margin" Storyboard.TargetName="pnlLeftMenu" From="0,0,0,0" To="-650,0,0,0" AccelerationRatio=".9" Duration="0:0:1" />
</Storyboard>
</UserControl.Resources>
<UserControl.Template>
<ControlTemplate>
<Grid Background="Red">
<StackPanel Panel.ZIndex="2" Name="pnlLeftMenu" Orientation="Horizontal" HorizontalAlignment="Left" Margin="-650,0,0,0" Height="500">
<!-- Content -->
<Border>The Content is in here</Border>
<Grid>
<Button x:Name="BtnShow" Height="25" Width="25" VerticalAlignment="Top" HorizontalAlignment="Left" >
<Button.Content>
<Path Stroke="Black"
StrokeThickness="2"
Data="M 0,0 L 0.5,0.5 L 0,1"
Stretch="Uniform"></Path>
</Button.Content>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource sbShowLeftMenu}"></BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<Button x:Name="BtnHide" Height="25" Width="25" VerticalAlignment="Top" HorizontalAlignment="Left" Visibility="Collapsed" >
<Button.Content>
<Path Stroke="Black"
StrokeThickness="2"
Data="M 1,1 L 0.5,0.5 L 1,0"
Stretch="Uniform"></Path>
</Button.Content>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource sbHideLeftMenu}"></BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</Grid>
</StackPanel>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsVisible"
Value="True"
my:TriggerTracing.TriggerName="BoldWhenMouseIsOver"
my:TriggerTracing.TraceEnabled="True">
<Trigger.EnterActions>
<BeginStoryboard Name="sbShowLeftMenu" Storyboard="{StaticResource sbShowLeftMenu}"/>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard Name="xy" Storyboard="{StaticResource sbHideLeftMenu}"/>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</UserControl.Template>
You seem to have some complex logic which is interfering with the operations of the story boards.
I would recommend that you not try do this in triggers but instead in code behind. Create a state machine operation and then open/close/make visible/make invisible depending on the state.
The following code is what I use for similar logic of moving a panel based on boolean state via storyboards. You can expand it to handle visibility as well with other states.
private bool moveRight = true; // Start out on the left side, then move right.
public void MoveRight()
{
try
{
if (moveRight)
{
(Resources["MoveToOpen"] as Storyboard)?.Begin(this, false);
(Resources["FlipArrowClose"] as Storyboard)?.Begin(this, false);
}
else
{
(Resources["MoveToClose"] as Storyboard)?.Begin(this, false);
(Resources["FlipArrowOpen"] as Storyboard)?.Begin(this, false);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
moveRight = !moveRight;
}

how to start the animation if the storyboard definition and the corresponding grid inside canvas is in windows.resources tag

i want to start animation in the grid.the grid is inside the canvas tag.both the story board definition and the grid to animate is inside the windows.resources tag.
the code is given below
<VisualBrush x:Key="Passport" x:Shared="false" Stretch="Uniform">
<VisualBrush.Visual>
<Canvas RenderTransformOrigin="0.5,0.5" >
<Grid Margin="5.397,45.106,5.239,0" VerticalAlignment="Top" Height="7.801" x:Name="side_scan_strip" RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform >
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1"/>
<SkewTransform AngleX="0" AngleY="0"/>
<RotateTransform Angle="0"/>
<TranslateTransform X="0" Y="0"/>
</TransformGroup>
</Grid.RenderTransform>
</Grid>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
<Storyboard x:Key="Storyboard1" x:Name="Storyboard1">
<DoubleAnimationUsingKeyFrames x:Name="dak1" BeginTime="00:00:00" Storyboard.TargetName=" side_scan_strip" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">
<SplineDoubleKeyFrame KeyTime="00:00:01" Value="109.75"/>
<SplineDoubleKeyFrame KeyTime="00:00:02" Value="0"/>
<SplineDoubleKeyFrame KeyTime="00:00:03" Value="109.75"/>
<SplineDoubleKeyFrame KeyTime="00:00:04" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
the exception i got is :
' side_scan_strip' name cannot be found in the name scope of 'WpfApplication1.MainWindow'.
It might be because of a little error :) In the Storyboard you have written: " side_scan_strip" while it should be "side_scan_strip". See the little space in the beginning of the first? I think that is what is triggering the exception.

Xaml transformation keep existing transformations non-affected transformations

I modified a toggle switch so I can use an icon ontop of it. Then I added a custom property which holds the image information and it's rotation. The definition of my image looks somewhat like this:
<Image x:Name="SwitchKnobActive" Source="{Binding Path=(common:FilterSwitchImageHolder.ActiveImage), RelativeSource={RelativeSource TemplatedParent}}" Width="50" Visibility="{Binding IsOn, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=True}" RenderTransformOrigin="0.5, 0.5">
<Image.RenderTransform>
<TransformGroup>
<TranslateTransform x:Name="KnobActiveTranslateTransform"/>
<RotateTransform Angle="{Binding Path=(common:FilterSwitchImageHolder.Angle), RelativeSource={RelativeSource TemplatedParent}}" />
</TransformGroup>
</Image.RenderTransform>
</Image>
Now when I select the image I want to scale it by the factor of two, when I do this I loose the rotation. I tried setting the rotation again like above but it got ignored.. Is there a way how I can keep an existing transformation?
Edit:
The transformation on click XAML:
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="RenderTransform" Storyboard.TargetName="SwitchKnobActive">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value >
<ScaleTransform ScaleX="2" ScaleY="2"></ScaleTransform>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
FYI: All this code is within a control template of a toggle switch.
How are you applying scale transform?
Usual pattern for this would be to add dummy scale transform to the transform group and modify it later. This way all other transforms are not being replaced.
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="myScaleTransform" ScaleX="1.0" ScaleY="1.0">
<TranslateTransform x:Name="KnobActiveTranslateTransform"/>
<RotateTransform Angle="{Binding Path=(common:FilterSwitchImageHolder.Angle), RelativeSource={RelativeSource TemplatedParent}}" />
</TransformGroup>
</Image.RenderTransform>
You can later access it from xaml storyboard like this:
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="SwitchKnobActive"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>

Storyboard execution on Mouse Over

I've developed a WPF application in Expression Blend, a simple button attach with a storyboard with bounce effect.
So far so good, now I want my button to bounce when my mouse enters on it, it does. But it keeps doing that. I move mouse once and after bouncing again falls into the mouse region (I am not moving the mouse, mouse cursor is still) and starts bouncing again.
This is the whole code that I am using
<Window.Resources>
<Storyboard x:Key="Storyboard1">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="button">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="button">
<EasingDoubleKeyFrame KeyTime="0" Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<BounceEase EasingMode="EaseIn"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="-29">
<EasingDoubleKeyFrame.EasingFunction>
<BounceEase EasingMode="EaseIn"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource Storyboard1}"/>
</EventTrigger>
</Window.Triggers>
<Grid x:Name="LayoutRoot">
<Button x:Name="button" Content="Button" HorizontalAlignment="Left" Height="51" Margin="76,100,0,0" VerticalAlignment="Top" Width="130" RenderTransformOrigin="0.5,0.5">
<Button.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Button.RenderTransform>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<ei:ControlStoryboardAction Storyboard="{StaticResource Storyboard1}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
Just copy paste it in your xaml window and you'll good to go.
Keep the mouse on the top corner of mouse. You'll see the problem.
Thanks
The thing that pops out is that you're moving the button by 29px down when it's 51px tall. Chances are the button moves away from the mouse and starts moving back to reverse the animation.
You can work around this by reworking the button and animation:
- place the button in a container (grid, stackpanel - anything that works for you)
- make the container's background "Transparent" (which is NOT the same as "null")
- make the button align to the bottom of that container
- make the animation trigger on the container and change the container's height
The result is that when the button moves away the mouse is still over its container so the animation doesn't stop and reverse.
- control animation behavior with AutoReverse and RepeatBehavior
--
This is obviously not the whole code you're using (where's the "i" namespace declaration?) so I can't see your problem on my end.
Let's take this button animation as example:
<Grid>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Content="Test ">
<Button.RenderTransform>
<ScaleTransform ScaleX="1.0" ScaleY="1.0" />
</Button.RenderTransform>
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.5" Storyboard.TargetProperty="RenderTransform.ScaleX" To="2.0" />
<DoubleAnimation Duration="0:0:0.5" Storyboard.TargetProperty="RenderTransform.ScaleY" To="2.0" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.5" Storyboard.TargetProperty="RenderTransform.ScaleX" To="1.0" />
<DoubleAnimation Duration="0:0:0.5" Storyboard.TargetProperty="RenderTransform.ScaleY" To="1.0" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Grid>
So when your mouse enters the button area, it grows, and when the mouse leaves, it shrinks back.
By default, a StoryBoard's:
AutoReverse = False, if true the animation reverts as soon as it's over.
FillBehavior = HoldEnd, which makes the button's appearence stay as how it looks once the animation is over (i.e: the button will remain 2x bigger as long as you keep your mouse above the button). If set to Stop, it will revert back to its original state as soon as the animation finishes (without animation, which is what is different from AutoReverse).
RepeatBehavior = 1x, you can set this to a TimeSpan, nx where n is the number of times you want the animation repeated, or Forever and it will loop forever.
You should check that these settings haven't been tampered with.
And last but not least, make sure the logic tied to your animation makes sense and doesn't induce a loop (which would, in turn, make the animation loop). For example if you animate based on the mouse being over an element, and the animation moves the element, then your mouse isn't over the element anymore, and thus the animation reverts, bringing back the button under the mouse, etc, forever.
You could try to put the mouse inside a canvas with a transparent (not null) background and animate the button when the mouse gets over the canvas. The button would leave its position but the canvas wouldn't. You could use that solution inside a template control, for example.

Scale transform in xaml (in a controltemplate) on a button to perform a "zoom"

I've got a button with an image in it and it's being styled by the following:
<ControlTemplate x:Key="IconButton" TargetType="Button">
<Border>
<ContentPresenter Height="80" Width="80" />
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation From="1" To="0.5" Duration="0:0:0.5" />
<DoubleAnimation From="0.5" To="1" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<BeginStoryboard>
<Storyboard TargetProperty="Width">
<DoubleAnimation From="80" To="95" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Button is as follows:
<Button Template="{StaticResource IconButton}" Name="btnExit">
<Image Source="Images/Exit.png" />
</Button>
The problem is that the width doesn't change when my mouse goes over. (Or at least - the width of the image does not...)
I believe there is a "scale" transform I can use to enlarge the button and all it's contents? how would I do that here...?
Thanks.
Your template seems to be pretty minimal but I'm assuming your only just getting started on it, however this will help you get started with using a ScaleTransform as opposed to animating the width.
The ScaleTransform can be applied to the RenderTransform property of either the Button itself or just the Border of your template. This could be a TransformGroup if you want to do more than just Scale (i.e. a composite transform consisting of other tranforms such as Translate, Rotate, Skew) but to keep it simple and for examples sake something like the following applies a single ScaleTransform value to the Button:
<Button Template="{StaticResource IconButton}" Name="btnExit">
<Button.RenderTransform>
<ScaleTransform ScaleX="1.0" ScaleY="1.0"></ScaleTransform>
</Button.RenderTransform>
<Image Source="Images/Exit.png" />
</Button>
or this to apply to the Border of the ControlTemplate:
<ControlTemplate x:Key="IconButton" TargetType="Button">
<Border Background="Blue" x:Name="render">
<Border.RenderTransform>
<ScaleTransform ScaleX="1.0" ScaleY="1.0"></ScaleTransform>
</Border.RenderTransform>
<ContentPresenter Height="80" Width="80" />
</Border>
...
...
Next you will want to change your MouseEnter trigger to target that property and for width you will want to target the ScaleX property of the ScaleTransform. The following Storyboard will scale the Button 2.5 times in the X direction (add TargetName="render" to <Storyboard... if you have chosen to apply the Transform to the Border as opposed to the Button).
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<BeginStoryboard>
<Storyboard TargetProperty="RenderTransform.ScaleX">
<DoubleAnimation To="2.5" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
If you were to use a TransformGroup with a number of transforms you would change the TargetProperty value to something like RenderTransform.(TransformGroup.Children)[0].ScaleX assuming the ScaleTransform is the first child of the group.
This should get you up and running with what you need and you can take it where you want from there...
HTH

Resources