Fading out a WPF control on Visibility.Hidden - c#

I'd like to use style to make a WPF control (in this case a 3rd party one from Telerik, but could be anything) fade out as it's Visibility property is changed to Hidden. This approach I'm trying to use works well for fading in content after it's set to Visibility.Visible. But this does NOT work the opposite way:
<Style x:Key="FadeoutStyle" TargetType="telerik:RadDiagramShape">
<Style.Triggers>
<Trigger Property="Visibility" Value="Hidden">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
I'm pretty sure of why this doesn't work: as soon as the control is set to Hidden, WPF stops drawing it. So my animation might even be playing, but it's not showing because Visibility.Hidden immediately got applied.
Question 1: Is my assumption correct? Question 2: What is a good pattern to work around this? I can imagine a solution where a custom property initiates the fadeout anim and uses a callback once done that applies the actual Visibility.Hidden setting. Is there a cleaner solution?

Visibility.Hidden issues aside, ultimately it turned out that I was using the trigger incorrectly. My working version (using a different property) looks like this:
<Trigger Property="IsEnabled" Value="False">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>

Related

Trigger Visibility change before it is successfully applied

I have the style for one of my user controls:
<Style x:Key="AddServerPanel" TargetType="{x:Type uc:AddServer}">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Margin" Duration="0:0:0.2" From="252,550,26,0" To="252,248,26,0" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
<Trigger Property="Visibility" Value="Hidden">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Margin" Duration="0:0:0.2" From="252,248,26,0" To="252,550,26,0" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
I have a user control hidden in the background appearing when I click on the button. I want to have a slide in and slide out animation. This code already works for sliding in but sliding out doesn't works properly as the control just disappears. I think this is because the visibility property is applied to the control and afterwards the trigger is called. How could I fix this behaviour to have a proper "slide out" effect on the control?
Edit
I changed my code and switched over to a DataTrigger.
However this is working but I just see the animation when the Binding property value is false. This is really curious.
Also it would be nice to know how the set the "From" property in the ThicknessAnimation to the current margin of the control.
<uc:AddServer x:Name="AddServerPanel" Height="300" Width="570" Margin="252,550,26,0">
<uc:AddServer.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=AddServerPanelVisible}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Margin" Duration="0:0:2" From="252,550,26,0" To="252,248,26,0" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding Path=AddServerPanelVisible}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="Margin" Duration="0:0:2" From="252,248,26,0" To="252,550,26,0"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</uc:AddServer.Style>
</uc:AddServer>
If you are using animations to show/hide the control, chances are you are manipulating the opacity of said control. If that's the case, you can begin your Storyboard in code, and, when completed, set visibility flag to true. This will run animation first, then set the visibility.
HOWEVER. I should point out that because you are already hiding the control via opacity, there's no real need to change it's visibility at that point.
Another option would be to set a visibility flag (that doesn't bind to the control's visibility property), and when flag value changes, start appropriate Storyboard (e.g., if flag is true, show control, or begin show animation).
I also wanted to explain why what you currently have does what it does.
Whenever you set a trigger, you are telling the control to do something when a property equals such and such value. What happens is the control is first hiding, and when visibility is hidden, only then will it start the hiding animation. Only problem is...it's already hidden! That's where my solution comes in ;)
Edit
Realized your animating margin, not opacity. In either case, the control is always hidden before beginning the animation, which is the problem.
Solution
Here's an example:
<DataTrigger Binding="{Binding IsVisible}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<!-- Fade in animation -->
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding IsVisible}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<!-- Fade out animation -->
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
IsVisible is a flag you'd have to define. The rest is pretty straightforward.

Activate Storyboard on Visibility Changed

Currently I have an Image that I pulse when it is loaded. I want to rather activate the Storyboard when I change the Visibility of the Image. But I see that Image.Triggers have to be EventTriggers.
Which event do I need to hook into?
<Image.Triggers>
<EventTrigger RoutedEvent="Image.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="MandateErrorImage"
Storyboard.TargetProperty="Opacity"
From="1.0" To="0.1" Duration="0:0:0.5"
AutoReverse="True" RepeatBehavior="0:0:2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
In WPF there is an event UIElement.IsVisibleChanged but is a CLR event, not a routed event, therefore in EventTrigger can not be used.
In this case use IsVisible property in DataTrigger like this:
<Window.Resources>
<Style x:Key="AnimationImageStyle" TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=IsVisible}"
Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1.0" To="0.1"
Duration="0:0:0.5"
AutoReverse="True"
RepeatBehavior="0:0:2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Image Name="TestImage"
Style="{StaticResource AnimationImageStyle}"
Source="Enter.jpg"
Width="400"
Height="400" />
</Grid>
If you want to animate on a DependencyProperty or bound value you will need to create a Style for your image and put the animation in the style.
The following Style will perform your animation when the Visibility of your image is set to Visible:
<Style x:Key="FlashingImageStyle" TargetType="{x:Type Image}">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
From="1.0" To="0.1" Duration="0:0:0.5"
AutoReverse="True" RepeatBehavior="0:0:2" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>

Polygon Animation won't work in c#

I am having a small Problem in C#. I want to have a Polygon I drew in WPF to be animated on hover.
I successfully drew the polygon like this:
<Polygon Fill="#FF767676" Points="0,8,12,0,12,16" Margin="30" Style="{DynamicResource BackAndForth}"/>
I then defined a style
<Style x:Key="BackAndForth" TargetType="{x:Type Polygon}">
<Setter Property="Opacity" Value="0.6" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Opacity" From="0.6" To="1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Opacity" From="1" To="0.6" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
Somehow this won't work. The setter property in line 2 works. But somehow the animation won't get triggered. I already looked it up but polygons have a IsMouseOver Property.
The exact same code works perfecty on buttons (with a proper target type).
Any ideas what I am missing?

How to animate multiple Path elements in WPF

I have a collection of glyphs that are in form of WPF <Path> elements.
Something like this:
<Grid>
<Path Data="..." Height="33.333" Stretch="Fill" Width="33.334"/>
<Path Data="..." Height="33.333" Stretch="Fill" Width="33.334"/>
<Path Data="..." Height="33.333" Stretch="Fill" Width="33.334"/>
<Path Data="..." Height="33.333" Stretch="Fill" Width="33.334"/>
</Grid>
These are just image or icon glyphs. I'm trying to find a way to switch or flip between these various Path elements so that it looks like an animation. I'm pretty new to WPF and have tried looking for examples but couldn't find anything similar here or elsewhere on the web. The closest example I found was erasing one <Image> element with another using Storyboard and DoubleAnimation but I can't figure out how to apply it to <Path>.
I'm basically trying to find a way to show one path element and hide all other paths, wait a second, show next path element and hide all other, and so on, to make it look like a flip animation.
I would appreciate if someone can post a simple example or point me in the right direction. Thanks.
Alright give this a try. Note that each Path will get its own Storyboard. So if you have 4 Paths, you get 4 Storyboards.
<Path>
<Path.Style>
<Style TargetType="{x:Type Path}">
<Setter Property="Opacity" Value="0"/>
<Style.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="0"
To="1"
Duration="00:00:1"
BeginTime="00:00:1"
AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
<Path>
<Path.Style>
<Style TargetType="{x:Type Path}">
<Setter Property="Opacity" Value="0"/>
<Style.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="0" To="1"
Duration="00:00:1"
BeginTime="00:00:2"
AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
<Path>
<Path.Style>
<Style TargetType="{x:Type Path}">
<Setter Property="Opacity" Value="0"/>
<Style.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="0" To="1"
Duration="00:00:1"
BeginTime="00:00:3"
AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
<Path>
<Path.Style>
<Style TargetType="{x:Type Path}">
<Setter Property="Opacity" Value="0"/>
<Style.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="0"
To="1"
Duration="00:00:1"
BeginTime="00:00:4"
AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
Notice how we're using the FrameworkElement.Loaded event to trigger the animation. You can use this event on virtually any UI element. Each storyboard changes the opacity from 0 (invisible) to 1 (completely visible) in 1 second (you can change this using the Duration property). Also, the BeginTime property is different for each storyboard, this is required to make sure the items are animated one after another. Finally, we set the AutoReverse property to make sure the Paths are disappeared (i.e. the animation is reversed). This should give you the idea.

WPF Animation Triggers Regardless of Property Value

I am trying to make a smooth fade in/out animation for an inline alert box in a WPF form. The problem is that the animation seems to run even when the property is not set to the value expected in the trigger. I've put a breakpoint in the converter method and it never returns anything except Visibility.Hidden, however the trigger on Visibility.Visible is firing the animation of the Height property (may or may not be firing the one on opacity too but I couldn't tell since the Grid is Visibility.Hidden).
Here is the style:
<Style TargetType="Grid" x:Key="AlertStyle">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
Duration="00:00:02"
BeginTime="00:00:00"
To="1.0" />
<DoubleAnimation Storyboard.TargetProperty="Height"
Duration="00:00:01"
BeginTime="00:00:00"
To="60.0"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
The Grid in question is here:
<Grid DockPanel.Dock="Top"
Visibility="{Binding Path=Message, Converter={StaticResource NullToVis}}"
Style="{StaticResource AlertStyle}">
<ContentPresenter Content="{Binding Path=Message, Mode=OneWay}"/>
</Grid>
The Value Converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value == null) ? Visibility.Hidden : Visibility.Visible;
}
When I add a second trigger to the <Style.Triggers> element neither animation ever runs.
The second trigger:
<Trigger Property="Visibility" Value="Hidden">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
Duration="00:00:01"
BeginTime="00:00:00"
To="0.0" />
<DoubleAnimation Storyboard.TargetProperty="Height"
Duration="00:00:02"
BeginTime="00:00:00"
To="0.0"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
I've never tried to do anything related to Animation in WPF before and I've found similar styles peppered all over the internet but haven't been able to modify any successfully to get them to do what I'm trying to accomplish.
Try this:
<Style TargetType="Grid" x:Key="AlertStyle">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard Name="Storyboard">
<Storyboard BeginTime="00:00:00">
<DoubleAnimation Storyboard.TargetProperty="Opacity"
Duration="00:00:02" To="1.0" />
<DoubleAnimation Storyboard.TargetProperty="Height"
Duration="00:00:01" To="60.0"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<StopStoryboard BeginStoryboardName="Storyboard" />
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
If that doesn't work, then you can try using a DataTrigger with a custom Boolean value... you can't go too far wrong with that.

Categories