I am using MVVM so I need to bind a rectangle shape a property which will eventually animate it.
I've found few solutions and tried to replicate their approach but the storybroad doesnt kick in
I am including my source code
<Rectangle x:Name="RectangleCompleted" HorizontalAlignment="Left" Height="50" Margin="440,178,0,0" VerticalAlignment="Top" Width="100" Stroke="#FF0BB3AE" StrokeThickness="5" RadiusX="8" RadiusY="7" >
<i:Interaction.Triggers>
<ei:DataTrigger Binding="{Binding Path=Animation}" Value="ON" Comparison="Equal">
<ei:ControlStoryboardAction
Storyboard="{StaticResource MyAnimation}" ControlStoryboardOption="Play"/>
</ei:DataTrigger>
</i:Interaction.Triggers>
<Rectangle.Effect>
<DropShadowEffect RenderingBias="Quality" ShadowDepth="3" Color="#FFD2D6D8"/>
</Rectangle.Effect>
<Rectangle.Fill>
<SolidColorBrush Color="#FFE7F4F6"/>
</Rectangle.Fill>
</Rectangle>
<Window.Resources>
<Storyboard x:Key="MyAnimation">
<DoubleAnimation
Storyboard.TargetName="RectangleCompleted"
Storyboard.TargetProperty="Opacity"
From="0"
To="1"
RepeatBehavior="Forever"
AutoReverse="True"/>
</Storyboard>
</Window.Resources>
Is your Animation property a bool or string? If it's a bool you need the value in your trigger check for True or False. If it's a string, then you should be fine with what you got. Provided your implementation of INPC and the Animation property is right this should work fine.
I've put together an example with the code you've posted and you should see it work fine. Download Link
I've made two small change. I've added a Button to toggle the animation to either start or stop and put the entire thing in a StackPanel. Secondly I made the Animation property a bool so just had the Triggers to check for "True" or "False" than the "ON" value you were checking for.
<StackPanel x:Name="LayoutRoot">
<Rectangle x:Name="RectangleCompleted"
Width="100"
Height="50"
RadiusX="8"
RadiusY="7"
Stroke="#FF0BB3AE"
StrokeThickness="5">
<Rectangle.Resources>
<Storyboard x:Key="MyAnimation">
<DoubleAnimation AutoReverse="True"
From="0"
RepeatBehavior="Forever"
Storyboard.TargetName="RectangleCompleted"
Storyboard.TargetProperty="Opacity"
To="1" />
</Storyboard>
</Rectangle.Resources>
<i:Interaction.Triggers>
<ei:DataTrigger Binding="{Binding Path=Animation}"
Comparison="Equal"
Value="True">
<ei:ControlStoryboardAction ControlStoryboardOption="Play"
Storyboard="{StaticResource MyAnimation}" />
</ei:DataTrigger>
<ei:DataTrigger Binding="{Binding Path=Animation}"
Comparison="Equal"
Value="False">
<ei:ControlStoryboardAction ControlStoryboardOption="Stop"
Storyboard="{StaticResource MyAnimation}" />
</ei:DataTrigger>
</i:Interaction.Triggers>
<Rectangle.Effect>
<DropShadowEffect RenderingBias="Quality"
ShadowDepth="3"
Color="#FFD2D6D8" />
</Rectangle.Effect>
<Rectangle.Fill>
<SolidColorBrush Color="#FFE7F4F6" />
</Rectangle.Fill>
</Rectangle>
<Button Command="{Binding ToggleCommand}"
Content="Toggle Animation" />
</StackPanel>
and all ToggleCommand does is:
ToggleCommand = new RelayCommand(() => Animation = !Animation);
Update:
If you just want to trigger your animation when the window loads, all you need is:
<Window.Resources>
<Storyboard x:Key="MyAnimation">
<DoubleAnimation AutoReverse="True"
From="0"
RepeatBehavior="Forever"
Storyboard.TargetName="RectangleCompleted"
Storyboard.TargetProperty="Opacity"
To="1" />
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource MyAnimation}" />
</EventTrigger>
</Window.Triggers>
Related
I wish to bind DoubleAnimation.To to a dependency property and trigger the animation when that property changes. I can get the basic animation to work, but cannot figure out how to trigger it when the property changes.
The example shows me trying to animate Rectangle's Canvas.Left property. Note that I have bound DoubleAnimation.To to Position. I want the animation to run and move my rectangle when Position changes. Can someone help me figure out the correct trigger?
<Viewbox>
<Canvas Height="200" Width="200">
<Rectangle Name="MyRectangle" Canvas.Left="50" Canvas.Top="50" Width="100" Height="100" Fill="Blue">
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard Name="MyStoryboard">
<DoubleAnimation Storyboard.TargetName="MyRectangle"
Storyboard.TargetProperty="(Canvas.Left)"
To="{Binding Position}" Duration="0:0:5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Canvas>
The answer is to use an EventTrigger and set its RoutedEvent property to Binding.TargetUpdated. Also, you must add NotifyOnTargetUpdated=True to your binding (Position, in my case).
<Viewbox>
<Canvas Height="200" Width="200">
<Rectangle Name="MyRectangle" Canvas.Left="50" Canvas.Top="50" Width="100" Height="100" Fill="Blue">
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard Name="MyStoryboard">
<DoubleAnimation Storyboard.TargetName="MyRectangle"
Storyboard.TargetProperty="(Canvas.Left)"
To="{Binding Position, NotifyOnTargetUpdated=True}" Duration="0:0:5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Canvas>
</Viewbox>
I have a TabControl where each TabItem has a separate control as its Content element. Now, I can easily execute a storyboard when switching to a tab by using a UserControl.Loaded EventTrigger. However, I also want to run an exit animation when switching from one tab to another (i.e. allow the old Content control to animate away, followed by the new Content control's entrance animation).
Is it possible to do this with standard WPF constructs?
If not, how would I go about developing a custom solution that handles this?
Edit:
I went ahead and made a modified TabControl that extends the base TabControl and overrode its OnSelectionChanged method as follows:
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
if (e.AddedItems.Count == 1 && e.RemovedItems.Count == 1)
{
var oldTab = e.RemovedItems[0] as TabItem;
if (oldTab != null)
{
var exitStoryboard = /** code to find the storyboard **/
if (exitStoryboard != null)
{
exitStoryboard.Completed = (_, __) => base.OnSelectionChanged(e);
exitStoryboard.Begin();
return;
}
}
}
base.OnSelectionChanged(e);
}
This works, except when I click between the tabs too quickly in which case the base.OnSelectionChanged never gets called, presumably because the storyboard is not active any more. Tips?
Here's a solution for 2 tabs, the general idea is to pop up an image of the last tab after the selection changes, and then fade the current tab in.
With a little effort you could probably genericize it for any number of tabs by tracking the last tab for the VisualBrush in a property instead of the hardcoded "other" tab used here.
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TabControl>
<TabItem x:Name="tab1" Header="Tab 1">
<Border Background="Transparent">
<Grid>
<TextBlock FontSize="40" Foreground="Red" Text="Tab 1 Contents">
<TextBlock.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity">
<DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="0.0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimation
Duration="0:0:0.25"
From="1"
Storyboard.TargetName="tab2Shadow"
Storyboard.TargetProperty="Opacity"
To="0"/>
<DoubleAnimation
BeginTime="0:0:0.25"
Duration="0:0:0.25"
From="0"
Storyboard.TargetProperty="Opacity"
To="1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
<Rectangle x:Name="tab2Shadow">
<Rectangle.Fill>
<VisualBrush
AlignmentX="Left"
AlignmentY="Top"
AutoLayoutContent="False"
Stretch="None"
Visual="{Binding ElementName=tab2, Path=Content}"/>
</Rectangle.Fill>
</Rectangle>
</Grid>
</Border>
</TabItem>
<TabItem x:Name="tab2" Header="Tab 2">
<Border Background="Transparent">
<Grid>
<TextBlock FontSize="40" Foreground="Red" Text="Tab 2 Contents">
<TextBlock.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity">
<DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="0.0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimation
Duration="0:0:0.25"
From="1"
Storyboard.TargetName="tab1Shadow"
Storyboard.TargetProperty="Opacity"
To="0"/>
<DoubleAnimation
BeginTime="0:0:0.25"
Duration="0:0:0.25"
From="0"
Storyboard.TargetProperty="Opacity"
To="1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
<Rectangle x:Name="tab1Shadow">
<Rectangle.Fill>
<VisualBrush
AlignmentX="Left"
AlignmentY="Top"
AutoLayoutContent="False"
Stretch="None"
Visual="{Binding ElementName=tab1, Path=Content}"/>
</Rectangle.Fill>
</Rectangle>
</Grid>
</Border>
</TabItem>
</TabControl>
</Grid>
You can use the TabControl.SelectionChanged event.
You can define a custom style for your tabcontrol and do animation on Selector.SelectionChanged RoutedEvent
<Style x:Key="{x:Type TabControl}"
TargetType="TabControl">
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="40" />
<RowDefinition />
</Grid.RowDefinitions>
<UniformGrid Grid.Row="0"
Rows="1"
IsItemsHost="True" />
<ContentPresenter x:Name="TabContent"
Grid.Row="2"
Content="{TemplateBinding SelectedContent}">
</ContentPresenter>
</Grid>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Selector.SelectionChanged">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="0"
To="1"
Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="TabContent"
Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I am trying to achieve something similar to iPhone message view. On a button click, a delete button would slide out for every message and on clicking "done" the delete buttons would slide in. I have been able to achieve that using mvvm except the sliding effect.
Here is the style I could write with my limited knowledge:
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding ShowDeleteButton}" Value="false">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
Here "ShowDeleteButton" is a bool property in the view model. This works fine. but the collapsing and "getting visible" is happening abruptly and very fast. I need some sliding animation.
Please note that as the delete buttons disappear, the rest of the controls should take up the rest of space and, as the button appear, the existing controls should shrink and give space to the buttons.
I tried with storyboard animation but even after a couple hours I am not able to figure out the strange errors it is throwing. Here is the code:
<DataTrigger Binding="{Binding ShowDeleteButton}" Value="false">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard BeginTime="0:0:1">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visiblity">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Collapsed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard BeginTime="0:0:1">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visiblity">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
Can someone please help the soul in distress?
regards
Old question... huh...
Anyway... forget about visibility. The trick is to animate Width/Height of the Control placed in Grid's Column/Row where the Column/Row has Width set to Auto. Uff :)
So here is a Grid named "container" with two columns. In the first column can be placed whatever you want, but you should align it to Right since you want it to move when your delete button is displayed. And in the second column there is a Control (representing your DeleteButton) Notice that first column has Width="*" and second column has Width="Auto"
<Grid Background="Purple" Width="350" Height="100">
<CheckBox Content="Show" IsChecked="{Binding Path=ShowDeleteButton}" Height="16" HorizontalAlignment="Left" Margin="12,12,0,0" Name="checkBox1" VerticalAlignment="Top" />
<Grid x:Name="container" Margin="40,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid Name="content" Margin="0">
<Label Content="Look I'm flyin'!" Margin="0,30,10,0" VerticalAlignment="Top" HorizontalAlignment="Right"/>
</Grid>
<Control Grid.Column="1" Margin="0">
<Control.Template>
<ControlTemplate>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ShowDeleteButton}" Value="true">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="flyout"
Storyboard.TargetProperty="(Grid.Width)"
From="0" To="120" Duration="0:0:1" AutoReverse="False"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="flyout"
Storyboard.TargetProperty="(Grid.Width)"
From="120" To="0" Duration="0:0:1" AutoReverse="False"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</ControlTemplate.Triggers>
<Grid x:Name="flyout" Width="0">
<Rectangle Fill="Green"/>
<Label Content="I'm DELETE button" Margin="10,30,0,0" VerticalAlignment="Top"/>
</Grid>
</ControlTemplate>
</Control.Template>
</Control>
</Grid>
</Grid>
And the Control in Grid's second column has DataTrigger based on your ViewModel's property "ShowDeleteButton"
Since the Width of column where the Control is placed is Auto you can change the width of the Control using simple DoubleAnimation and you get desired behavior.
EDIT
Look at this if you want the flyout to be over everything else.
<Grid x:Name="LayoutRoot">
<Grid x:Name="container" Width="120" HorizontalAlignment="Right">
<Button x:Name="button" Content="In" Margin="0,13,7.5,0" VerticalAlignment="Top" HorizontalAlignment="Right" d:LayoutOverrides="HorizontalAlignment">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="flyout"
Storyboard.TargetProperty="(Grid.Width)"
From="0" To="120" Duration="0:0:1" AutoReverse="False"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</Grid>
<Grid x:Name="flyout" Width="120" Margin="0" HorizontalAlignment="Right">
<Rectangle Fill="Green"/>
<Label Content="This is some label" Margin="6.038,27,1.006,0" VerticalAlignment="Top"/>
<Button Content="Out" Margin="11.917,58,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="80.083">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="flyout"
Storyboard.TargetProperty="(Grid.Width)"
From="120" To="0" Duration="0:0:1" AutoReverse="False"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</Grid>
</Grid>
Please note that button "Out" can be clicked while it's moving. It should be probably disabled. I just did it like this because I wanted to do it simple and all in xaml. Sure you can use mahapps. Or you can just make it as fast as it is in MahApps and it's impossible to click it twice. :) I have looked at MahApps FlyOutDemo and my solution looks much simpler to me. And I don't need third party library.
I have a simple button (3 of them - all same except the content binding which is just an integer.
<Button Width="24" Height="24" Style="{StaticResource CircleButton}"
Background="#CC2B2B" Foreground="#FFFFFF"
Content="{Binding PinnedItemsCount,
FallbackValue=0,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
NotifyOnTargetUpdated=True}"
Name="ui_btnPinnedCount" />
How do I make the button boune when the data changes?
The underlying content implements INotifyPropertyChanged and the data bindings are all working, just need the button to bounce 3 times once the data changes....
Thanks!
Use animations with a DataTrigger
http://msdn.microsoft.com/en-us/library/system.windows.media.animation.bounceease.aspx
How to: Trigger an Animation When Data Changes
<Rectangle Name="myRectangle" Width="200" Height="30" Fill="Blue">
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseDown">
<BeginStoryboard>
<Storyboard>
<Storyboard x:Name="myStoryboard">
<DoubleAnimation From="30" To="200" Duration="00:00:3"
Storyboard.TargetName="myRectangle"
Storyboard.TargetProperty="Height">
<DoubleAnimation.EasingFunction>
<BounceEase Bounces="2" EasingMode="EaseOut"
Bounciness="2" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
This makes it fade in and out, replace with any sotryboard animation I imagine.
<Button Width="24" Height="24" Style="{StaticResource CircleButton}" Background="#CC2B2B" Foreground="#FFFFFF"
Content="{Binding PinnedCount, FallbackValue=0, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnTargetUpdated=True}"
Name="ui_btnPinnedCount">
<Button.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard AutoReverse="True">
<DoubleAnimation Storyboard.TargetName="ui_btnPinnedCount" Storyboard.TargetProperty="Opacity" To=".1" Duration="0:0:.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
I have the following XAML code which triggers an animation of the rectangle when the LastDataUpdate field is changed. LastDataUpdate is a DateTime within a class that implements INotifyPropertyChanged. I'd like the animation to NOT run if the LastDataUpdate==DateTime.MinValue. Is there any way I can implement this in the XAML?
<Rectangle x:Name="NewDataAnimation" Tag="{Binding Path=LastDataUpdate, NotifyOnTargetUpdated=True}" Opacity="0" Width="5" Height="5" Fill="LawnGreen" HorizontalAlignment="Left" VerticalAlignment="Top">
<Rectangle.Style>
<Style>
<Style.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:0" To="1.0" />
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:4" From="1.0" To="0.0" BeginTime="0:0:2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
Andrew
Why, not trigger on the property itself and have a converter determine whether to execute animation?
Below XAML assumes that you create a converter which will evaluate the LastDataUpdate against the value that you don't want. If you set LastDataUpdate to DateTime.MinValue, the animation won't start. As soon as you make a change, the binding will be forced to re-evaluate through PropertyChanged, and will let converter do its decision-making...
<Rectangle Opacity="0" Width="5" Height="5" Fill="LawnGreen" HorizontalAlignment="Left" VerticalAlignment="Top">
<Rectangle.Style>
<Style>
<Style.Triggers>
<Trigger Property="{Binding LastDataUpdate, Converter={StaticResource LastDataUpdateToDoAnimation}}" Value="True" >
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:0" To="1.0" />
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:4" From="1.0" To="0.0" BeginTime="0:0:2" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>