I have a ListBox with items that will be bound to a datasource and ItemTemplate will be a user control. I need to get new items added to the datasource collection to animate / move in from out of view into view.
Here is what I have. (please ignore the lack of data binding / ItemTemplate) I want to focus on the animation.
<ListBox Width="350" Height="125">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="LayoutTransform">
<Setter.Value>
<ScaleTransform x:Name="transform"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<EventTrigger RoutedEvent="Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:5" />
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)" To="-0"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBoxItem>
<Rectangle Width="320" Height="50" Fill="Green">
<Rectangle.RenderTransform>
<TranslateTransform X="-50"/>
</Rectangle.RenderTransform>
</Rectangle>
</ListBoxItem>
</ListBox>
If you run this, you will see the opacity change works. However the item does not animate to 0. Nor does it work if I add a duration.
This is how I do an upwards slide animation on listboxitems:
<ListBox DataContextChanged="klantListBox_DataContextChanged" Name="klantListBox" Width="380" Height="255" Margin="0 10 10 0" ItemsSource="{Binding}" SelectionChanged="klantListBox_SelectionChanged">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="RenderTransform">
<Setter.Value>
<TranslateTransform Y="230" X="0" />
</Setter.Value>
</Setter>
<Style.Triggers>
<EventTrigger RoutedEvent="Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.(TranslateTransform.Y)" To="0"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
The animation is being applied to the ListBoxItem thanks to the
TargetType="{x:Type ListBoxItem}"
however the transformation is on the
Rectangle.RenderTransform.
Move the <TranslateTransform x="-50"> to the ListBoxItem's RenderTransform.
Related
For the past week, I tried in vain to find a way to trigger a path animation.
What I want to do is by using a boolean property defined in my ViewModel, so that when this value is true, an rectangle shall move along the path.
I thought it was easy at first but...
Demos of Path-Animation I found, would trigger the Storyboard by means of RoutedEvent like clicking a button or Button.Loaded etc., and I haven't got a way to trigger it by DependencyProperty.
I am new to WPF and thank you in advance!
Code here:
<!--I define a rectangle which is expected to be auto-moving along the path when "Monitoring" is set true. -->
<Rectangle Width="20" Height="10" Fill="LightBlue">
<Rectangle.RenderTransform>
<MatrixTransform x:Name="RectangleMatrixTransform">
<MatrixTransform.Matrix >
<Matrix />
</MatrixTransform.Matrix>
</MatrixTransform>
</Rectangle.RenderTransform>
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<DataTrigger Binding="{Binding Monitoring}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<!--Here I got compile exception: 'TargetName property cannot be set on a Style Setter.'-->
<MatrixAnimationUsingPath
Storyboard.TargetName="RectangleMatrixTransform"
Storyboard.TargetProperty="Matrix"
DoesRotateWithTangent="True"
Duration="0:0:5"
RepeatBehavior="Forever" >
<MatrixAnimationUsingPath.PathGeometry>
<PathGeometry Figures="M 10,100 C 35,0 135,0 160,100 180,190 285,200 310,100"
PresentationOptions:Freeze="True" />
</MatrixAnimationUsingPath.PathGeometry>
</MatrixAnimationUsingPath>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</Canvas>
BTW, WPF is strong but really tough :(
Just drop Storyboard.TargetName and use Storyboard.TargetProperty="RenderTransform.Matrix":
<Rectangle Width="20" Height="10" Fill="LightBlue">
<Rectangle.RenderTransform>
<MatrixTransform />
</Rectangle.RenderTransform>
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<DataTrigger Binding="{Binding Monitoring}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<MatrixAnimationUsingPath
Storyboard.TargetProperty="RenderTransform.Matrix"
DoesRotateWithTangent="True"
Duration="0:0:5"
RepeatBehavior="Forever" >
<MatrixAnimationUsingPath.PathGeometry>
<PathGeometry Figures="M 10,100 C 35,0 135,0 160,100 180,190 285,200 310,100" />
</MatrixAnimationUsingPath.PathGeometry>
</MatrixAnimationUsingPath>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
Im trying to create an effect in my tabcontrol when draging over a tab I want to move it x number of pixels to the right creating a sliding effect as I drag over, but I cant seem to get the animation to fire. What am I missing here:
<Style TargetType="{x:Type TabItem}">
<Setter Property="RenderTransform">
<Setter.Value>
<TransformGroup>
<RotateTransform />
<TranslateTransform />
<SkewTransform />
<ScaleTransform />
</TransformGroup>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="TabItem.DragEnter" >
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(TabItem.RenderTransform).(TranslateTransform.X)"
From="0"
To="50"
Duration="0:0:0.5"
FillBehavior="Stop"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
You have set the RenderTransform property to a TransformGroup and you want to animate the X property of the second child of this one so the Storyboard.TargetProperty should be set to RenderTransform.Children[1].X:
<DoubleAnimation
Storyboard.TargetProperty="RenderTransform.Children[1].X"
From="0"
To="50"
Duration="0:0:0.5"
FillBehavior="Stop"/>
Your current DoubleAnimation should work if you set the RenderTransform property to a TranslateTransform:
<Setter Property="RenderTransform">
<Setter.Value>
<TranslateTransform />
</Setter.Value>
</Setter>
I have a listview with some items. Also I have two buttons, add and delete. I have implemented drag and drop functionality in order to drag a single listviewitem from listview to the delete button and then on delete button drop, delete the listview item.
When listview item is dragged from listview to delete button and mouse is over the button (during drap and drop operation and before drop on button) I want to change the button image. It is working if I create a data trigger on button on IsMouseOver property, but I only want to change the button image when mouse is over the delete button during drag and drop operation (before drop). How can I do this?
<Button AllowDrop="True" Drop="btnDrop_Drop" Height="64" Width="64" Margin="10" Click="Button_Click_Delete" Content="Delete">
<Button.Template>
<ControlTemplate>
<StackPanel>
<Image x:Name="image1" Visibility="Visible" Height="36" Width="36" Stretch="UniformToFill" Source="Resources/icons8-Trash Can-48.png"/>
<Image x:Name="image2" Visibility="Collapsed" Height="36" Width="36" Stretch="UniformToFill" Source="Resources/icons8-Trash Can-48-3.png"/>
<Label HorizontalAlignment="Center">Delete</Label>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="image1" Property="Visibility" Value="Collapsed" />
<Setter TargetName="image2" Property="Visibility" Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
You could use EventTriggers to handle the Drop Target Events.
<ControlTemplate>
<StackPanel>
<Image x:Name="image1" Opacity="1" ... />
<Image x:Name="image2" Opacity="0" ... />
...
</StackPanel>
<ControlTemplate.Resources>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<Trigger Property="Opacity" Value="0">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
</Style.Triggers>
</Style>
<Storyboard x:Key="swapImages" TargetProperty="Opacity">
<DoubleAnimation Storyboard.TargetName="image1" To="0" Duration="0" />
<DoubleAnimation Storyboard.TargetName="image2" To="1" Duration="0"/>
</Storyboard>
<Storyboard x:Key="resetImages" TargetProperty="Opacity">
<DoubleAnimation Storyboard.TargetName="image1" To="1" Duration="0" />
<DoubleAnimation Storyboard.TargetName="image2" To="0" Duration="0"/>
</Storyboard>
</ControlTemplate.Resources>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="DragOver">
<BeginStoryboard Storyboard="{StaticResource swapImages}"/>
</EventTrigger>
<EventTrigger RoutedEvent="DragLeave">
<BeginStoryboard Storyboard="{StaticResource resetImages}"/>
</EventTrigger>
<EventTrigger RoutedEvent="Drop">
<BeginStoryboard Storyboard="{StaticResource resetImages}"/>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
adressing Opacity instead of Visibility.
when defining Storyboards in Resources, their location in the xaml file is important. Resources should be defined first (before the Triggers using them) or initialization will fail.
I have ListBox:
<ListBox x:Name="ListBoxImages"
ScrollViewer.CanContentScroll="True"
UseLayoutRounding="False"
SelectionMode="Extended"/>
ListBox style:
<Style TargetType="{x:Type ListBox}">
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<Border Name="Border">
<ScrollViewer Focusable="false">
<WrapPanel ItemWidth="100"
IsItemsHost="True"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and ListBoxItem style (animation here, sorry for long code):
<Style TargetType="{x:Type ListBoxItem}">
<!--...-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="border"
RenderTransformOrigin="0.5,0.5">
<Border.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="ScaleTransform"/>
</TransformGroup>
</Border.RenderTransform>
<ContentPresenter/>
</Border>
<!--Animation-->
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleX"
Duration="0:0:0.1"
From="0" To="1"/>
<DoubleAnimation Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleY"
Duration="0:0:0.1"
From="0" To="1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="FrameworkElement.Unloaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleX"
Duration="0:0:0.1"
To="0"/>
<DoubleAnimation Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleY"
Duration="0:0:0.1"
To="0"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Question. Animations when adding the element (FrameworkElement.Loaded) played not always. Such feeling that it is played when an item has been created, but not yet displayed.
The animation when item is deleted (FrameworkElement.Unloaded) does not play.
So, how to fix it?
Your Loaded storyboard is defined correctly, thus there should be other reasons why it is sometimes played correctly and sometimes not. Is there a long-living operation on the UI thread when a new item is added to the list box? This would result in a freeze so that the animation is not played fluently all the time.
Your Unloaded storyboard is not played because this event is raised when the element is removed from the visual / logical tree that is used to render the whole scene. This storyboard should be started before this removal but unfortunately there is no mechanism / event that tells that an item is to be removed.
Currently there is no easy way to fade out an item from an ItemsControl in WPF. In WinRT and Silverlight, there are two separate visual states for ItemsControl items that you can use for fade-in or fade-out. As Krishna mentioned, the only way is to implement custom functionality to tell an item that it is about to be removed and that it should run the fade-out animation. After that animation, the item can be removed from the visual / logical tree.
I want to show a popup with delay 2 seconds always when any cell of a column is selected and the cursor stays longer than 2 seconds. I could implement that with timers and eventhandlers but I think,I can also achieve that with storyboards. I try to do that so:
<Window x:Class="Wpfpopupstoryboard.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<Storyboard x:Key="ShowPopup">
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="LockPopup" Storyboard.TargetProperty="(Popup.IsOpen)">
<DiscreteBooleanKeyFrame KeyTime="00:00:02.0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="HidePopup" Storyboard.TargetName="LockPopup" Storyboard.TargetProperty="(Popup.IsOpen)">
<BooleanAnimationUsingKeyFrames>
<DiscreteBooleanKeyFrame KeyTime="00:00:00.1" Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<DataGrid Name="MyGrid" xmlns:sys="clr-namespace:System;assembly=mscorlib" IsReadOnly="True">
<DataGrid.Resources>
<Style TargetType="DataGridCell">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridCell">
<StackPanel>
<Border x:Name="border"
BorderThickness="2"
BorderBrush="Silver">
<ContentPresenter Name="MyContentPresenter" Content="{Binding}"/>
</Border>
<Popup x:Name="LockPopup" PlacementTarget="{Binding ElementName=MyContentPresenter}" Placement="Bottom" DataContext="{Binding}">
<TextBlock Text="This is a popup" Background="White" Foreground="Black" />
</Popup>
</StackPanel>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="StackPanel.MouseLeftButtonDown">
<BeginStoryboard Storyboard="{StaticResource ShowPopup}"/>
</EventTrigger>
<EventTrigger RoutedEvent="StackPanel.MouseLeave">
<BeginStoryboard Storyboard="{StaticResource HidePopup}"/>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding}" Header="column" />
</DataGrid.Columns>
<sys:String>item 1</sys:String>
<sys:String>item 2</sys:String>
<sys:String>item 3</sys:String>
<sys:String>item 4</sys:String>
</DataGrid>
</Grid>
What is wrong? Can somebody help me?
Thank you!
With using RemoveStoryboard action, you need just 1 Storyboard (to show the popup). Also the right event to trigger the Storyboard here is Selected instead of StackPanel.MouseLeftButtonDown:
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Selected">
<BeginStoryboard Storyboard="{StaticResource ShowPopup}" Name="bg"/>
</EventTrigger>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard Storyboard="{StaticResource ShowPopup}" Name="bg2"/>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<RemoveStoryboard BeginStoryboardName="bg"/>
</EventTrigger>
</ControlTemplate.Triggers>
You can delete the HidePopup Storyboard, because we don't need it with the code above.
In fact you can also use StackPanel.PreviewMouseLeftButtonDown, somehow the StackPanel.MouseLeftButtonDown is suppressed (while bubbling up from the inner elements). However it's just a bit explanation about why it's not working at first. Using the Selected event is the best choice.
MouseLeftButtonDown event have a Direct Routing strategy (see UIElement.MouseLeftButtonDown Event (msdn)).
So it will not bubble to your ContentTemplate.
You need to set the event trigger on your StackPanel directly.