C# WPF Pulsing Button - c#

I need some help figuring out how to make a button pulse its background colour. I use the style below to create the buttons and would like the buttons background colour to pulse (different shades of colour) when in normal state. The purpose is to draw the users attention to the button.
Ideally I would like this to occur after a certain period of inactivity. Is it possible to do this within the style or do I need to create a complete custom UserControl ? If it is possible I would appreciate it if someone could provide sample code illustrating how to achieve this.
I believe it should be possible to do the following:
1. Create a ControlTemplate.Resources section and add a Storyboard which defines the animation to be used.
1.1 What would the storyboard look like and how does it relate to the actual button background colours
1.2 How would one add a delay to the start of the storyboard (so it only gets triggered after a period of inactivity (i.e. no click on the button)
2. Add a trigger somehow to start the storyboard. how to do that for the normal state of the button.
<Style x:Key="ContinueButton" TargetType="{x:Type Button}">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid x:Name="grid">
<Border x:Name="border" CornerRadius="0" BorderBrush="{StaticResource ThemeSolidColorBrushGreen}" BorderThickness="2" Background="{StaticResource ThemeSolidColorBrushGreen}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" TextElement.Foreground="White"
TextElement.FontSize="{Binding Source={StaticResource settingsProvider}, Path=Default.FontSizeParagraph}" TextElement.FontFamily="{Binding Source={StaticResource settingsProvider}, Path=Default.FontFamily}" TextElement.FontWeight="Bold"></ContentPresenter>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" TargetName="border" Value="{StaticResource ThemeSolidColorBrushGreen}">
</Setter>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="grid" Value="0.25"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

You can use EventTrigger to achieve this. Just replace the ControlTemplate.Trigger section of your code with the following code
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Button.MouseEnter">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="border" AutoReverse="True"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="Blue" RepeatBehavior="Forever" Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" TargetName="border" Value="GreenYellow"/>
</Trigger>
<!--<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" TargetName="border" Value="Gray"/>
</Trigger>-->
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="grid" Value="0.25"/>
</Trigger>
</ControlTemplate.Triggers>

Related

WPF Toggle-Button hover animation issue

In my WPF application, I've created 3 different custom toggle button styles. I would like to add a colour animation anytime a button is hovered over, so using Event Triggers and Color Animation storyboards, this is what I've done;
BTW, there's an image in the each of the toggle buttons styles.
Toggle Button Style
<Style x:Key="ExeatKey" TargetType="ToggleButton">
<Setter Property="Foreground" Value="#B2B2B2"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Margin" Value="0 0 0 0"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid Margin="0 0 0 0" VerticalAlignment="Center" HorizontalAlignment="Center">
<Image Margin="0 10 0 30">
<!--Image Change Colour Trigger-->
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=takeExeatBtn, Path=IsChecked}" Value="True">
<Setter Property="Source" Value="/Images/Others/Button Icons/ex_blu.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=takeExeatBtn, Path=IsChecked}" Value="False">
<Setter Property="Source" Value="/Images/Others/Button Icons/ex_gray.png"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<TextBlock Text="Take An Exeat" TextAlignment="Center" FontSize="20" Margin="0 58 0 0" FontWeight="SemiBold"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="#BCD0E8" Duration="0:0:0.5" Storyboard.TargetProperty="Background.Color"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="Transparent" Duration="0:0:0.5" Storyboard.TargetProperty="Background.Color"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Background" Value="#BCD0E8"/>
<Setter Property="Foreground" Value="#5050EA"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Cursor" Value="Arrow"/>
<Setter Property="Background" Value="#BCD0E8"/>
<Setter Property="Foreground" Value="#7878FF"/>
</Trigger>
</Style.Triggers>
</Style>
Now naturally, I thought that this would work, but whenever the mouse enters it doesnt animate, only MouseLeave works.
Is there something that I've missed in the code?

IsMouserOver Interference With IsSelected in WPF

Hey guys I have an issue with triggers for IsMouserOver and IsSelected.
My goal is to create an animation that changes the BorderThickness of my ListViewItems in IsMouserOver. Using EnterActions and ExitActions yields the desired result, however, when I try to also take into account the IsSelected property in another trigger, every property but BorderThickness can be set.
When I remove the whole IsMouseOver trigger, BorderThickness will be set in IsSelected and displayed correctly.
<Style TargetType="{x:Type ListViewItem}" x:Key="SubMenuStyles">
<Setter Property="Height" Value="40"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Left"
VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="Orange"/>
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation
Storyboard.TargetProperty="BorderThickness" From="0,0,0,0" To="10,0,0,0"
Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation
Storyboard.TargetProperty="BorderThickness" From="10,0,0,0" To="0,0,0,0"
Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<Trigger Property="ListViewItem.IsSelected" Value="True">
<Setter Property="Background" Value="#233E4F"/>
<Setter Property="BorderThickness" Value="50,0,0,0"/>
<Setter Property="BorderBrush" Value="Orange"/>
</Trigger>
</Style.Triggers>
</Style>
Problem
The behavior you encountered before is shown in the picture below:
where the problem is that your orange border is being reset every time the mouse is over the selected item.
What I believe you want to achieve is to keep the fixed 50px border on a selected item, like shown below:
Solution
In order to achieve that, we need to find a way to execute the animation only for items that are not selected (i.e. where IsSelected="False").
This is where MultiTriggers come into play.
MultiTriggers are quite like "normal" Triggers with the important addition that they fire not when only one condition is fulfilled, but when all conditions are fulfilled.
e.g.
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsSelected" Value="False" />
<!-- More conditions, if you want -->
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<!-- Will only be set when ALL conditions are fulfilled. -->
</MultiTrigger.Setters>
<MultiTrigger.EnterActions>
<!-- Will also only be executed when ALL conditions are fulfilled -->
</MultiTrigger.ExitActions>
</MultiTrigger>
Code
So, in your case, adjust your style to look like this:
<Window.Resources>
<Style x:Key="SubMenuStyles" TargetType="{x:Type ListViewItem}">
<Setter Property="Height" Value="40" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter HorizontalAlignment="Left" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="ListViewItem.IsSelected" Value="True">
<Setter Property="Background" Value="#233E4F" />
<Setter Property="Foreground" Value="White" />
<Setter Property="BorderThickness" Value="50,0,0,0" />
<Setter Property="BorderBrush" Value="Orange" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsSelected" Value="False" />
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="BorderBrush" Value="Orange" />
</MultiTrigger.Setters>
<MultiTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="BorderThickness"
From="0,0,0,0"
To="10,0,0,0"
Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<MultiTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Storyboard.TargetProperty="BorderThickness"
From="10,0,0,0"
To="0,0,0,0"
Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</MultiTrigger.ExitActions>
</MultiTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ListView Width="200"
Height="150"
Margin="30">
<ListViewItem Style="{StaticResource SubMenuStyles}">A ListView</ListViewItem>
<ListViewItem IsSelected="True" Style="{StaticResource SubMenuStyles}">with several</ListViewItem>
<ListViewItem Style="{StaticResource SubMenuStyles}">items</ListViewItem>
</ListView>
</Grid>

C# WPF MVVM trigger change background color of button permanently

I want to change the color of my button permanently, if the user clicked a button. How do I achieve that in XAML?
The trigger I have right now won't work, because they are only temporary, not overriding default values.
Here my button template:
<Style TargetType="{x:Type Button}" x:Key="ButtonStyleNavigation">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="#FF365B74" />
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="FontFamily" Value="Arial" />
<!-- <Setter Property="BorderThickness" Value="2"/>-->
<!-- <Setter Property="BorderBrush" Value="Yellow"/>-->
<Setter Property="FontSize" Value="15" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border CornerRadius="10" Background="{TemplateBinding Background}">
<Grid>
<ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,5,0" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" Value="0.3"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Yellow" />
<Setter Property="Background" Value="#FF658EAA"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="Yellow" />
<Setter Property="Foreground" Value="#FF658EAA" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
SOLUTION:
So, I was able to get what I needed with the use of a ToggleButton. I've bound for each button one property to their IsChecked function.
In my ViewModel I change the checked-state accordingly to my desire. (mentioned in the comments)
Here is my new ToggleButton template:
<Style TargetType="{x:Type ToggleButton}" x:Key="ButtonStyleNavigation">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="#FF365B74" />
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="FontFamily" Value="Arial" />
<Setter Property="FontSize" Value="15" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border CornerRadius="10" Background="{TemplateBinding Background}" Height="19">
<Grid>
<ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,5,0" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Foreground" Value="Yellow" />
<Setter Property="Background" Value="#FF658EAA"/>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="#FF365B74" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Yellow" />
<Setter Property="Background" Value="#FF658EAA"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="Yellow" />
<Setter Property="Foreground" Value="#FF658EAA" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Sounds like you want a ToggleButton. They have a bool property called "IsChecked" that changes each time you click the button.
Taking the style you provided and changing it like so should achieve what you want.
<Style TargetType="{x:Type ToggleButton}" x:Key="ButtonStyleNavigation">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="#FF365B74" />
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="FontFamily" Value="Arial" />
<Setter Property="FontSize" Value="15" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border CornerRadius="10" Background="{TemplateBinding Background}">
<Grid>
<ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,5,0" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" Value="0.3"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="Yellow" />
<Setter Property="Foreground" Value="#FF658EAA" />
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="#FF658EAA" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have removed the IsMouseOver trigger as it conflicts with the IsChecked triggers.
When you say you permanently i presume this is what you mean. If you wish for the button to remain the same colour after a user clicks on the button again, you will have to deal with click events and check for number of clicks on the button.
EDIT:
As per your comment, if you want to have only one button enabled at time, look into using radio buttons instead.
Here is a tutorial on getting multiple radio buttons set up.
Check out this answer on how to style them to look like normal buttons.
You can then use the triggers in a similar fashion to check for enum values in your view model.
Here is a pure xaml answer, using a ControlTemplate as requested, taking your original question and just changing the <ControlTemplate.Triggers> section:
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Button.Foreground).(SolidColorBrush.Color)" To="Yellow" Duration="0"/>
<ColorAnimation Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)" To="#FF658EAA" Duration="0"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
In your original question you had (perhaps unintentionally) triggered from the mouse over event, in which case change here the RoutedEvent accordingly.
NB. You may be able to find an alternative to ColourAnimation with a zero duration, but I thought I'd post this as it at least gives you something to work with.

how to set image background in wpf

I am trying to set inactive buttons to have semi-transparent (i.e. greyed out) images. Yet for some reason the images become white/ yellow (as if on a bright background):
As you can see it's light yellow
Possibly better annotated screenshot
Here are the XAML Styles:
<Style x:Key="ToolButton" TargetType="{x:Type Button}" BasedOn="{x:Null}">
<Setter Property="Background" Value="#888"/>
<Setter Property="Padding" Value="10,2"/>
<Setter Property="BorderThickness" Value="0"/>
<Style.Resources>
<Style TargetType="Image">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5"/>
</Trigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>
<Style x:Key="ToolPanel" TargetType="{x:Type StackPanel}">
<Setter Property="Background" Value="#111"/>
<Style.Resources>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource ResourceKey=ToolButton}"/>
</Style.Resources>
</Style>
(StackPanel and buttons are dark for contrast, they are actually mean to be light grey)
What should I do to get the greyed out effect I want?
The problem is the default Button template: it changes its background based on the control's state, and that new background takes precedence over the one you're setting in your style.
In this case, the brush for the 'Disabled' state is something like #F4F4F4, or a very light gray.
The fix would be to declare a new Button template. For example:
<ControlTemplate TargetType="Button">
<!--Optional: Add border brush/thickness if you want a border-->
<Border x:Name="border" Background="{TemplateBinding Background}">
<ContentPresenter x:Name="contentSite" Margin="{TemplateBinding Padding}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<!--Optional: Remove background when disabled-->
<!--Setter TargetName="border" Property="Background" Value="Transparent" /-->
<Setter TargetName="contentSite" Property="Opacity" Value="0.5" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="#AAA" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Background" Value="#333" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Note that with this version, you do not need the Image style to change the Opacity; it's taken care of in the Button template.

Mouse pressed trigger for control

I have a control which mainly has one image(I will add more later).I have defined the template .
I want to define a trigger for mouse press but I couldn't find any in control class documentation.
Which property should I check for mouse click.
<Grid>
<Control x:key="Mycontrol" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center">
<Control.Style>
<Style TargetType="{x:Type Control}">
<Setter Property="Width" Value="220"/>
<Setter Property="Height" Value="540"/>
<Setter Property="IsHitTestVisible" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate >
<Grid>
<Image Source="Base.png" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="XXXXXXX" Value="True">
<Setter Property="Background" Value="Black"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Control.Style>
</Control>
</Grid>
If you want to "trap" a mouse click trigger in order to change some other properties of your control, you can use an EventTrigger.
<EventTrigger RoutedEvent="Mouse.MouseDown">
<BeginStoryboard>
<Storyboard>
<ColorAnimation ... />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
If your control needs to respond to mouse click, it is always better to use Button for that purpose. IsPressed property is defined in ButtonBase, which will fullfill your need.
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background"
Value="Red" />
</Trigger>
</Style.Triggers>
</Style>

Categories