c# wpf animating opacity in visibility trigger - c#

I am creating a Style containing Animation's, which can then be inherited to specific control styles, which are applied automatically.
Actually I'm struggling on implementing a simple Animation:
When Visibility is changed to Visible, Opacity is changed from 0 to 1
When Visibility is changed to something else than visible, Opacity is doing the inverse thing
So far I got:
<Style x:Key="BaseAnimationsStyle">
<Style.Triggers>
<Trigger Property="FrameworkElement.Visibility" Value="Visible">
<Trigger.EnterActions> <!-- this works -->
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions><!-- this doesn't -->
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
If the above Style is set on a control it then has the following behaviour:
When the control's Visibility is set to Visible the transition is working correctly, meaning it is fading in.
Issue:
When the control's Visibility is set to Hidden (or even Collapsed), the control will be hidden instantly without fading.
My guess is, that there is some default behaviour to be overridden of how FrameworkElement's deal with Visibility-Changes.

Setting the Visibility property to Collapsed or Hidden will make the element invisible right away but instead of setting the Visibility property you could set some attached property of yours and then animate the Opacity property to fade out the element. Please refer to the following link for more information and an example.
WPF: How To Animate Visibility Property?: http://blogs.microsoft.co.il/arik/2010/02/08/wpf-how-to-animate-visibility-property/ https://www.codeproject.com/Articles/57175/WPF-How-To-Animate-Visibility-Property
WPF Fade Animation

I have solved my issue with the information provided by #mm8's Answer.
Basically I added the VisibilityAnimation Class to my Project. I then simply created my Base-Style using the Provided Attached-Property inside a Setter.
<!-- Animations -->
<Style x:Key="BaseAnimationsStyle">
<Setter Property="anim:VisibilityAnimation.AnimationType" Value="Fade" />
</Style>
<!-- This adds the Visibility Animation to every Grid which has access to this resource -->
<Style TargetType="{x:Type Grid}" BasedOn="{StaticResource BaseAnimationsStyle}" />

Another Solution:
Create AttachedProperties for Visiblity. This enables you to set the Visibility of any control without having the Opacity set automatically.
If you want to use Bindings on the property, the first evaluation of the binding causes an animation, if the value is not Visibility.Visible. This is why another Property is needed, to specify another Visibility to start.
public static class AnimateableVisibility
{
public static readonly DependencyProperty VisibilityProperty = DependencyProperty.RegisterAttached(
"Visibility", typeof(Visibility), typeof(AnimateableVisibility), new PropertyMetadata(default(Visibility), VisibilityPropertyChanged));
private static void VisibilityPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var val = (Visibility) e.NewValue;
// Set StartVisibility to Visible when Visibility is set to Visible
if (val == Visibility.Visible)
d.SetCurrentValue(StartVisibilityProperty, val);
}
public static readonly DependencyProperty StartVisibilityProperty = DependencyProperty.RegisterAttached(
"StartVisibility", typeof(Visibility), typeof(AnimateableVisibility), new PropertyMetadata(default(Visibility)));
public static Visibility GetVisibility(DependencyObject obj)
{
return (Visibility)obj.GetValue(VisibilityProperty);
}
public static void SetVisibility(DependencyObject obj, Visibility value)
{
obj.SetValue(VisibilityProperty, value);
}
public static Visibility GetStartVisibility(DependencyObject obj)
{
return (Visibility)obj.GetValue(VisibilityProperty);
}
public static void SetStartVisibility(DependencyObject obj, Visibility value)
{
obj.SetValue(VisibilityProperty, value);
}
}
Now you can use those properties as follows:
<Grid>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Orientation="Horizontal">
<Button Margin="5,0,15,0" Padding="7,0" Style="{StaticResource VisibilityAnimation}" utils:AnimateableVisibility.StartVisibility="Hidden"
utils:AnimateableVisibility.Visibility="{Binding ElementName=CheckBox, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}">I'm a Button</Button>
<CheckBox VerticalAlignment="Center" IsChecked="False" x:Name="CheckBox"></CheckBox>
</StackPanel>
</Grid>
By changing StartVisibility to Visible, you can see how the Button fades out on startup.
all thats missing now is the applied Style:
<Style x:Key="VisibilityAnimation">
<Style.Triggers>
<Trigger Property="utils:AnimateableVisibility.StartVisibility" Value="Hidden">
<!-- This avoids the Animation in cases when the first evaluation of AnimateableVisibility.Visibility is false -->
<Setter Property="UIElement.Visibility" Value="Hidden" />
</Trigger>
<Trigger Property="utils:AnimateableVisibility.Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation To="1" Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation To="0" Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>

Related

Make a single method work for multiple WPF elements

I've made a click event/method that alters the opacity and IsEnabled properties of a textbox.
private void EditButton(object sender, RoutedEventArgs e)
{
religionTB.IsEnabled = true;
DoubleAnimation fade = new
DoubleAnimation(1,TimeSpan.FromSeconds(0.2));
religionTB.BeginAnimation(OpacityProperty, fade);
}
In my WPF project, there are multiple textboxes, I'd like to apply this method to all these textboxes without having to list all of them in the method. How would I go by this?
You can achieve this by using a Style. To do so, go to the context of your event handler (Control or Window) and add a DependencyProperty, to flag the enabled mode and bind a ToggleButton (the edit button) to it that sets this property to enable/ disable the controls and to trigger a fade in and fade out animation:
In your control:
public static readonly DependencyProperty IsEditEnabledProperty = DependencyProperty.Register(
"IsEditEnabled",
typeof(bool),
typeof(MainWindow),
new PropertyMetadata(default(bool)));
public bool IsEditEnabled { get { return (bool) GetValue(MainWindow.IsEditEnabledProperty); } set { SetValue(MainWindow.IsEditEnabledProperty, value); } }
In your XAML add the TextBox style and link a ToggleButton to IsEditEnabled:
<Window.Resources>
<Style x:Key="OpacityStyle" TargetType="TextBox">
<Setter Property="Opacity" Value="0" />
<Setter Property="IsEnabled"
Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=IsEditEnabled}" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=IsEditEnabled}"
Value="True">
<! Fade in animation -->
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
BeginTime="0:0:0"
From="0"
To="1"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<! Fade out animation -->
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
BeginTime="0:0:0"
From="1"
To="0"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<ToggleButton x:Name="EditButton" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=IsEditEnabled, Mode=TwoWay}" />
<TextBox x:Name="AnimatedTextBox" Style="{StaticResource TextBoxAnimationStyle}" >
<TextBox x:Name="AnotherAnimatedTextBox" Style="{StaticResource TextBoxAnimationStyle}" >
<TextBox x:Name="NonanimatedTextBox" >
</StackPanel>
</Grid>
If you make the Style implicit by removing the x:Key attribute, it will apply to all TextBox elements within the scope

WPF change button hover style programmatically

I want to create a custom style for buttons in my WPF application. I want to be able to change their Backgroound and BorderThickness properties when mouse is over them.
Some of the buttons are created dynamically in c# so I don't think that xaml solution will be enough.
I tried achieving my goal using Triggers and Setters in c#, but I couldn't get it to work, hover color always stayed light blue.
Here is how I'm creating buttons:
Button but = new Button()
{
Height = 40,
Background = new SolidColorBrush(Colors.Gray),
Content = new Label() {
FontSize = 13,
FontWeight = FontWeights.Medium,
Foreground = new SolidColorBrush(Colors.White)
}
}
stackPanel.Children.Add(but)
I know this is not the best way to create controls and I could probably be better off using MVVM, but I can't change the whole application so I'm just looking for a c# solution.
Thanks!
EDIT:
I added a style to App.xaml file. It looks like this (UpperMenuModeButton is my control based on regular button):
<Application.Resources>
<Style TargetType="UpperMenuModeButton" x:Key="UpperMenuModeButton" >
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Application.Resources>
I am setting the style while creating the button like this:
this.Style = (Style) Resources["UpperMenuModeButton"];
I am still getting regular light blue background on hover.
You can create a general style like this:
<Style TargetType="Button">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="Comic Sans MS"/>
<Setter Property="FontSize" Value="14"/>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:0.2"
Storyboard.TargetProperty="MaxHeight"
To="90" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:1"
Storyboard.TargetProperty="MaxHeight" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style>
Put this code in the scope of your buttons (in it's page or grid or container) and it'll work automatically.
For more info see here
Edit
For your problem regarding button hover events see here
For that style to work on a normal button you need to set the TargetType to Button. Otherwise it will not be applied. Hope this helps.

How to show select|unselect states for grids in xaml?

I have a few grids which contains button and textblock inside each grids. I have grid style like this
<Style x:Key="SubSection_Grid_Style" TargetType="Grid">
<Setter Property="Margin" Value="0,5,0,5"/>
</Style>
I want to show which grid has been tapped or selected, and after tapping another grid, it should change state back to normal. I know how to set it like
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="SelectionBackground"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="1" />
</Storyboard>
but not sure how to switch between select|unselect. Any idea?
Instead of trying to use the states of the grid. Try to write your own converter and bind the opacity or background to some Boolean variable.
<Grid Background="{Binding IsSomeGridShow, Converter={StaticResource BooleantoOpacityOrBackgroundConverter}}" >
<Button>
<TextBlock>
</Grid>

How to use condition in EventTrigger in WPF

I have two questions.
Q1: How can I use conditions in an EventTrigger?
In the following code you can see two EventTriggers for ListBoxItem. As you can see, This EventTriggers is for MouseEnter and MouseLeave event.
<UserControl.Resources>
<DataTemplate x:Key="DataTemplateItemDirect">
...
...
...
<DataTemplate.Triggers>
<EventTrigger SourceName="borderItem" RoutedEvent="Border.MouseLeave">
<BeginStoryboard>
<Storyboard>
<ParallelTimeline>
<ThicknessAnimationUsingKeyFrames Storyboard.TargetName="TitlePanel" Storyboard.TargetProperty="Margin" BeginTime="00:00:00">
<SplineThicknessKeyFrame KeyTime="00:00:00" Value="0,0,0,0" />
<SplineThicknessKeyFrame KeyTime="00:00:0.1" Value="0,40,0,0" />
</ThicknessAnimationUsingKeyFrames>
</ParallelTimeline>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger SourceName="borderItem" RoutedEvent="Border.MouseEnter">
<BeginStoryboard>
<Storyboard>
<ParallelTimeline>
<ThicknessAnimationUsingKeyFrames Storyboard.TargetName="TitlePanel" Storyboard.TargetProperty="Margin" BeginTime="00:00:00">
<SplineThicknessKeyFrame KeyTime="00:00:00" Value="0,40,0,0" />
<SplineThicknessKeyFrame KeyTime="00:00:0.1" Value="0,0,0,0" />
</ThicknessAnimationUsingKeyFrames>
</ParallelTimeline>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</UserControl.Resources>
Q2: In the server side code (C# Code), I've defined a public static bool variable and my condition is that when the variable was equal to True, EventTrigger do their work. My goal is to give the option to the users to enable or disable animations in EventTriggers.
So how can I do that?
Q1 : Conditions cannot be used in the XAML but in your code.
Q2 : Why is your bool variable declared as static ?
If you want to give the users a possibility to disable or enable something, may be you could use a checkbox and bind it to your bool variable.
In WPF, an EventTrigger in XAML simply hooks an event to an animation. Your requirements cannot be accomplished with XAML alone. One way that you could implement your requirements would be to attach an event handler to the events in XAML and then do the conditional part in the event handler.
First, you'll need to define your Storyboards in some Resources section so that you can access them from the event handler. Then you can check the value of your bool variable and programmatically start the Storyboard from there if it is true.
In Resources:
<Window.Resources>
<Storyboard x:Key="YourStoryboard" ... />
</Window.Resources>
In XAML:
<UserControl.Resources>
<DataTemplate x:Key="DataTemplateItemDirect">
...
<Border Name="borderItem" DataContext="{Binding}"
MouseEnter="MainWindow_MouseEnter" MouseLeave="MainWindow_MouseLeave" ... />
...
</DataTemplate>
</UserControl.Resources>
In the event handler:
private void MainWindow_MouseEnter(object sender, RoutedEventArgs e)
{
if ((sender.DataContext).YourBoolVariable)
{
Storyboard storyboard = (Storyboard)FindResource("YourStoryboard");
Storyboard.SetTarget(storyboard , YourControl);
storyboard.Begin();
}
}
UPDATE >>>
In order to deal with multiple animations individually, you can add the YourBoolVariable into your data item class (so that you have one for each animation). Then in the DataTemplate, you can set the DataContext of the Border to the data bound item and then retrieve it in the event handlers. The code example has been updated to reflect this.

How to pass StaticResources to a ViewModel in MVVM?

My application (MVVM Light) resizes it's main window (hides and shows it with an animation). For the animation I use a DataTrigger with parameters from StaticResources:
<Window.Resources>
<system:Double x:Key="WindowMaxWidth">400</system:Double>
<system:Double x:Key="WindowMinWidth">25</system:Double>
</Window.Resources>
<Window.Style>
<Style TargetType="Window">
<Style.Triggers>
<DataTrigger Binding="{Binding DropBox.IsShown}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width"
To="{StaticResource WindowMaxWidth}"
Duration="0:0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width"
To="{StaticResource WindowMinWidth}"
Duration="0:0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Style>
In my ViewModel I need my window's width value, so I bound it. The problem is that it's 0 by default, so I have to initialize it with a value. Actually what need is the value form my static resources: WindowMaxWidth.
I can't move the value of WindowMaxWidth to ViewModel because DataTriggr doesn't accept bindings (it complains about threads)
I don't want to keep the same value separately in StaticResources and ViewModel to avoid incoherence.
What should I do?
Put WindowMaxWidth and WindowMinWidth in your viewmodel and reference them with x:Static:
namespace MyNamespace
{
class ViewModel
{
public static double WindowMaxWidth = 400;
public static double WindowMinWidth = 25;
}
}
Import the right namespace xmlns:myns="clr-namespace:MyNamespace"
<DoubleAnimation Storyboard.TargetProperty="Width"
To="{x:Static myns:ViewModel.WindowMaxWidth}"
Duration="0:0:0:0.2"/>
You can use code behind in such way (for instance in constructor, after you set DataContext to ViewModel):
(this.DataContext as MyViewModel).MyWindowWidth = (double)this.FindResource("WindowMaxWidth");

Categories