how to refresh wpf data trigger ? - c#

i have the grid with storyboard as below.
<Grid x:Name="grd_Order" Grid.Column="2" Height="16" Margin="0,-2,0,0" Visibility="Collapsed" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.Resources>
<Storyboard x:Key="stry_OrderMsgShowHide" RepeatBehavior="3x">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Visibility)" >
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
<DiscreteObjectKeyFrame KeyTime="0:0:2" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
<Grid.Style>
<Style >
<Style.Triggers>
<DataTrigger Value="True" Binding="{Binding Path=BlinkOrderAlert,Mode=TwoWay}">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="stry_BlinkOrdAlert" Storyboard="{StaticResource stry_OrderMsgShowHide}"/>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
and in my ViewModel.cs,
private bool blinkOrderAlert;
public bool BlinkOrderAlert
{
get
{
return blinkOrderAlert;
}
set
{
if (blinkOrderAlert == value)
return;
this.blinkOrderAlert = value;
this.RaisePropertyChanged(this, new PropertyChangedEventArgs("BlinkOrderAlert"));
}
}
public void BlinkOrdAlert()
{
this.BlinkOrderAlert=false;
this.BlinkOrderAlert = true;
}
public ViewModel()
{
this.BlinkOrderAlert=true;
}
and it only works first time when constructor is initialized. Whenever i call the BlinkOrdAlert function, it's not working anymore. How can i modify the datatrigger to run storyboard everytime i call the function? Thanks.

Consider adding the following data trigger to you style.
The Following data trigger will remove the story board when the BlinkOrderAlert value set to false and when the value was true it will add story board.
hope this will help.
<DataTrigger Value="False" Binding="{Binding Path=BlinkOrderAlert,Mode=TwoWay}">
<DataTrigger.EnterActions>
<RemoveStoryboard BeginStoryboardName="stry_BlinkOrdAlert"></RemoveStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>

You should not really use a DataTrigger for this at all, you try use a property like an event which is quite a hack. Unfortunately the native triggers are, let's say not optimal, so you cannot use an EventTrigger as it only supports RoutedEvents.
But you might be able to use ViewModel-events using the EventTrigger from Blend's Interactivity (Blend 3 SDK) instead, so that might be worth a try.

Related

WPF DataTrigger Animation only fires once

I have an MVVM pattern application where I want the users to be able to enter dates, but also apply some validation on those dates. I do this by checking whatever they enter and overwriting it with the nearest valid date, if their entry is not valid. In order to let the user know that their date has been overwritten, I would have tried to animate the foreground of the date picker textbox, but I find that the animation is only visible on the first time their date is "corrected" in this way.
In the MainViewModel, I have a Ping property that notifies the UI each time it is set to "true" and a validation method that sets Ping = true each time it has to overwrite a date:
public bool Ping
{
get => _ping;
set
{
if (value && !_ping)
{
_ping = value;
OnPropertyChanged();
_ping = false;
}
}
}
private DateTime _from;
//Bound to the Date input field in the UI
public DateTime From
{
get { return _from; }
set
{
if (_from != value)
{
_from = giveValidDate("From", value);
OnPropertyChanged();
}
}
}
private DateTime giveValidDate(string posn, DateTime givenDate)
{
DateTime validDate = new DateTime();
// [...A Load of validation that results in a valid Date output...] //
Ping = givenDate != validDate;
return validDate;
}
There is a TextBox style that I am using that has the animation on it:
<Style x:Key="PingableTextBox" TargetType="TextBox">
<Setter Property="TextBlock.FontSize" Value="18"/>
<Setter Property="TextElement.FontSize" Value="18"/>
<Setter Property="TextElement.Foreground" Value="{StaticResource Text_LightBrush}"/>
<Setter Property="TextElement.FontWeight" Value="Normal"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border BorderThickness="{TemplateBinding Border.BorderThickness}"
CornerRadius="2"
BorderBrush="{StaticResource Highlight_LightBrush}"
Background="{StaticResource Empty_DarkBrush}"
x:Name="border"
SnapsToDevicePixels="True">
<ScrollViewer HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"
Name="PART_ContentHost" Focusable="False" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsMouseOver" Value="True">
<Setter Property="Border.BorderBrush" TargetName="border" Value="{StaticResource Good_MidBrush}"/>
<Setter Property="Cursor" Value="IBeam"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Ping}" Value="true">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="Pinger"/>
<BeginStoryboard Name="Pinger">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Foreground.Color"
From="{StaticResource Bad_Bright}" To="{StaticResource Text_Light}" FillBehavior="Stop"
Duration="0:0:0:1.0"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="Pinger"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
However, when I run the application, the trigger is only seen to act once (the brief red flash when an invalid date is selected):
I have seen many other questions on Stack Overflow about the same issue, but the solution has always been to add the line <StopStoryboard BeginStoryboardName="Pinger"/> in the Enter Actions, to add the line <RemoveStoryboard BeginStoryboardName="Pinger"/> to the Exit Actions or to add FillBehavior="Stop" to the storyboard. I have tried every combination of each of these in all places I could think of and the problem still persists.
Is there some other explanation for the problem that I could have missed that would fix it for me, or something that I have failed to implement correctly.. In short, why is it only firing once?
PS - some of the questions I have used to implement the code you see above:
WPF Storyboard only fires once
WPF Fade In / Out only runs once
WPF MultiDataTrigger on Tag property only firing once
WPF - problem with DataTrigger which work only once
You must raise the PropertyChanged after you reset the Ping property in order to trigger the Trigger.ExitAction.
Setting the backing field _ping does not propagate any change notification to the view.
This means your problem is not the problem that your quoted answer tries to solve.
You should also not define the Trigger.ExitAction in your scenario. Since you have configured the animation to stop automatically once the timeline has completed (FillBehavior="Stop"), you don't need to do anything to stop it. Note that RemoveStoryboard does not do you any favor in your case. It would only complicate the logic to reset the property since RemoveStoryboard would kill the animation immediately on the instant property toggle. This means, avoiding the Trigger.ExitAction or to be more precise the RemoveStoryboard allows you to toggle the property instantly:
// Trigger the animation
Ping = givenDate != validDate;
// Reset the property immediately to reset the animation trigger.
// Because the `Trigger.ExitAction` is deleted,
// the currently running animation will complete the timeline.
Ping = false;
If you want to implement the logic more gracefully you can toggle the Ping property and define the Trigger.EnterAction for the true state and the Trigger.ExitAction for the false state (thus converting each state into a validation error signal):
public bool Ping
{
get => _ping;
set
{
if (value && !_ping)
{
_ping = value;
OnPropertyChanged();
}
}
}
private DateTime giveValidDate(string posn, DateTime givenDate)
{
DateTime validDate = new DateTime();
// [...A Load of validation that results in a valid Date output...] //
// Trigger the animation by toggling the property.
if (givenDate != validDate)
{
Ping ^= true;
}
return validDate;
}
<DataTrigger Binding="{Binding Ping}" Value="true">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Foreground.Color"
From="{StaticResource Bad_Bright}" To="{StaticResource Text_Light}" FillBehavior="Stop"
Duration="0:0:0:1.0"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Foreground.Color"
From="{StaticResource Bad_Bright}" To="{StaticResource Text_Light}" FillBehavior="Stop"
Duration="0:0:0:1.0"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>

WPF StopStoryboard - after animation stops it does not Bind to the Color property [duplicate]

This question already has an answer here:
: 'Cannot animate 'Fill.Color' on an immutable object instance.'
(1 answer)
Closed 2 years ago.
I am new to WPF, I Have a rectangle that I try to animate to flesh red color if some boolean is true. and I want it to stop when the boolean is false. In order to do that, I used the <DataTrigger.ExitActions>
However, I still want my Fill color to change according to the AlertColor, but after the animation is stopped it seems like the binding is stoped also, and the background-color stays LightPink only.
why? how can I fix this and is there a better approach to animate the color only in case of a specific color and stop the animation when the color changes (with Binding)?
The XAML relevant code:
<Rectangle Width="840" Height="40">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Fill">
<Setter.Value>
<SolidColorBrush Color="{Binding AlertUnit.AlertColor , FallbackValue=LightPink}"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding AlertUnit.Emergency}" Value="true" >
<DataTrigger.EnterActions>
<BeginStoryboard Name="FlashingRedAnimation">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Fill.Color" To="White" Duration="0:0:1" AutoReverse="True"
RepeatBehavior="Forever"></ColorAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="FlashingRedAnimation" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
edit:
I found the problem it was a really silly one. the binding should be:
Binding AlertUnit.AlertColor.Color
not:
Binding AlertUnit.AlertColor
Thank you all for the help.
I produced a similar visual effect by animating the opacitity value of a brush rather than the colour.
<Style TargetType="{x:Type vctrl:perBlinkingBorder}">
<Style.Triggers>
<Trigger Property="IsBlinking" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetProperty="(BlinkingBorderBrush).Opacity">
<DoubleAnimation
AutoReverse="True"
RepeatBehavior="Forever"
From="1"
To="0"
Duration="0:0:0.5">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
To ensure that the visual effect is removed, I have two brush properties on the perBlinkingBorder control, and switch between them when the IsBlinking property is set.
More details and a demo project on my recent blog post.
I found the problem it was a really silly one.
the binding should be:
Binding AlertUnit.AlertColor.Color
not:
Binding AlertUnit.AlertColor
Thank you all for the help.

WPF grid and togglebutton mutual binding

I have a grid in my application and I need that grid to work like a pop up while checking a togglebutton it should appear and while unchecking it should disappear and for that I wrote a code like this.
<Grid x:Name="popup" Visibility="{Binding IsChecked,ElementName=button,Converter={StaticResource BooleanToVisibility}}" >
<Grid.Resources><Storyboard x:Key="ResetButton1">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(ToggleButton.IsChecked)"
Storyboard.TargetName="button">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<System:Boolean>False</System:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard></Grid.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="TouchDown">
<ei:ControlStoryboardAction Storyboard="{StaticResource ResetButton1}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock >this is a popup</TextBlock> </Grid>
<ToggleButton x:Name="button"></ToggleButton>
My problem occurs when I uncheck the togglebutton after I checked it. The pop up stays there with an animation.It works fine when i click out of the togglebutton.How I can Handle it?
Ok, so the way you have it currently doesn't make much sense amigo. Subscribing to the TouchDown event to fire off a storyboard that just unchecks the ToggleButton is a cyclic loop of negating itself.
Instead, just throw a couple .Triggers in there to toggle the visibility of the Grid and get rid of that Storyboard and TouchDown EventTrigger.
So just throw something like this in as triggers.
<DataTrigger Binding="{Binding IsChecked, ElementName=button}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsChecked, ElementName=button}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
Or you could also use ChangePropertyAction from the Blend SDK for wpf.
Hope this helps.

Using dynamic parameters for binding ElementName in styles. Reusing styles

I have a question.
I have created a style in WPF designer (XML) for TextBlocks. After IsMouseOver event is fired on any of two image controls, my textBlocks changes its positions. This style is used for some textBlocks.
<Style x:Key="movingTextBlocksStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=image1, Path=IsMouseOver}" Value="True">
<Setter Property="RenderTransform">
<Setter.Value>
<TranslateTransform/>
</Setter.Value>
</Setter>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)" From="0" To="-125"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=image2, Path=IsMouseOver}" Value="True">
<Setter Property="RenderTransform">
<Setter.Value>
<TranslateTransform/>
</Setter.Value>
</Setter>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)" From="0" To="-125"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
This style will be used as:
<TextBlock x:Name="textBlock1" Style="{StaticResource movingTextBlocksStyle}"/>
<TextBlock x:Name="textBlock2" Style="{StaticResource movingTextBlocksStyle}"/>
My problem is that I want to reuse this style for another textBlocks: textBlock3 and textBlock4 where "Binding ElementName" differs, for example image3 and image4.
I have thought that if there was any possibility to reuse this style with some type of dynamic parameter or argument, it would be great.
I searched for any solution only as xml-code without any C# (I am using C# with WPF) or converter implementation.
Thanks in advance.
Here's pure XAML solution:
Collect all controls on which the text block depends in an array and set it as data context:
<Image x:Name="image1"/>
<Image x:Name="image2"/>
<TextBlock Style="{StaticResource movingTextBlocksStyle}">
<TextBlock.DataContext>
<x:Array Type="system:Object">
<x:Reference>image1</x:Reference>
<x:Reference>image2</x:Reference>
</x:Array>
</TextBlock.DataContext>
</TextBlock>
You'll need to remember to change all bindings on the TextBlock properties accordingly, because data context is no longer inherited. Then in the style definition bind using corresponding array indices:
<DataTrigger Binding="{Binding [0].IsMouseOver}" Value="True">...</DataTrigger>
...
<DataTrigger Binding="{Binding [1].IsMouseOver}" Value="True">...</DataTrigger>
The system: prefix namespace is clr-namespace:System;assembly=mscorlib.
You could define attached properties which would hold the controls on which the TextBox depends, like so (I'll use only one property to keep it brief):
static class Helper
{
public static object GetImage(DependencyObject obj)
{
return (object)obj.GetValue(ImageProperty);
}
public static void SetImage(DependencyObject obj, object value)
{
obj.SetValue(ImageProperty, value);
}
public static readonly DependencyProperty ImageProperty =
DependencyProperty.RegisterAttached(
"Image",
typeof(object),
typeof(Helper),
new PropertyMetadata(null));
}
Then, in the data trigger, bind using the attached property:
...
<DataTrigger Binding="{Binding Path=(local:Helper.Image).IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True">
...
Lastly, set the property value to be the control on which the TextBlock depends:
<Image x:Name="image1"/>
...
<TextBlock local:Helper.Image="{Binding ElementName=image1}"
Style="{StaticResource movingTextBlocksStyle}"/>
This solution is not pure XAML - it requires a little bit of code-behind to define the attached properties, but I doubt there's an elegant way to completely avoid it.

WPF Fade Out on a control

In my WPF app, I have a feedback control that I want to appear after a user action completes (save data, delete...). The visibility is set to Hidden to begin and style set to the animateFadeOut style defined as a resource (see below). Then I want to set the text and control Visibility to visible in my C# code and have the feedback control display the message and fade out after 5 seconds and remain hidden (Visibility.Hidden).
The following XAML works the first time I call control.Visiblity= Visibility.Visible but the control doesn't reappear the second time. I figure that is because the animation is still running, which has control over the feedback control. I then tried to set FillBehavior to "Stop" but that just made the control visible again and I want it hidden. Then, with FillBehavior="Stop", I tried to set a trigger "when Opacity = 0, set the Visibility to Hidden". The trigger didn't seem to fire and I was left with the visible control once more after the animation completed.
Please help point out what I am doing wrong here.
Alternatively, if you can suggest a better way to display a control that fades after 5 seconds and can be called over and over, I would appreciate it.
Thanks!
<Style TargetType="{x:Type FrameworkElement}" x:Key="animateFadeOut">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard >
<Storyboard>
<DoubleAnimation BeginTime="0:0:5.0" Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
The problem is that after your animation completes your control still has Visibility=Visible, so it cannot be entered again.
I would rather use animation that does the whole thing, first shows the control, then hides it.
<Storyboard x:Key="animate">
<ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation BeginTime="0:0:0.0" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.2"/>
<DoubleAnimation BeginTime="0:0:5.0" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.5"/>
<ObjectAnimationUsingKeyFrames BeginTime="0:0:5.5" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Hidden</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
And use it as follows:
((Storyboard)FindResource("animate")).Begin(someControl);
Liz is correct about Visibility still being Visible. alpha-mouse is also correct that you need to set it back to Hidden at some point. But it won't work if you set it back before the animation is completed like this:
MyControl.Visibility = System.Windows.Visibility.Visible;
MyControl.Visibility = System.Windows.Visibility.Hidden;
because animations take higher precedence (MSDN)
You can set it back to Hidden in Storyboard.Completed event:
private void Show()
{
MyControl.Visibility = System.Windows.Visibility.Visible;
var a = new DoubleAnimation
{
From = 1.0,
To = 0.0,
FillBehavior= FillBehavior.Stop,
BeginTime = TimeSpan.FromSeconds(2),
Duration = new Duration(TimeSpan.FromSeconds(0.5))
};
var storyboard = new Storyboard();
storyboard.Children.Add(a);
Storyboard.SetTarget(a, MyControl);
Storyboard.SetTargetProperty(a, new PropertyPath(OpacityProperty));
storyboard.Completed += delegate { MyControl.Visibility = System.Windows.Visibility.Hidden; };
storyboard.Begin();
}
Here is my work around. This fades a control in and back out again. Instead of playing around with the Visibility, I handled it by playing only with the Opacity.
Thanks to Kane from this post for the orginal code: Fade any control using a WPF animation
Storyboard storyboard = new Storyboard();
TimeSpan duration = TimeSpan.FromMilliseconds(500); //
DoubleAnimation fadeInAnimation = new DoubleAnimation()
{ From = 0.0, To = 1.0, Duration = new Duration(duration) };
DoubleAnimation fadeOutAnimation = new DoubleAnimation()
{ From = 1.0, To = 0.0, Duration = new Duration(duration) };
fadeOutAnimation.BeginTime = TimeSpan.FromSeconds(5);
Storyboard.SetTargetName(fadeInAnimation, element.Name);
Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath("Opacity", 1));
storyboard.Children.Add(fadeInAnimation);
storyboard.Begin(element);
Storyboard.SetTargetName(fadeOutAnimation, element.Name);
Storyboard.SetTargetProperty(fadeOutAnimation, new PropertyPath("Opacity", 0));
storyboard.Children.Add(fadeOutAnimation);
storyboard.Begin(element);
My God that took forever. Take a look at this, it solves that problem of animating upon Visibility changes to 'Visible' and 'Hidden' using alpha and the animation will not freeze.
using System.Windows;
namespace WpfApplication4
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
button.Visibility = Visibility.Hidden;
}
private void button2_Click(object sender, RoutedEventArgs e)
{
button.Visibility = Visibility.Visible;
}
}
}
XAML:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication4"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<Style BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}">
<Style.Resources>
<Storyboard x:Key="FadeOut">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" FillBehavior="Stop">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Visibility.Visible}"/>
<DiscreteObjectKeyFrame KeyTime="0:0:1" Value="{x:Static Visibility.Hidden}"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:1" AutoReverse="False" />
</Storyboard>
<Storyboard x:Key="FadeIn">
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:1" AutoReverse="False" />
</Storyboard>
</Style.Resources>
<Setter Property="Width" Value="120"></Setter>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Visibility" Value="Hidden" />
<Condition Property="Opacity" Value="1" />
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="FadeInStoryboard" />
<BeginStoryboard Name="FadeOutStoryboard" Storyboard="{StaticResource FadeOut}" />
</MultiTrigger.EnterActions>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Visibility" Value="Visible" />
<Condition Property="Opacity" Value="0" />
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="FadeOutStoryboard" />
<BeginStoryboard Name="FadeInStoryboard" Storyboard="{StaticResource FadeIn}" />
</MultiTrigger.EnterActions>
</MultiTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="200,186,0,0" VerticalAlignment="Top" Width="75" Height="38" />
<Button x:Name="button1" Content="Hide it" HorizontalAlignment="Left" Margin="112,96,0,0" VerticalAlignment="Top" Width="75" Click="button1_Click"/>
<Button x:Name="button2" Content="Show it" HorizontalAlignment="Left" Margin="200,96,0,0" VerticalAlignment="Top" Width="75" Click="button2_Click"/>
<Label x:Name="label" Content="{Binding ElementName=button, Path=Opacity}" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
<Label x:Name="label1" Content="{Binding ElementName=button, Path=Visibility}" HorizontalAlignment="Left" Margin="10,36,0,0" VerticalAlignment="Top"/>
</Grid>
</Window>
All the answers above use at least some sort of XAML code, which I personally am not that great of a fan of (because it's very confusing), so I found a way to do the same with some simple C# code:
int secs = 2; // How long the fade should take in seconds
for (int i = 99; i >= 0; i--)
{
someControl.Opacity = i / 100d;
await Task.Delay(secs * 10);
}
someControl.Visibility = Visibility.Hidden;
someControl.Opacity = 1;
You can use this on any Control. You also need to add the async modifier in your method signature. Without the await operator your UI couldn't respond while it's fading out the control.
After the control faded out, you can make it visible again like this:
someControl.Visibility = Visibility.Visible;
This method may not be the "best", but it is certainly the simplest and easiest to understand.
This should fix your storyboard.
However, remember that once the animation is complete, your control is completely opaque - invisible, but your Visibility property is still set to Visible. So you'll have to make sure that the Visibility property is reset to hidden or collapsed somewhere too.
<Style TargetType="{x:Type FrameworkElement}" x:Key="animateFadeOut">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard Name="MyFadeEffect">
<Storyboard>
<DoubleAnimation BeginTime="0:0:5.0" Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<StopStoryboard BeginStoryboardName="MyFadeEffect"/>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>

Categories

Resources