Make WPF control disappear when lost focus - wpf

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.

Related

RoutedEvent TextBox.LostFocus not working in my WPF application

I am working with the WPF animations using Storyboards. What I want is to fade out all the content of the page as soon as my textbox gets focus, and to fade in everything back as soon as the focus is removed from the textbox. To do that, I have the following XAML with no code behind.
<Page.Triggers>
<EventTrigger RoutedEvent="TextBox.GotFocus">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="Grid1"
Storyboard.TargetProperty="Opacity"
From="1" To="0.1" Duration="0:0:0.2"
AutoReverse="False" >
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="TextBox.LostFocus">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="Grid1"
Storyboard.TargetProperty="Opacity"
From="0.1" To="1" Duration="0:0:0.2"
AutoReverse="False">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
To move focus out of textbox, I have the following button:
<Button
Name="SearchButton"
Height="30" Width="30"
Grid.Column="1"
Focusable="True"
IsHitTestVisible="True"
Style="{StaticResource SearchButton}"
Padding="3,3,3,3"
Margin="3,3,3,3" Click="Button_Click"/>
When I run the app, clicking on the textbox makes the fade out work fine. But when I click on the button, the fade in does not kick in.
Can anyone please give me some insights?
You should place your triggers in the TextBox directly, not on the Page level:
<TextBox>
<TextBox.Triggers>
<EventTrigger RoutedEvent="TextBox.GotFocus">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="Grid1"
Storyboard.TargetProperty="Opacity"
From="1" To="0.1" Duration="0:0:0.2"
FillBehavior="HoldEnd">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="TextBox.LostFocus">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="Grid1"
Storyboard.TargetProperty="Opacity"
From="0.1" To="1" Duration="0:0:0.2"
FillBehavior="Stop">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBox.Triggers>
</TextBox>
Otherwise, the storyboards will be triggered for each and every GotFocus and LostFocus routed event of each UIElement on the Page because of these events' bubbling strategy.

Style.Trigger vs. Grid.Trigger

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>

Fade out animation on DockPanel

<DockPanel Name="MyPanel" IsVisibleChanged="MyPanel_IsVisibleChanged">
<DockPanel.Triggers>
<EventTrigger RoutedEvent="IsVisibleChanged"> // error here
</EventTrigger>
</DockPanel.Triggers>
</DockPanel>
Above is my dockpanel xmal code. Because IsVisibleChanged is not a RoutedEvent I can not add in the EventTrigger this code:
<Storyboard x:Key="hideMe">
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:2" To="0.0"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:2" Value="{x:Static Visibility.Hidden}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="showMe">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:5" To="0.75"/>
</Storyboard>
I try to give a fade out animation to my dockpanel.
Instead of using an event trigger, I would use a normal trigger to check the value of the Visibility property of the DockPanel.
You can create a style on the DockPanel to do it, like this:
<DockPanel Name="MyPanel">
<DockPanel.Style>
<Style TargetType="DockPanel">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<!-- Set storyboard to run when DockPanel is set visible here: -->
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<!-- Set storyboard to run when DockPanel is no longer set visible here: -->
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
</DockPanel>

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 Window fadeIn does not work

I have the following code:
<Window.Background>
<SolidColorBrush Opacity="0.7" Color="White" x:Name="BackgroundBrush"></SolidColorBrush>
</Window.Background>
<Window.Triggers>
<EventTrigger RoutedEvent="Loaded">
<EventTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:5" Storyboard.TargetName="BackgroundBrush" From="0.7">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger.EnterActions>
</EventTrigger>
</Window.Triggers>
But nothing happened when window is shown. Why?
You have to animate the opacity of the window itself, rather than the background.
You need to set AllowsTransparency to true, which also necessitates the WindowStyle to be set to None. (You need to create your own standard window buttons)
In addition to what H.B. said, you need to add your BeginStoryboard to the EventTrigger.Actions collection, not the EnterActions collection. So this works:
<Window.Background>
<SolidColorBrush Opacity="0.7" Color="White" x:Name="BackgroundBrush"></SolidColorBrush>
</Window.Background>
<Window.Triggers>
<EventTrigger RoutedEvent="Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:5" Storyboard.TargetName="BackgroundBrush" From="0.7">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Window.Triggers>

Resources