WPF Animation of border width - wpf

I've got a dockpanel on my ui as follows;
<DockPanel>
<Border DockPanel.Dock="Top">Header</Border>
<Border DockPanel.Dock="Bottom">My footer</Border>
<Border DockPanel.Dock="Left">Menu</Border>
<Border>Content</Border>
</DockPanel>
What I want to do is to have a storyboard animation to show / hide the Menu on the left-hand side. I've got my border to increase the width when it has loaded but I want a way to close / reopen it. I need a button somewhere but I want this to trigger the animation in the border control rather than itself. Ideally I was thinking of something like the Toolbox / Server Explorer in visual studio.
Does anyone have any pointers / examples for getting started?
Thanks

I'm not sure I understand what you mean, but if you want to animate it in/out then you probably want to update its width? If you have a property on your ViewModel/PresentationModel that you can bind to then you can do something like:
<DataTrigger Binding="{Binding IShouldBeVisible}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard AccelerationRatio="0.4" DecelerationRatio="0.4">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.Width)">
<SplineDoubleKeyFrame KeyTime="00:00:0.13" Value="100"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard AccelerationRatio="0.4" DecelerationRatio="0.4">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.Width)">
<SplineDoubleKeyFrame KeyTime="00:00:0.1" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
If you are doing complicated animations that change multiple properties, different timings etc then it's much easier to put together in Blend, even if you do it in a test project then cut+paste the resulting StoryBoard :-)

Related

Go back to old color after storyboard color animation in WPF

I have a datagrid where the row color is decided by a level parameter that I convert in my rowstyle. This is fine but now I also want to change the color temporarily when I enter the row with my mouse and drag and drop (and change back when I leave or drop). I manage to change the color with this code:
<DataGrid.RowStyle>
<Style TargetType="DataGridRow" BasedOn="{StaticResource MyDataGridRowStyle}">
<Setter Property="Background" Value="{Binding Level,Converter={StaticResource LevelToIndentConverter}}" />
<Style.Triggers>
<EventTrigger RoutedEvent="DataGridRow.DragEnter">
<BeginStoryboard x:Name="DragHoverStoryboard">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Background.Color"
Duration="0:0:0" To="#41B1E1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="DataGridRow.DragLeave">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Background.Color"
Duration="0:0:0" To="Transparent" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
The problem is to get back to the orginal color. Currently I have "To" as transparent in the DragLeave event trigger but I want it to be my "old" color.
I tried to do a simple bind (same as my setter) but I got exception and read that you can't bind color animations. So is there something else I can use or get around this problem? In summary I simply want this:
Mouse enter row with drag and drop
Row change to color x
Mouse leave row (or I drop)
Row change back to it's color y (where y is different for different rows so not hard coded).
then add this to property to your animation and remove the second animation
FillBehavior="Stop"

WPF Storyboard Animation Loops Forever Even After Being Set

I have a a custom user control to animate based on a DependencyProperty which is bound to a DataTrigger. If the DependencyProperty is equal to Failure, it should animate the fill color of a rectangle (named buttonColor) within the user control.
For some reason though, it always loops forever even if I set the RepeatBehavior to any value including 1.
If I remove the RepeatBehavior attribute, it only plays the animation once (as expected). Here is the code which I have the issue:
<DataTrigger Binding="{Binding Path=ButtonAction.Status}" Value="Failure">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="Pulse"/>
<BeginStoryboard>
<Storyboard RepeatBehavior="1">
<ColorAnimation Storyboard.TargetName="buttonColor"
Storyboard.TargetProperty="Fill.Color"
To="{StaticResource FailedColor}"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
The correct syntax to repeat N times is:
<Storyboard RepeatBehavior="Nx">
for example:
<Storyboard RepeatBehavior="6x">
Setting a duration value will also limit the repeat behavior as it takes precedence. So if you have repeat behavior set on the ColorAnimationUsingKeyFrames tag but on the storyboard you set a Duration="0:0:4" then the animation will only repeat for 4 seconds.
To be clear and add onto the the answer, if one actually wants to repeat continuously one can set the RepeatBehavior to Forever such as here where I am rotating a vector around its center point.
<Storyboard x:Key="ChaseRotate" RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="path">
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
See How to: Rotate an Object - WPF .NET Framework
Full Example for Chasing Circles in Xaml.

WPF Datagrid events DataTriggers vs EventTriggers

relatively simple question that I am struggling to find a nice elegant solution to. I have a grid with a column that displays values that update every n seconds. I wish to show an animation when the value changes and flash the cell in different colours based on if the number is negative or positive. Found a whole host of methods that do almost what I want, but nothing that exactly matches my needs.
Using an EventTrigger I can make a cell flash every time an update occurs. Using a relatively simple animation below. But I am unable to make the colour the animation uses conditional as the storyboard freezes the UI elements, so I cannot use binding in the storyboard to define the colour.
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard HandoffBehavior="Compose" Name="GreenCell">
<Storyboard TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)">
<ColorAnimation Duration="0:0:1.50" From="Green" To="Transparent" AutoReverse="False"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
Using a DataTrigger I can make the cell colour conditional by creating a Converter that converts the updates to this Value and if the value is negative and then binding a data trigger to this and setting the background colour when this changes. But if the number is already negative, and remains negative (but changes) the trigger is not fired.
<DataTrigger Binding="{Binding Value, Converter={StaticResource cellBackGroundConverter}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard HandoffBehavior="Compose" Name="GreenCell">
<Storyboard TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)">
<ColorAnimation Duration="0:0:1.50" From="Green" To="Transparent" AutoReverse="False"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding Value, Converter={StaticResource cellBackGroundConverter}}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard HandoffBehavior="Compose" Name="RedCell">
<Storyboard TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)">
<ColorAnimation Duration="0:0:1.50" From="Red" To="Transparent" AutoReverse="False"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
What I require seems to be an event trigger, to capture every update and play the animation with the conditional aspect of the data trigger.
Surely I am missing something simple here, would love for someone to set me straight!
Many thanks
Matt

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.

DataTrigger don't seems to fire

I want to create a datatrigger that makes my page blink (from transparent to red). So I created a DataTrigger that listens to a boolean flag within my viewmodel. This flag shall indicate whenever the user needs to be reminded. In that case, my page shall blink from transparent to red.
I was pretty sure that I have implemented the data trigger in a correct manner, but my app does nothing - no error, no blinking... So I must have something missed.
<Style x:Key="ReminderPage" TargetType="{x:Type ViewTemplates:TpApplicationBarView}" BasedOn="{StaticResource TpApplicationBarViewStyle}">
<Style.Triggers>
<!-- Reminder animation, when the time comes to remind the user -->
<DataTrigger Binding="{Binding IndicateReminderAnimation}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard x:Name="Blink">
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
AutoReverse="True"
From="Transparent"
To="Red"
Duration="0:0:1"
RepeatBehavior="Forever">
</ColorAnimation >
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding IndicateReminderAnimation}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
AutoReverse="False"
To="Transparent"
Duration="0:0:1">
</ColorAnimation >
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
So, what do I have done wrong?
Update: I can see the following message in the output window:
System.Windows.Media.Animation Warning: 6 : Unable to perform action because
the specified Storyboard was never applied to this object for interactive control.;
Action='Stop'; Storyboard='System.Windows.Media.Animation.Storyboard';
Storyboard.HashCode='61356140'; Storyboard.Type='System.Windows.Media.Animation.Storyboard';
TargetElement='System.Windows.Media.Animation.Storyboard'; TargetElement.HashCode='61356140';
TargetElement.Type='System.Windows.Media.Animation.Storyboard'
Update2: After googling arround I found out, that it is a problem with the UI Thread. So I made a dispatcher call whenever I set the bound property. But even with this trick, there's no color animation. But the error in the output window seems to be vanished. So, I'm searching for further ideas on how to fix the animation.
Update3: It seems to be a general problem setting the background color of the page. But it's really strange. The Page is placed in a NavigationFrame. Setting the background color of the navigation frame will change the color of the application, but setting the background color of the page (even without any animation) won't change anything.
I think you will have to set the animations Target, something like this -
Storyboard.TargetName="yourWindowName"
You may have already checked this, but make sure that correct object is set as your TpApplicationBarView's DataContext(having IndicateReminderAnimation property).
I found the bug - or better the two bugs.
1.) It seems not be possible to change the background color of a page that is placed within a Navigation Frame.
So first was to move the binding and event to the MainWindow itself (wpf window class)
2.) The Style that contains the data trigger did not work. After googling around I found a working solution for what I'm searching for.
<Storyboard x:Key="RemindUser" >
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
AutoReverse="True"
From="Transparent"
To="{StaticResource WinAccentBackgroundColor}"
Duration="0:0:1"
RepeatBehavior="Forever">
</ColorAnimation >
</Storyboard>
<Storyboard x:Key="StopRemindUser">
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
AutoReverse="True"
To="Transparent"
Duration="0:0:1">
</ColorAnimation >
</Storyboard>
<Style x:Key="ReminderWindow" TargetType="{x:Type Metro:SnappedTransparentWindow}" BasedOn="{StaticResource TransparentWindow}">
<Style.Triggers>
<!-- Reminder animation, when the time comes to remind the user -->
<DataTrigger Binding="{Binding IndicateReminderAnimation}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource RemindUser}"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource StopRemindUser}"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
The key was to split the binding and storyboard into different parts.

Resources