How do I use content size in Button Template Triggers? - wpf

I'm creating a ControlTemplate for a Button. I want to animate the size so it grows to fit the content when the mouse hovers over it. However, I'm not sure how to get the content size in the triggers section. Here is what I have.
<ControlTemplate x:Key="buttonTemplate" TargetType="Button">
<Grid Name="grid" Height="12" Width="12" Opacity="0.4">
<ContentPresenter Name="content" ... />
</Grid>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="grid"
Storyboard.TargetProperty="Width"
To="40"
Duration="0:0:0.4"/>
<DoubleAnimation
Storyboard.TargetName="grid"
Storyboard.TargetProperty="Height"
To="20"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="grid"
Storyboard.TargetProperty="Width"
Duration="0:0:0.2"/>
<DoubleAnimation
Storyboard.TargetName="grid"
Storyboard.TargetProperty="Height"
Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
I've tried using TemplateBinding in place of the To="40" in the trigger, but that causes exceptions. How can I use dynamic values in the triggers?

Rather than animating the layout sizes directly you can apply a ScaleTransform to grid and animate its ScaleX and ScaleY values. This will not only adjust automatically for whatever the size is but can also provide better performance, especially if you apply it as a RenderTransform instead of a LayoutTransform. If there are other changes to the layout that you're expecting to happen during the animation you can stick with LayoutTransform but it will be slower as it needs to continually recalculate layout measurements and arrangement.

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.

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>

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>

Animation to autosized window on startup in WPF

I am trying to animate an error message popup to expand itself slightly then contract to its original size all in about a half second interval to give emphasis to it in WPF. The window is designed as a border with another border inside that houses a stack panel with an icon on the left and a messagebox to the right. On initialization the messagebox gets the exception populated and the whole window's size is set to sizetocontent. By the way I am using .NET 3.5. Please help!
Add this to your xaml within the outside Border or to the Window
<Style>
<Setter Property="Border.RenderTransform">
<Setter.Value>
<ScaleTransform CenterX="50" CenterY="50" ScaleX="1" ScaleY="1" />
</Setter.Value>
</Setter>
<Style.Triggers>
<EventTrigger RoutedEvent="Border.Loaded">
<EventTrigger.Actions>
<BeginStoryboard >
<Storyboard>
<DoubleAnimation Duration="0:0:0.5"
Storyboard.TargetProperty="RenderTransform.ScaleX"
From="1.0" To="1.1" Duration="0:0:0.5"
AutoReverse="True"/>
<DoubleAnimation Duration="0:0:0.5"
Storyboard.TargetProperty="RenderTransform.ScaleY"
From="1.0" To="1.1" Duration="0:0:0.5"
AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>

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