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>
Related
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>
My req is to have a grid of 2 rows - the first row will be filled with comboboxes as search criteria for a DB query. The second row will be a DataGrid with the results.
I want the upper grid to slide down from the top when I hover over it, and slide back up on mouse leave. I guess I can have a simple textblock at the top "Filters" and hovering over it will bring down the comboboxes?
I have something like this, but when mouse over, the animation goes up/down with out stopping.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ControlsGrid"
Storyboard.TargetProperty="(Grid.Height)"
From="0"
To="66"
Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ControlsGrid"
Storyboard.TargetProperty="(Grid.Height)"
From="66"
To="0"
Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<TextBlock HorizontalAlignment="Center" FontSize="20" Text="Filters..."/>
</Grid>
<Grid Margin="0" Name="ControlsGrid" VerticalAlignment="top" Background="Yellow"/>
<DataGrid Grid.Row="1" ColumnHeaderStyle="{DynamicResource GridViewColumnHeaderStyle}" Background="LightGray" RowBackground="LightYellow" AlternatingRowBackground="LightBlue"
x:Name="dataGridViewRoomQuery" BorderBrush="Gray" BorderThickness="5"/>
</Grid>
The problem with your approach is that the "hovering" Grid (the one with the event triggers) gets a MouseLeave event whenever the ControlsGrid is placed above it.
You have to put the ControlsGrid into the hovering Grid:
<Grid Grid.Row="0" Background="Transparent">
<Grid.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ControlsGrid"
Storyboard.TargetProperty="Height"
To="66" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ControlsGrid"
Storyboard.TargetProperty="Height"
To="0" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<TextBlock HorizontalAlignment="Center" FontSize="20" Text="Filters..."/>
<Grid Name="ControlsGrid" Height="0" VerticalAlignment="Top" Background="Yellow">
</Grid>
</Grid>
<DataGrid .../>
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 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 a wrap panel full of usercontrols. When I hover the mouse over a usercontrol I want it to expand to show more details.
Some stripped down sample code:
<UserControl x:Class="WPFTestBed.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<UserControl.Resources>
</UserControl.Resources>
<UserControl.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<EventTrigger.Actions>
<Setter TargetName="WPFTestBed.UserControl1" Property="Control.Width" Value="200"/>
</EventTrigger.Actions>
</EventTrigger>
</UserControl.Triggers>
<Grid Height="95" Width="123">
<Button Height="23" HorizontalAlignment="Left" Margin="17,30,0,0" Name="button1" VerticalAlignment="Top" Width="75">Button</Button>
</Grid>
</UserControl>
I would appreciate it if someone could point out where I'm going wrong, and set me down the correct path.
Ideally, I want the usercontrol to delay for x seconds when there is a mouseover, before expanding and showing the extra details.
Here is a solution (tested):
Keep in mind that you have to set a Width manually on the UserControl for this to work.
Then add this to your UserControl's body:
<UserControl.Style>
<Style>
<Style.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard BeginTime="0:0:4.0" Storyboard.TargetProperty="Width">
<DoubleAnimation To="200" Duration="0:0:1.0"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
It will start after 4 seconds, then animate the width from its current value to 200, over 1 second.
I've managed to get a bit further with what I want:
<UserControl x:Class="WPFTestBed.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="200" Width="170">
<UserControl.Resources>
<Storyboard x:Key="ExpandDisplay">
<DoubleAnimation Storyboard.TargetProperty="(UserControl.Width)"
From="200" To="380" Duration="0:0:0.25" AutoReverse="False"/>
<DoubleAnimation Storyboard.TargetProperty="(Border.Width)"
Storyboard.TargetName="mainBorder"
From="190" To="370" Duration="0:0:0.25" AutoReverse="False"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="button2"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="CollapseDisplay">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="button2"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetProperty="(UserControl.Width)"
From="380" To="200" Duration="0:0:0.25" AutoReverse="False"/>
<DoubleAnimation Storyboard.TargetProperty="(Border.Width)"
Storyboard.TargetName="mainBorder"
From="370" To="190" Duration="0:0:0.25" AutoReverse="False"/>
</Storyboard>
</UserControl.Resources>
<UserControl.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseDown">
<BeginStoryboard Storyboard="{StaticResource ExpandDisplay}"/>
</EventTrigger>
<EventTrigger RoutedEvent="Button.Click" SourceName="button2">
<BeginStoryboard Storyboard="{StaticResource CollapseDisplay}"/>
</EventTrigger>
</UserControl.Triggers>
<Border Name="mainBorder" Height="190" Width="160" Background="Black" CornerRadius="20,20,20,20">
<Border.BitmapEffect>
<DropShadowBitmapEffect Color="Black" Direction="320" ShadowDepth="10" Opacity=".5"
Softness="9"/>
</Border.BitmapEffect>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Height="23" Width="75" VerticalAlignment="Center" HorizontalAlignment="Center" Name="button1" >Details</Button>
<Button Grid.Row="1" Height="23" Width="23" VerticalAlignment="Center" HorizontalAlignment="Center" Name="button2" Visibility="Collapsed">-</Button>
</Grid>
</Border>
</UserControl>
This expands the usercontrol when I click, rather than mouseover, as I couldn't find a satisfactory way to wait for a second before beginning. I could delay the start and cancel on a mouseleave but this also cancelled the expand whenever the mouseleave was called.
I have a button to collapse the usercontrol, but I would prefer to switch which storyboard the usercontrol onclick event calls, can this be done?