Style.Trigger vs. Grid.Trigger - wpf

In my Code I have defined two Triggers, a Label and a Canvas.
The description of my problem:
When the cursor goes straight across the Label, the Style.Trigger gets activated and the background colour changes (to orange). When the cursor runs across the canvas-area the Grid.Trigger gets activated and changes the background color(to violet). So far, so good.Is the cursor now, running (after the Grid.Trigger was active) across the label-area the background does not change at all.
It seems to me that the Grid.Trigger gets priority once it was active.
<Window x:Class="Sample01.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<!-- Defined Style starts here -->
<Style x:Key="{x:Type Label}" TargetType="{x:Type Label}" >
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.Setters>
</Trigger.Setters>
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="DarkOrange" Duration="0:0:0.5"
Storyboard.TargetProperty="Background.Color"
/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="White" Duration="0:0:1"
Storyboard.TargetProperty="Background.Color" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
<!-- End defined Style-->
</Grid.Resources>
<!-- Define Trigger -->
<Grid.Triggers>
<EventTrigger RoutedEvent="MouseEnter"
SourceName="canvas">
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="BlueViolet" Duration="0:0:1"
Storyboard.TargetProperty="Background.Color"
Storyboard.TargetName="label" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave"
SourceName="canvas">
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="White" Duration="0:0:1"
Storyboard.TargetProperty="Background.Color"
Storyboard.TargetName="label" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Label x:Name="label" VerticalAlignment="Top" Height="100" BorderBrush="Black" BorderThickness="2" Content="LABEL"/>
<Canvas x:Name="canvas" Height="100" VerticalAlignment="Bottom"
IsHitTestVisible="True"
Background="AntiqueWhite"
/>
</Grid>
Can someone explain this behavior ?

You're running into the order of precedence of value sources for Dependency Properties. A common case of this is when you set a local value directly on an element, a value set in a style is overridden. In this case, you're applying an animation to the property, which takes precedence over anything set in the Style (or even a local value).
To allow the Style to take over again you need to make the animation no longer apply to the Label. You can do this by explicitly removing the initial animation, which will reset back to the original state, like a Property Trigger does:
<EventTrigger RoutedEvent="FrameworkElement.MouseEnter"
SourceName="canvas">
<BeginStoryboard x:Name="GridMouseover">
<Storyboard>
<ColorAnimation To="BlueViolet" Duration="0:0:1"
Storyboard.TargetProperty="Background.Color"
Storyboard.TargetName="label" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="FrameworkElement.MouseLeave"
SourceName="canvas">
<RemoveStoryboard BeginStoryboardName="GridMouseover"/>
</EventTrigger>
The disadvantage of this is that you lose the smooth animation back to White. VisualStateManager is a much better choice for this kind of thing in many cases because it handles that for you automatically.
The other thing you can do is tell the Storyboard to stop applying itself after finishing by changing the FillBehavior:
<EventTrigger RoutedEvent="FrameworkElement.MouseEnter"
SourceName="canvas">
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="BlueViolet" Duration="0:0:1"
Storyboard.TargetProperty="Background.Color"
Storyboard.TargetName="label" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="FrameworkElement.MouseLeave"
SourceName="canvas">
<BeginStoryboard>
<Storyboard FillBehavior="Stop">
<ColorAnimation To="White" Duration="0:0:1"
Storyboard.TargetProperty="Background.Color"
Storyboard.TargetName="label" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>

Related

Make WPF control disappear when lost focus

I have a StackPanel that is being used as a container for other menuing controls. I want the StackPanel to disappear when somewhere else on the UI is clicked (similar to typical menus/context menus). I'm struggling with how to do this. Any suggestions? I've tried event triggers in the style like below but it doesn't seem to work correctly.
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Style.Triggers>
<EventTrigger RoutedEvent="LostMouseCapture" >
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="1" To="0" Duration="0:0:0.1"
Storyboard.TargetProperty="Opacity"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
Set the stackPanel's trigger like this:
<EventTrigger RoutedEvent="MouseEnter" >
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="1" Duration="0:0:0.1"
Storyboard.TargetProperty="Opacity"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
and add this tag to Window (as well as other controls you want to when clicked, hide the stack panel):
<Window.Triggers>
<EventTrigger RoutedEvent="MouseDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="0" Duration="0:0:0.1"
Storyboard.TargetName="disappearingStackPanel"
Storyboard.TargetProperty="Opacity"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
You can also set IsHitTestVisible=False on controls that don't have mouse interaction so you don't have to add the trigger to them.

WPF XAML Animation How do i stop it when bound boolean becomes false?

I want the animation to stop when the boolean CanAnimate becomes false. It starts on true, so how do i tell it to stop when CanAnimate is false? (The CanAnimate bool is set inside a SelectedItem setter)
<Border BorderBrush="Black" BorderThickness="2" Margin="1" Name="ReviewNote">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding CanAnimate}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Border.Opacity)"
From="1.0" To="0.0" AutoReverse="True"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock/>
</Border>
You can try using the ExitActions on the DataTrigger to stop the animation, by overriding with another animation. For instance:
<DataTrigger Binding="{Binding CanAnimate}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Border.Opacity)"
From="1.0" To="0.0" AutoReverse="True"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Border.Opacity)"
From="0.0" To="0.0" Duration="0:0:0.0" FillBehavior="HoldEnd" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
Alternatively, there is a way to stop storyboards in XAML by name, again you could use ExitActions for this. This previous question shows the way.
Hope this helps!

Change Background on DragEnter

I want to change the background of a framework element when the DragEnter event is fired and revert its background when the DragLeave event is fired. Additionally, I want this applied in a style.
Heres what I have currently:
<EventTrigger RoutedEvent="Button.DragEnter">
<BeginStoryboard x:Name="DragHoverStoryboard">
<Storyboard>
<DoubleAnimation Storyboard.Target="??????????"
Storyboard.TargetProperty="Background"
Duration="0:0:0"
To="{DynamicResource HoverBrush}" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Button.DragLeave">
<StopStoryboard BeginStoryboardName="DragHoverStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="Button.Drop">
<StopStoryboard BeginStoryboardName="DragHoverStoryboard" />
</EventTrigger>
The problem here is that I can't apply target by a name because this style can be applied to any FrameworkElement. How do I apply the target to the element that the Style is attached to?
Storyboard.Target is not the problem, just leave it out. However, you need to change the rest of the animation. To animate a color, use a ColorAnimation instead of a DoubleAnimation. Also, the property "Background" does not contain a color but a brush, so animate the property "Background.Color" instead. Here is a working example:
<Style TargetType="Button">
<Setter Property="Background" Value="Red"/>
<Style.Triggers>
<EventTrigger RoutedEvent="Button.DragEnter">
<BeginStoryboard x:Name="DragHoverStoryboard">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Background.Color"
Duration="0:0:0" To="Green" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Button.DragLeave">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Background.Color"
Duration="0:0:0" To="Red" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>

WPF DoubleAnimation does not decrease the value after an animation

Consider the following simple WPF form, we will try to animate border1's Height:
This is the XAML for border1:
<Border Margin="3" BorderBrush="Black" BorderThickness="1" Name="border1">
<Border.Style>
<Style>
<Setter Property="Control.Height" Value="50" />
<Style.Triggers>
<DataTrigger Binding="{Binding X}" Value="1">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Height" To="100" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding X}" Value="2">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Height" To="200" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding X}" Value="3">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Height" To="300" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding X}" />
</Border>
X is a normal DependencyProperty and buttons do their work without problem and TextBlock's content inside border1 shows the changed value.
Now the problem is Height can be only Increased! For example by pressing 1, Height increases to 100, pressing 3 increases it to 300 but pressing 2 does not change the height.
If I set the initial border1's height to, for example, 400, all buttons can decrease it to 100, 200 or 300 but after this stage no animation can decrease border's height.
Am I missing some obvious point regarding WPF animation?
Update
Part of the issue is the storyboard in the DataTrigger is not being stopped, so (the last DataTrigger defined) is always holding the Height's 'animated' value.
Adding a StopStoryboard in the exit action fixes part of it. You can now switch between all three heights again... but the animation always starts from the Height's base value (which is clearly not what you want). You want it to start from where it was last set at.
The following MSDN article explains the second issue pretty well:
http://msdn.microsoft.com/en-us/library/aa970493.aspx
Good Solution- EventTriggers on your three buttons seem to work better and doesn't have the problems that the DataTrigger encounters (plus you don't even need the dependency property 'X' anymore!)
<Border Margin="3" BorderBrush="Black" BorderThickness="1" Name="border1">
<Border.Style>
<Style>
<Setter Property="Control.Height" Value="50" />
</Style>
</Border.Style>
<TextBlock Text="{Binding X}" />
</Border>
and
<StackPanel Orientation="Horizontal">
<Button Content="1" Width="50">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="border1" Storyboard.TargetProperty="Height" To="100"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<Button Content="2" Width="50">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="border1" Storyboard.TargetProperty="Height" To="200"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<Button Content="3" Width="50">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="border1" Storyboard.TargetProperty="Height" To="300"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
There must be a simpler way to do this, but I cannot recall if there is at the moment.
Otherwise this will work. You can set the ExitAction of your triggers to set back to the default value. Do this by using a DoubleAnimation with no To value.
i.e.,
<DataTrigger Binding="{Binding X}" Value="1">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" Duration="0:0:0.2" To="100" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>

Smooth transition from image to image

Here is my XAML:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Image x:Name="Expander_Normal"
Source="/Images/arrow-e.tiff" Width="13" Height="13" />
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter x:Name="Expander_Expanded"
TargetName="Expander_Normal" Property="Source"
Value="/Images/arrow-s.tiff" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
The transition from image to another image is very rough and I don't really like it. So how can I make the transitions smooth.
UPDATE:
Maybe instead of changing the image, maybe ROTATE the image. The main image looks like >. So maybe rotate it down (90 degrees clockwise)
If you want to go fancy, you could:
Add a story board
Use a double animation on opacity to fade out the image box
Change the image
Use another double animation to fade in the image box
UPDATE
To rotate the image:
Add a rotate transform to the image
Use a double animation on the rotate transform's angle property
See http://www.vbforums.com/showthread.php?t=555120 for an example
Try this:
<Grid>
<Image Source="Image1.png"
Height="100" Width="100">
<Image.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
BeginTime="0:0:0"
Duration="0:0:0.5"
From="1"
To="0"
Storyboard.TargetProperty="(Image.Opacity)"
/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
BeginTime="0:0:0"
Duration="0:0:0.8"
From="0"
To="1"
Storyboard.TargetProperty="(Image.Opacity)"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
<Image Source="Image2.png"
Height="100" Width="100" Opacity="0">
<Image.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
BeginTime="0:0:0"
Duration="0:0:0.5"
From="0"
To="1"
Storyboard.TargetProperty="(Image.Opacity)"
/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
BeginTime="0:0:0"
Duration="0:0:0.8"
From="1"
To="0"
Storyboard.TargetProperty="(Image.Opacity)"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
</Grid>

Resources