WPF Xaml Storyboard stops after dynamically changing Accent (Mahapps) - wpf

I have a WPF app which is using Mahapps for styling. The user has the ability to change the apps accent and or theme during run time which is done through the ThemeManager (a class provided by Mahapps). I have a Border which I have styled:
<Border Margin="5">
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="{DynamicResource AccentColorBrush3}"/>
<Setter Property="BorderThickness" Value="2 2 2 2"/>
<Setter Property="Background" Value="{DynamicResource AccentColorBrush4}"/>
<Setter Property="CornerRadius" Value="0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsPopulated}" Value="True">
<Setter Property="BorderBrush" Value="{DynamicResource AccentColorBrush3}"/>
<Setter Property="BorderThickness" Value="2 2 2 2"/>
<Setter Property="Background" Value="{DynamicResource AccentColorBrush4}"/>
<Setter Property="CornerRadius" Value="0"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsPopulated}" Value="False">
<Setter Property="BorderThickness" Value="3"/>
<Setter Property="CornerRadius" Value="2"/>
<DataTrigger.EnterActions>
<BeginStoryboard Name="BeginStoryboard">
<Storyboard>
<ColorAnimation AutoReverse="True"
RepeatBehavior="Forever"
Storyboard.TargetProperty="BorderBrush.Color"
Duration="00:00:01"
From="Transparent"
To="IndianRed"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="BeginStoryboard"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid/>
</Border>
The styling and datatriggers work as expected. The border has a glowing red border brush which fades in and out - this is to grab the users attention. However, when the accent is changed at run time through a settings menu I have created, this seems to remove the animation and the border brush colour becomes the selected accent colour. The border thickness remains at 3 so I can see the setters for the data trigger stay the same, but the storyboard seems to be removed.
Any ideas as to why this is happening?

Related

Select background Color Based On Condition In WPF without loosing hover effect

I have this Style:
<Style x:Key="MenuBtn" TargetType="Button">
<Setter Property="Margin" Value="3"/>
<Setter Property="Background" Value="{DynamicResource PN_ColorFondoAcentoClaro}"/>
<Setter Property="Width" Value="240"/>
<Setter Property="Height" Value="55"/>
<Setter Property="FontFamily" Value="{DynamicResource PN_FontFamily}"/>
<Setter Property="FontSize" Value="{DynamicResource PN_FontSizeMediana}"/>
<Setter Property="Foreground" Value="{DynamicResource PN_ColorFuenteOscuro}"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
BorderBrush="{DynamicResource PN_ColorFondoAcentoClaro}"
BorderThickness="0.5"
Background="{TemplateBinding Background}"
CornerRadius="5">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="{DynamicResource PN_ColorAlerta}"/>
<Setter Property="BorderThickness" Value="10"/>
</Trigger>
<EventTrigger RoutedEvent="Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="220" Duration="0:0:0.7"
AccelerationRatio="0.10" DecelerationRatio="0.10"
Storyboard.TargetProperty="(Canvas.Width)" />
<DoubleAnimation Duration="0:0:0.7"
AccelerationRatio="0.10" DecelerationRatio="0.15"
Storyboard.TargetProperty="(Canvas.Width)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard Storyboard="{StaticResource SetHoverBackgroundStoryboard}"/>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard Storyboard="{StaticResource RestoreBackgroundStoryboard}"/>
</EventTrigger>
<DataTrigger Binding="{Binding IsFavorite}" Value="True">
<Setter Property="Background" Value="{DynamicResource PN_ColorFondoAcentoClaro}"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsFavorite}" Value="False">
<Setter Property="Background" Value="Transparent"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I need to show some options, some ones are marked as favorites with IsFavorite property. I want to show a diferente color when IsFavorite is active. I used two datatriggers for it
<DataTrigger Binding="{Binding IsFavorite}" Value="True">
<Setter Property="Background" Value="{DynamicResource PN_ColorFondoAcentoClaro}"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsFavorite}" Value="False">
<Setter Property="Background" Value="Transparent"/>
</DataTrigger>
They works pretty well, but my mouse hover now shows nothing. Thats why I tryed
<Storyboard x:Key="SetHoverBackgroundStoryboard">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame
KeyTime="0:0:0"
Value="{StaticResource PN_ColorAlerta}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="RestoreBackgroundStoryboard">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame
KeyTime="0:0:0"
Value="{StaticResource PN_ColorFondoAcentoClaro}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
SetHoverBackgroundStoryboard sets my background like hover, but when I use RestoreBackgroundStoryboard don't know how to use IsFavorite to show my original color again.
There is a way to combine DataTrigger and EventTrigger to solve it?
How to avoid loose hover background effect when DataTrigger change my control background?
or
What I'm doing wrong?
You could use a DataTrigger to combine several conditions, for example that IsFavorite is true and IsMouseOver is also true:
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsFavorite}" Value="True" />
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Red" />
</MultiDataTrigger>

XAML: Style ControlTemplate storyboard animation won't play, but trigger IS hit

I have a button style which I'm currently making. I want to trigger a smooth animation when the user clicks on the button. I need the two rectangle to be contained within the ControlTemplate as shown below.
The idea is that upon pressing the button, one of the rectangles fades out, and upon releasing it fades back in.
The triggers ARE being hit. I can prove this by adding a simple colour change for the buttons by doing the following before the Trigger.EnterActions:
<Setter TargetName="BackgroundNormal" Property="Fill" Value="Cyan" />
The storyboards seem not to trigger at all regardless of their content.
What am I doing wrong, please?
<Style x:Key="MyButton" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="{DynamicResource TextParagraphWhiteP1}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Light" />
<Setter Property="Background" Value="{DynamicResource Gradient_Playback_ButtonBase}" />
<Setter Property="MinHeight" Value="15" />
<Setter Property="MinWidth" Value="0" />
<Setter Property="Padding" Value="6,2" />
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Rectangle x:Name="BackgroundDepressed" Fill="Magenta" />
<Rectangle x:Name="BackgroundNormal" Fill="{DynamicResource Gradient_Playback_ButtonBase}" />
</Grid>
<ControlTemplate.Resources>
<Storyboard x:Key="PressDown">
<DoubleAnimation Storyboard.TargetName="BackgroundNormal" Storyboard.TargetProperty="(UIElement.Opacity)" To="0.0" Duration="0:0:0.1" />
</Storyboard>
<Storyboard x:Key="Release">
<DoubleAnimation Storyboard.TargetName="BackgroundNormal" Storyboard.TargetProperty="(UIElement.Opacity)" To="1.0" Duration="0:0:0.35" />
</Storyboard>
</ControlTemplate.Resources>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="true">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource PressDown}" />
</Trigger.EnterActions>
</Trigger>
<Trigger Property="IsPressed" Value="false">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource Release}" />
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Button's nested style trigger not executed after click

I've tried to recreate a material design kind of button. One of the animations of this button is that upon mouse over is that the color of the button changes to a darker hue of the original color.
In order to realise this for every color, I overlay my button with a rectangle and change it's opacity between 25 % and 0 % depending on mouse over or not. This style is implemented in the style of the rectangle. But it appears that this style is only executed before I click. Once I click the button, this style trigger is not executed anymore.
I've also implemented a animation to change the opacity of the rectangle to 40 % when the button is clicked. this animation however, is still visible after the first click.
I thought that it might be because the rectangle's style is included in the control template. But I don't know how to extract those styles from the control template to the button's style.
<Style x:Key="MaterialFlatButton" TargetType="Button">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Rectangle RadiusX="4" RadiusY="4" Fill="#000000" Panel.ZIndex="1" x:Name="Reccie">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Opacity" Value="0"/>
<!--These styles-->
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="0.26"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="false">
<Setter Property="Opacity" Value="0"/>
</Trigger>
<EventTrigger RoutedEvent="MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.4" Duration="0:0:0.25"/>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"
Margin="16 0 16 0" TextBlock.FontFamily="{StaticResource LatoMedium}"
TextBlock.FontSize="{StaticResource FontSizeBody}" Typography.Capitals="AllSmallCaps"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<!--Should go here perhaps?-->
<Style.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#000000"/>
<Setter Property="Opacity" Value="0.26"/>
</Trigger>
</Style.Triggers>
</Style>
edit: does it do what you want if you set FillBehavior="Stop"?
<EventTrigger RoutedEvent="MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard FillBehavior="Stop">
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.26" To="0.4" Duration="0:0:0.25"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>

Strange conflict of XAML IsMouseOver and IsEnabled causing button to appear active when disabled

In my application, I have a custom button style and template that has a couple states based on XAML triggers:
<Style x:Key="UniversalButton" TargetType="{x:Type Button}">
<Setter Property="Cursor" Value="Hand" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Background" Value="#FFD7D7D7" />
<Setter Property="BorderBrush" Value="#FF999999" />
<Setter Property="Foreground" Value="#FF666666" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<ControlTemplate.Resources>
<Style x:Key="ButtonBorder" TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="3" />
<Setter Property="BorderThickness" Value="0,0,0,2" />
<Setter Property="SnapsToDevicePixels" Value="True" />
</Style>
<Style x:Key="ButtonContent" TargetType="{x:Type ContentPresenter}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="TextBlock.TextAlignment" Value="Center" />
<Setter Property="Margin" Value="8,7,8,8" />
<Setter Property="RecognizesAccessKey" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
</Style>
</ControlTemplate.Resources>
<ControlTemplate.Triggers>
<Trigger Property="ContextMenu.IsOpen" Value="True">
<Setter Property="Opacity" Value="0.8" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsEnabled" Value="True" />
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.8" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<MultiTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</MultiTrigger.ExitActions>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.6" />
</Trigger>
</ControlTemplate.Triggers>
<Border Style="{DynamicResource ButtonBorder}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
Padding="{TemplateBinding Padding}"
Opacity="{TemplateBinding Opacity}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ContentPresenter Style="{DynamicResource ButtonContent}"
TextBlock.FontWeight="{TemplateBinding FontWeight}"
TextBlock.Foreground="{TemplateBinding Foreground}"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The specific instance of the button in question has a command with a CanExecute based on the selections of an ItemsControl in the view. When I toggle my selections, including those that are meant to disable the button, things work fine. The button becomes semi-transparent as I have called for in the XAML trigger for IsEnabled.
The problem starts when I mouse over the button, activating the storyboards above. After this happens, changing the selections to a state in which the button is disabled leaves the button fully opaque, regardless of the state of IsEnabled/CanExecute.
Through extensive debugging, I've determined that the issue is not caused by the CanExecute as previously thought, and that it must be linked to the IsMouseOver trigger. I've exhausted all of my XAML knowledge in trying to figure this conflict out and I'd be very grateful for help.
Thanks in advance!
I believe that the issue is caused by Dependency Property value precedence in WPF. In this case the animation (Timeline) has the default FillBehavior of FillBehavior.HoldEnd. After the animation finishes all the attempts to change the value (the Opacity property) should have a higher precedence. From the link you can see only system coercing the value can override the animation-based value (even locally setting such as by assignment directly in codebehind cannot override this).
That's why your template Trigger (whose precedence is 7) cannot override the current animated value. There are some solutions to this but I think you should use all storyboard in your triggers (instead of using a setter). If you don't want any animation, just set Duration to zero:
<Trigger Property="ContextMenu.IsOpen" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To=".8" Duration="0:0:0"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<!-- similar for ExitActions -->
</Trigger>
<!-- -->
<Trigger Property="IsEnabled" Value="False">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To=".6" Duration="0:0:0"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<!-- similar for ExitActions -->
</Trigger>
Using Storyboard this way is also good for maintenance. That's when you want to add some animation to the property value change, then you already have the Storyboard there and you can just add more options or animations...
PS: Don't be afraid of long XAML code. It's the nature and a characteristic of XAML code. However as a trade-off, we have more controls over the behaviors and effects.
I just have the same problem.
After several tests, I found the ExitActions of IsEnabled trigger, though finished its execution, would still somehow "stays" there and blocks the upcoming animation.
I haven't figured out the exact reason behind it, yet emprically, removing that storyboard before beginning another seems to solve the problem.
Codes like:
<Trigger Property="ContextMenu.IsOpen" Value="True">
<Trigger.EnterActions>
<RemoveStoryboard BeginStoryboardName="someStoryboard" />
<BeginStoryboard>
<!-- animation here -->
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Trigger.ExitActions>
<BeginStoryboard x:Name="someStoryboard">
</Trigger>

'Setter' object cannot be added to 'EventTrigger'?

I get the following error on the WPF code bellow:
'Setter' object cannot be added to 'EventTrigger'. The given object must be an instance of TriggerAction or a derived type.
<Style x:Key="LinkLabel" TargetType="{x:Type Label}">
<Setter Property="FontFamily" Value="Tahoma"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Foreground" Value="DarkBlue"/>
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter" >
<Setter Property="Cursor" Value="Hand"/>
</EventTrigger>
<EventTrigger RoutedEvent="MouseDown" >
<Setter Property="Foreground" Value="Red"/>
</EventTrigger>
<EventTrigger RoutedEvent="MouseUp" >
<Setter Property="Foreground" Value="DarkBlue"/>
</EventTrigger>
</Style.Triggers>
</Style>
Can anyone explain and point me in the right direction ?
MadSeb
Option 1 - Use BeginStoryboard like ChrisF said
Option 2 - Don't use EventTrigger - for example, for a label that changes background color when you mouse over it use:
<Style TargetType="Label">
<Setter Property="Background" Value="Blue"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true" >
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
Unfortunately there is no property IsMouseDown you can use.
About your example:
You don't have to set the cursor on MouseEnter, just set the Cursor property and it will only affect the mouse cursor when the mouse is over the control.
If you want to create an hyperlink control don't use a label, eitehr use a Button with a custom template (this will give you the Click event and IsPressed property) or, even better - use the Hyperlink class like this:
<TextBlock><Hyperlink>This is a link</Hyperlink></TextBlock>
This will have all the styling you wanted.
You must use a TriggerAction to change the values as in this example from the EventTriggers MSDN page:
<Style TargetType="Rectangle">
<Setter Property="Width" Value="50" />
<Setter Property="Height" Value="50" />
<Setter Property="Margin" Value="20" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="300" Duration="0:0:1.5"
AccelerationRatio="0.10" DecelerationRatio="0.25"
Storyboard.TargetProperty="(Canvas.Width)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1.5"
AccelerationRatio="0.10" DecelerationRatio="0.25"
Storyboard.TargetProperty="(Canvas.Width)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>

Resources