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.
Related
I have a Style for a ToggleButton which defines a ControlTemplate. My ToggleButton is animated when it changes states, but i don't want it to animate when i navigate to a new page. So, i added an EventTrigger on the Loaded event with SkipStoryboardToFill to avoid this behavior, and it does what i want.
My only issue now, is that when i add a new ToggleButton, it tries to skip storyboards which haven't been started, generating an Animation Warning ("Unable to perform action because the specified Storyboard was never applied to this object for interactive control.") which seems to impact my application's performance.
I could probably work around that but i'd rather solve the actual problem. Is there a way i could add a condition in my EventTrigger ?
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="ToggleButton.Loaded">
<SkipStoryboardToFill BeginStoryboardName="checkedSB" />
<SkipStoryboardToFill BeginStoryboardName="uncheckedSB" />
</EventTrigger>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource Self}}"
Value="true">
<DataTrigger.EnterActions>
<BeginStoryboard Name="checkedSB">
<Storyboard Storyboard.TargetName="Ellipse"
Storyboard.TargetProperty="Margin">
<ThicknessAnimation To="20 1 2 1" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard Name="uncheckedSB">
<Storyboard Storyboard.TargetName="Ellipse"
Storyboard.TargetProperty="Margin">
<ThicknessAnimation To="2 1 2 1" Duration="0:0:0.1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</ControlTemplate.Triggers>
Is there a way i could add a condition in my EventTrigger ?
Short answer: No.
An EventTrigger always applies when the corresponding event is being raised.
If you want to trigger the animations conditionally, you should either switch to using a MultiDataTrigger or implement the animations programmatically.
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>
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>
I have a ListBox in WPF which is binded using an XMLDataProvider which points to an xml File.
I have a storyboard set in the itemtemplate of my ListBoxItem as such:
<Setter Property="LayoutTransform">
<Setter.Value>
<ScaleTransform x:Name="transform" />
</Setter.Value>
</Setter>
<Style.Triggers>
<EventTrigger RoutedEvent="Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:.2" />
<DoubleAnimation Storyboard.TargetProperty="LayoutTransform.ScaleY" From="0" Duration="0:0:.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
This works great when the file is first loaded on starting the application. However when I add a new XMl element to the list like this:
MainNode.AppendChild(NewElement)
The element is added to the list but the storyboard does not fire. How can I make sure the programatically added item calls the "Loaded" event onto itself?
UPDATE: The style was applied to ListBox instead of ListBoxItem. Changing solved the question.
As mentioned in the comments Style should be applied on ListBoxItem which will be raised for every new ListBoxItem add.
ListBox loaded event will be raised only when it gets loaded on UI first time and not afterwards. So, move the style from ListBox to ListBoxItem.
I'm creating a drag and drop behavior, and the goal is to drag an item onto my grid, where a set of adorned elements representing the available actions will be available for the user to drop the element on. My problem is once I add the adorned element(s) to the AdornerLayer, I don't receive any Drag events. I need to get those events to both change UI and set some underlying properties. I've set AllowDrop=true on the AdornerLayer, the adorned element, my button inside the DataTemplate inside the ContentPresenter, and on the ContentPresenter itself, but still don't get any events.
<DataTemplate x:Key="promoMediaTemplate" DataType="{x:Type media:PromoMediaSearchResult}">
<Button Content="{Binding Path=Description}" Name="item" AllowDrop="True" Background="Red" /
<DataTemplate.Triggers>
<EventTrigger RoutedEvent="Button.PreviewDragEnter">
<BeginStoryboard x:Name="TextBeginStoryBoard">
<Storyboard>
<ColorAnimation
Storyboard.TargetName="item"
Storyboard.TargetProperty="Background"
Duration="0:0:1.0"
From="Red" To="Green" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Button.PreviewDragLeave">
<StopStoryboard BeginStoryboardName="TextBeginStoryBoard" />
</EventTrigger>
<EventTrigger RoutedEvent="Button.PreviewDrop">
<StopStoryboard BeginStoryboardName="TextBeginStoryBoard" />
</EventTrigger>
</DataTemplate.Triggers>
See this post: http://blogs.telerik.com/StefanDobrev/Posts/08-04-16/WPF_Series_Adorners_Commands_and_Logical_Tree.aspx