How to Bind DoubleAnimation Enabled/Disabled - wpf

Please tell me how Can I solve my problem. The situatino is as follow:
I had a list box with image inside it, I would like some of the image to blinking, some not. I had the property "Emergeny" if it is true the image should blink, now the question is how can I bind it. I try to Bind to "Duartion" or "To" propperty but I receives an error. The code below is my image which is blinking.
<Image Height="32" Width="32" Source="{Binding Emergency, Converter={StaticResource boolToPath}}">
<Image.Style>
<Style>
<Style.Triggers>
<EventTrigger RoutedEvent="Image.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Image.Opacity)" BeginTime="0:0:0" Duration="0:0:0.5"
From="1.0" To="0.0" RepeatBehavior="Forever" AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>

Instead of triggering on Event.Loaded, trigger on your property:
<Trigger Property="IsEmergency" Value="True">
<BeginStoryboard .../>
</Trigger>

Related

The ellipse can't rotate in datatrigger

I want to rotate the ellipse when ProgressBar is set to true in DataTrigger. But It isn't work.
I test the ellipse is work OK with Opacity. But it isn't work with RotateTransform. And the ellipse is also work with EventTrigger.
Which is my mistake and how I fix it.
Thanks for reading my issue.
<Ellipse Name="ProgressEllipse" Width="40" Height="40" StrokeThickness="7" Stroke="{StaticResource ProcessEllipseGradient}" Margin="5,0,0,0">
<Ellipse.RenderTransform>
<RotateTransform CenterX="20" CenterY="20" Angle="0"/>
</Ellipse.RenderTransform>
<!--<Ellipse.Triggers>
<EventTrigger RoutedEvent="Ellipse.Loaded" SourceName="ProgressEllipse">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ProgressEllipse"
Storyboard.TargetProperty="(Ellipse.RenderTransform).(RotateTransform.Angle)"
From="0"
To="360"
Duration="0:0:1"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Ellipse.Triggers>-->
<Ellipse.Style>
<Style TargetType="{x:Type Ellipse}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=ProgressBar}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Ellipse.RenderTransform).(RotateTransform.Angle)"
From="0"
To="1"
Duration="0:0:5"
RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
Addition information
Sorry WPF-it. I make my question not clear.
The Ellipse is work OK when It located in form directly. But It not work when located in a Template.
Follow is my code.
https://www.mediafire.com/?7rb8gvthx94rfiy
If I omit "RelativeSource={RelativeSource self}" in the Trigger. It will throw exception(Cannot animate '(0).(1)' on an immutable object instance)
In the sample code. I have three ellipse. The left ellipse is not work when I locate it in the template(I try to make it work, but It don't work). The center ellipse is the same code with the ellipse in template. But It work OK when I locate it directly in the form. And I test ellipse with Opacity property in the template. And It work OK. I don't know which is my mistake.
Can you give me a hand.
Thanks for reading my question
Ellipse.ProgressBar property does not exist in WPF. Your are probably referring some property from the underlying DataContext of Ellipse.
So change Binding="{Binding RelativeSource={RelativeSource Self}, Path=ProgressBar}" to simply Binding="{Binding Path=ProgressBar}"
Also make sure that this ProgressBar property generates property change notifications so that trigger will execute the action ...
Plus you should also have an EndStoryBoard also.
<Ellipse.Style>
<Style TargetType="{x:Type Ellipse}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ProgressBar}"
Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Name="MyStoryboard">
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Ellipse.RenderTransform).(RotateTransform.Angle)"
From="0"
To="1"
Duration="0:0:5"
RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="MyStoryboard"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>

How to write a style for a control that changes width of another control?

Is there any way to write a Style for a control that changes width of another control?
<Style x:Key="SubMenuStyle" TargetType="Label">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="LightCyan"/>
<Style.Triggers>
<EventTrigger RoutedEvent="MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Menu" Storyboard.TargetProperty="Width" To="0" Duration="0:0:.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
This code leads to error:
TargetName property cannot be set on a Style Setter
I know I can write codes below and it works:
<Label Name="Owners" Margin="0,1,0,0" MouseLeftButtonDown="SubMenuClicked" Style="{StaticResource SubMenuStyle}">
<Label.Triggers>
<EventTrigger RoutedEvent="MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Menu" Storyboard.TargetProperty="Width" To="0" Duration="0:0:.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Label.Triggers>
</Label>
But since I use this trigger in multiple labels I want to write it in a style once.
This is my code to define controls:
<Border Name="Menu" Grid.Column="2" Grid.Row="1" Width="0" HorizontalAlignment="Right" BorderBrush="LightBlue" BorderThickness="2" CornerRadius="2" Background="LightCyan">
<StackPanel Name="MenuPanel">
<Button Style="{StaticResource MenuButtonsStyle}">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ListsMenu" Storyboard.TargetProperty="Height" To="86" Duration="0:0:.6"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<StackPanel Name="ListsMenu" Height="0">
<Label Name="Owners" Margin="0,1,0,0" MouseLeftButtonDown="SubMenuClicked" Style="{StaticResource SubMenuStyle}"/>
<Label Name="Contacts" MouseLeftButtonDown="SubMenuClicked" Style="{StaticResource SubMenuStyle}"/>
<Label Name="Groups" MouseLeftButtonDown="SubMenuClicked" Style="{StaticResource SubMenuStyle}"/>
</StackPanel>
</StackPanel>
</Border>
Scenario: There is a border, It's default width is zero, It will raise to 165 in another trigger which works fine, I want to animate it to zero again when labels are clicked, but I can not access the width of that border in lables style
You claim that you use triggers in multiple labels so you can define trigger one time like this
<Storyboard x:Key="animation">
<DoubleAnimation Storyboard.TargetName="ListsMenu" Storyboard.TargetProperty="Height" To="86" Duration="0:0:.6"/>
</Storyboard>
and later when you need you can call it
<EventTrigger RoutedEvent="MouseEnter" >
<BeginStoryboard Storyboard="{StaticResource animation}"/>
</EventTrigger>
But I'm a bit confused what you meant to acquire.
You cannot get access in style since style does not have a namescope, as it was said. If it changes label's width you could do this in style like this
<Style x:Key="style_button" TargetType="Button">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width" To="300" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
but it invokes by StoryBoard.TargetName to another object.
try :
<EventTrigger RoutedEvent="MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.Target="{Binding ElementName=Menu}"
Storyboard.TargetProperty="Width" To="0" Duration="0:0:.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
TargetName is only valid inside Templates.

WPF XAML Grid Visibility Trigger

I have a status message located on the first row of my grid and I want it to slide in and out when the visibility changes.
The first visibility trigger works great and slides the first grid row open quickly. As soon as I add the 'Collapsed' trigger, nothing works at all. How do I reverse the animation to slide closed when the visibility is set to collapsed?
<Grid Grid.Row="0" Height="55" Visibility="{Binding StatusMessageVisibility, Mode=TwoWay}">
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" From="0" To="55" Duration="0:0:.1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
<Trigger Property="Visibility" Value="Collapsed">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" From="55" To="0" Duration="0:0:.1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Text="Hi There" />
</Grid>
You should remove the Visibility binding in your grid and use a DataTrigger that binds to the StatusMessageVisibility property. If you bind the grid's visibility then once it's collapsed it's collapsed and you won't be able to see the animation.
Also, instead of having two data triggers with EnterActions, use a single data trigger that also has an ExitAction for the collapsed state:
<Grid Grid.Row="0" Height="55">
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding StatusMessageVisibility}" Value="Visible">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" From="0" To="55" Duration="0:0:.1" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" From="55" To="0" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Text="Hi There" />
</Grid>

Don't trigger WPF animation if bound value is DateTime.MinValue

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>

WPF: Is there a way to change conditionally a property value in XAML or code behind (VB/C#) is needed?

I started a new little WPF application which has something like an entry form that is placed over a picture. That picture has the placeholders for the data that should be entered and where TextBoxes are placed so the user is actually entering the data into a form as she would do by hand or in a typewriter.
I wanted to add an effect, partiali hiding the textbox when the user leaves the field so placed some event handlers in one of the fields adding this code:
<TextBox Height="23" VerticalAlignment="Top" Width="237"
HorizontalAlignment="Left" Margin="641,25,0,0"
Name="txtAmount" LostFocus="txtAmount_LostFocus" />
private void txtAmount_LostFocus(object sender, RoutedEventArgs e)
{
Opacity = .5;
}
That worked so I continued adding code to set the same behavior in all textBoxes, so I changed the code above with the code below:
<Window.Resources>
<Storyboard x:Key="FadeOut">
<DoubleAnimation From="1.0" To="0.5"
Duration="0:0:0.2"
Storyboard.TargetProperty="Opacity">
</DoubleAnimation>
</Storyboard>
<Storyboard x:Key="FadeIn">
<DoubleAnimation From="0.5" To="1.0"
Duration="0:0:0.2"
Storyboard.TargetProperty="Opacity"/>
</Storyboard>
<Style x:Key="InTheShadow" >
<Style.Setters>
<Setter Property="TextBox.Opacity" Value="0.5" />
</Style.Setters>
</Style.Setters>
<Style.Triggers>
<EventTrigger RoutedEvent="TextBox.GotFocus">
<BeginStoryboard Storyboard="{StaticResource FadeIn}"/>
</EventTrigger>
<EventTrigger RoutedEvent="TextBox.LostFocus">
<BeginStoryboard Storyboard="{StaticResource FadeOut}"/>
</EventTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
The first code, also had GotFocus event handled but it's almost the same code, so the second code implemented that, and that was placed on all textBoxes setting the style.
Now, this worked too but, I wanted to add the same effect for MouseEnter/MouseLeave
<EventTrigger RoutedEvent="TextBox.MouseEnter">
<BeginStoryboard Storyboard="{StaticResource FadeIn}"/>
</EventTrigger>
<EventTrigger RoutedEvent="TextBox.MouseLeave">
<BeginStoryboard Storyboard="{StaticResource FadeOut}"/>
</EventTrigger>
but, here my question comes, those events obviously changed the Opacity always, and that was not my intention. I didn't want to change the Opacity for the TextBox that had the Focus, I wanted to keep that Opacity in 100%.
MultiTriggers to the rescue!
Care also had to be taken to stop the old storyboards. Hopefully this works as you expected:
<Style x:Key="InTheShadow" TargetType="TextBox">
<Setter Property="TextBox.Opacity" Value="0.5" />
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsFocused" Value="False" />
<Condition Property="IsMouseOver" Value="True" />
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="fadeOut1" />
<StopStoryboard BeginStoryboardName="fadeOut2" />
<BeginStoryboard x:Name="fadeIn1" Storyboard="{StaticResource FadeIn}"/>
</MultiTrigger.EnterActions>
<MultiTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="fadeIn1" />
<StopStoryboard BeginStoryboardName="fadeIn2" />
<BeginStoryboard x:Name="fadeOut1" Storyboard="{StaticResource FadeOut}"/>
</MultiTrigger.ExitActions>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsFocused" Value="True" />
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="fadeOut1" />
<StopStoryboard BeginStoryboardName="fadeOut2" />
<BeginStoryboard x:Name="fadeIn2" Storyboard="{StaticResource FadeIn}"/>
</MultiTrigger.EnterActions>
<MultiTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="fadeIn1" />
<StopStoryboard BeginStoryboardName="fadeIn2" />
<BeginStoryboard x:Name="fadeOut2" Storyboard="{StaticResource FadeOut}"/>
</MultiTrigger.ExitActions>
</MultiTrigger>
</Style.Triggers>
</Style>
Usage:
<StackPanel Margin="7">
<TextBox Margin="7" Style="{StaticResource InTheShadow}" />
<TextBox Margin="7" Style="{StaticResource InTheShadow}" />
<TextBox Margin="7" Style="{StaticResource InTheShadow}" />
<TextBox Margin="7" Style="{StaticResource InTheShadow}" />
<TextBox Margin="7" Style="{StaticResource InTheShadow}" />
</StackPanel>
Edit: Also, you should remove the "from" in your storyboards, that way if the text box already had mouse over and you focus, it won't re-animate:
<Storyboard x:Key="FadeOut">
<DoubleAnimation
To="0.5"
Duration="0:0:0.2"
Storyboard.TargetProperty="Opacity" FillBehavior="HoldEnd"
/>
</Storyboard>
<Storyboard x:Key="FadeIn">
<DoubleAnimation
To="1.0"
Duration="0:0:0.2"
Storyboard.TargetProperty="Opacity" FillBehavior="HoldEnd"
/>
</Storyboard>

Resources