WPF - DataTrigger animation doesn't work - c#

So I'm trying to make some animations during item removal from ItemsControl which is attached to ObservableCollectio<Item>
I know I can't do this in unload event because it's simply too late to perform any animation so I've tried to do this using DataTrigger
My xaml file looks like this:
<DataTemplate DataType="{x:Type MyApp:Item}">
<Border x:Name="ItemBorder">
<Label Content="{Binding Path=Name}" />
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Removing}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1" From="1.0" To="0.0"
Storyboard.TargetProperty="(Border.Opacity)" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
and my Item class is simply:
public class Item : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private bool removing;
public bool Removing {
get
{
return removing;
}
set
{
removing = value;
PropertyChanged(this, new PropertyChangedEventArgs("Removing"));
}
}
// same with `Name` property
}
I would like to start an animation by setting item.Removing = true but nothing happens.
What am I doing wrong?

You will have to update your animation as below i.e give elementname:
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Removing}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1" From="1.0" To="0.0"
Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ItemBorder" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</DataTemplate.Triggers>
OR
Try putting animation directly on your border style like below:
<DataTemplate DataType="{x:Type MyApp:Item}">
<Border x:Name="ItemBorder">
<Label Content="{Binding Path=Name}" />
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Removing}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1" From="1.0" To="0.0"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<Style.Triggers>
</Style>
</DataTemplate>

Related

c# DataTrigger Storyboard issue

I want my grid to show up on button click, and hide when clicked again. When i add one DataTrigger to grid it works fine, however for two of them it works only on the latter one and when it's supossed to play animation for the first one, it just basicly does nothing. Here is the code
<Grid Background="Red" HorizontalAlignment="Left" VerticalAlignment="Top">
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding Open}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1" Storyboard.TargetProperty="Width" From="0" To="300"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding Open}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1" Storyboard.TargetProperty="Width" From="300" To="0"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Button />
</Grid>
And my ViewModel
public bool Open
{
get
{
return _open;
}
set
{
_open = value;
RaisePropertyChanged("Open");
}
}
public RelayCommand OpenButtonClicked
{
get;
private set;
}
private bool _open;
public AppMenuViewModel() : base()
{
_open = false;
OpenButtonClicked = new RelayCommand(() => Open = !Open);
}
Where is the issue, and if there is none, what can I do to achieve what i want?
Your problem is, that animation doesn't stop. Add FillBehavior="Stop" to the animation and you will get what you are after.
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding Open}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1" Storyboard.TargetProperty="Width" From="0" To="300" FillBehavior="Stop"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding Open}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1" Storyboard.TargetProperty="Width" From="300" To="0" FillBehavior="Stop"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>

Different WPF Storyboard Animation in each ListBox.ItemTemplate>DataTemplate

I want to have different animations for each of a ListView item.
<Window.Resources>
<Storyboard x:Key="myAnimation" RepeatBehavior="Forever">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Source" Duration="0:0:2">
<DiscreteObjectKeyFrame KeyTime="0:0:0">
<DiscreteObjectKeyFrame.Value>
<BitmapImage UriSource="/img/image1.png"/>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:1">
<DiscreteObjectKeyFrame.Value>
<BitmapImage UriSource="/img/image2.png"/>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<ListBox Name="SessionList" HorizontalContentAlignment="Stretch" >
<ListBox.ItemTemplate>
<DataTemplate>
<Image x:Name="stateimage">
<Image.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard Storyboard="{StaticResource myAnimation}">
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The above XAML code works fine for showing the same animation myAnimation for each listitem. But how I can achive it to show different animations (i.e. I have to define several Storyboards in Window.Resources), depending on a Binding Property of the listitem ViewModel?
EDIT:
With the question linked below, I finally got it worked this way. Amazing!
<Window.Resources>
<Storyboard x:Key="animLOAD" RepeatBehavior="Forever">...</Storyboard>
<Storyboard x:Key="animPREPARED" RepeatBehavior="Forever">...</Storyboard>
<Style x:Key="animStyle" TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=st, Mode=OneWay}" Value="LOAD">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource animLOAD}" />
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding Path=st, Mode=OneWay}" Value="PREPARED">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource animPREPARED}" />
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Image Style="{StaticResource animStyle}" />
I'm not sure how accessible your model is for this approach but you should be able to use it to some measure.
You can use a set of DataTriggers, each inheriting a storyboard:
<DataTrigger Binding="{Binding Property}" Value="SomeValue">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource Storyboard}"/>
</DataTrigger.EnterActions>
</DataTrigger>
I would use them within a ControlTemplate:
<ControlTemplate TargetType="Image">
<ControlTemplate.Triggers>
<!-- DataTriggers -->
</ControlTemplate.Triggers>
</ControlTemplate>
EDIT:
This Question has a similar solution, you may not need the ControlTemplate at all.

Animate when another wrap panel is clicked

I have a list view that is being populated with wrap panels through my binding. My code currently will animate the opacity on MouseEnter (opacity up to 1.0) and MouseLeave (opacity down to {Binding PanelOpacity}) just fine. However, when I select a different wrap panel, I need the storyboard for MouseLeave to fire to reduce the opacity.
Snippet:
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel x:Name="panelObject" Orientation="Horizontal" Margin="5,7">
<WrapPanel.InputBindings>
<MouseBinding MouseAction="LeftClick"
Command="{Binding DataContext.GridClickCmd, RelativeSource={RelativeSource AncestorType=Grid}}"
CommandParameter="{Binding Server}"/>
</WrapPanel.InputBindings>
<WrapPanel.Style>
<Style TargetType="{x:Type WrapPanel}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red" />
</Style.Resources>
<Setter Property="Opacity" Value="{Binding PanelOpacity}" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="1.0" />
</Trigger>
</Style.Triggers>
</Style>
</WrapPanel.Style>
<WrapPanel.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard x:Name="FadeIn">
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
To="1.0" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard x:Name="FadeOut">
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
To="{Binding PanelOpacity}" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="SourceUpdated">
<BeginStoryboard>
<Storyboard x:Name="ChangeSelection">
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
To="{Binding PanelOpacity}" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</WrapPanel.Triggers>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
I've tried using a few different Routed Events and none of them seem to fire when a different wrap panel is clicked.
Last thing I tried was this:
<EventTrigger RoutedEvent="SourceUpdated">
<BeginStoryboard>
<Storyboard x:Name="ChangeSelection">
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
To="{Binding PanelOpacity}" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
Edit: I've got this in my C#. It works fine currently, without animation:
_viewModel.MainPanel.ToList().Find(x => x.Server == (string)parameter).PanelOpacity = 1.0;
_viewModel.MainPanel.ToList().FindAll(x => x.Server != (string)parameter).ForEach(x => x.PanelOpacity = 0.5);
Second Edit: Added more tags to show location of code.
Final Edit: full ItemTemplate code added.

How can i use isPressed in Label?

this is part of my code, i already search it on google but no one know what i mean.
<Label HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Width="30" Height="30" Content="" FontFamily="FontAwesome" HorizontalAlignment="Right" VerticalAlignment="Center">
<Label.Style>
<Style TargetType="{x:Type Label}">
<Style.Triggers>
<Trigger Property="Label.IsMouseOver" Value="True">
<Setter Property="Label.Background" Value="Yellow" />
</Trigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
You can try using EventTrigger in this case. If's of course longer but it's the simplest approach using pure standard XAML (not any custom):
<Label HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Width="30" Height="30" Content="Clgt?" FontFamily="FontAwesome"
HorizontalAlignment="Right" VerticalAlignment="Center">
<Label.Style>
<Style TargetType="{x:Type Label}">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="Yellow" Duration="0"
Storyboard.TargetProperty="Background.Color"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeftButtonUp">
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="Transparent" Duration="0"
Storyboard.TargetProperty="Background.Color"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
It's even better to use RemoveStoryboard action when the MouseLeftButtonUp like this:
<Style.Triggers>
<EventTrigger RoutedEvent="MouseLeftButtonDown">
<BeginStoryboard Name="bg">
<Storyboard>
<ColorAnimation To="Yellow" Duration="0"
Storyboard.TargetProperty="Background.Color"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeftButtonUp">
<RemoveStoryboard BeginStoryboardName="bg"/>
</EventTrigger>
</Style.Triggers>

Multiple DataTriggers - Storyboard overriden

I have two DataTriggers binded to a property (Side) but only one storyboard could be started (the one in the last DataTriggers).
Why ?
I feel like the last storyboard override the first one
<Border x:Name="layout" Background="Transparent" BorderBrush="#BAC8CE" BorderThickness="1" CornerRadius="5">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Side}" Value="Up">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
Duration="00:00:01"
From="Transparent"
To="Green"/>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
Duration="00:00:00.5"
From="Green"
To="Transparent"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding Side}" Value="Down">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
Duration="00:00:01"
From="Transparent"
To="Red"/>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
Duration="00:00:00.5"
From="Red"
To="Transparent"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
I would suggest stopping your storyboard before the other one runs:
<Border x:Name="layout" Background="Transparent" BorderBrush="#BAC8CE" BorderThickness="1" CornerRadius="5">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Side}" Value="Up">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="BeginStoryboardTwo" />
<BeginStoryboard x:Name="BeginStoryboardOne">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
Duration="00:00:01"
From="Transparent"
To="Green"/>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
Duration="00:00:00.5"
From="Green"
To="Transparent"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding Side}" Value="Down">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="BeginStoryboardOne" />
<BeginStoryboard x:Name="BeginStoryboardTwo" >
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
Duration="00:00:01"
From="Transparent"
To="Red"/>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
Duration="00:00:00.5"
From="Red"
To="Transparent"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="Hello World!" />
</Border>
try following solution, put your second datatrigger enter action at the first datatrigger exit action
<Border x:Name="layout"
Grid.Row="1"
Background="Transparent"
BorderBrush="#BAC8CE"
BorderThickness="1"
CornerRadius="5">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Side, ElementName=uc, Mode=OneWay}"
Value="Up">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
Duration="00:00:05"
From="Transparent"
To="Green" />
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
Duration="00:00:00.5"
From="Green"
To="Transparent" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
Duration="00:00:05"
From="Transparent"
To="Red" />
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
Duration="00:00:00.5"
From="Red"
To="Transparent" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
hope this helps

Categories

Resources