Search code examples
wpfxamlstoryboardwpf-controls

How can I animate the BorderThickness of a Button in WPF?


First of all, please bear with me. I'm completely new to StackOverflow, WPF, and XAML. Anyway, I watched some tutorials and understand the basics of Animations with StoryBoards. I already created some Double- and ColorAnimations.

Now I want a border to appear around a button when the mouse is over it (with an animation), but I can't get it to work. First, this is the style applied to the button.

<Style x:Key="DarkButtonNoBG" TargetType="{x:Type Button}">
        <Setter Property="OverridesDefaultStyle" Value="True" />
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="Foreground" Value="DarkGray" />
        <Setter Property="FontWeight" Value="Light"/>
        <Setter Property="FontSize" Value="15"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Grid Background="{TemplateBinding Background}">
                        <Border BorderThickness="0" BorderBrush="White">
                            <ContentPresenter x:Name="MyContentPresenter" 
                                          Content="{TemplateBinding Content}"
                                          HorizontalAlignment="Center" 
                                          VerticalAlignment="Center" />

                        </Border>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>

        ...
    </Style>

The border animation works when I remove the style, but then the background color of the button changes to light blue when hovered over and I can't override it with a color animation. This is my button control:

<Button Style="{StaticResource DarkButtonNoBG}" x:Name="btnStart" Grid.Row="4" Height="40" Width="150"
                 Click="btnStart_Click" Content="Start" FontSize="17" Foreground="White"    BorderThickness="0" BorderBrush="White">
                <!--#region Animation-->
                <Button.Triggers>
                    <EventTrigger RoutedEvent="MouseEnter">
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation Storyboard.TargetProperty="Width" From="150" To="165" Duration="0:0:0.4" DecelerationRatio="1"/>
                                <DoubleAnimation Storyboard.TargetProperty="Height" From="40" To="50" Duration="0:0:0.4" DecelerationRatio="1"/>
                                <DoubleAnimation Storyboard.TargetProperty="FontSize" From="17" To="20" Duration="0:0:0.4" DecelerationRatio="1"/>
                                <ColorAnimation Storyboard.TargetProperty="Background.Color" From="Transparent" To="#88444444"
                                                Duration="0:0:0.4" DecelerationRatio="1"/>
                                **<ThicknessAnimation Storyboard.TargetProperty="BorderThickness" To="2,2,2,2" Duration="0:0:1"/>**
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>

                    <EventTrigger RoutedEvent="MouseLeave">
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation Storyboard.TargetProperty="Width" From="165" To="150" Duration="0:0:0.4" DecelerationRatio="1"/>
                                <DoubleAnimation Storyboard.TargetProperty="Height" From="50" To="40" Duration="0:0:0.4" DecelerationRatio="1"/>
                                <DoubleAnimation Storyboard.TargetProperty="FontSize" From="20" To="17" Duration="0:0:0.4" DecelerationRatio="1"/>
                                <ColorAnimation Storyboard.TargetProperty="Background.Color" From="#88444444" To="Transparent"
                                                Duration="0:0:0.4" DecelerationRatio="1"/>
                                **<ThicknessAnimation Storyboard.TargetProperty="BorderThickness" 
                                                    To="0,0,0,0" Duration="0:0:0.4"/>**

                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                </Button.Triggers>
                <!--#endregion-->
            </Button>

The Width, Height, FontSize and Background Animations work perfectly but it seems I can't access the BorderThickness property because it is in the template.

I tried moving the ThicknessAnimation to the style, so I added this to the style:

<Trigger Property="IsMouseOver" Value="True">
                <Trigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <ColorAnimation Storyboard.TargetProperty="Background.Color" To="#88444444"
                                                Duration="0:0:0.4" DecelerationRatio="1"/>
                            **<ThicknessAnimation Storyboard.TargetProperty="BorderThickness" To="2,2,2,2" Duration="0:0:0.5"/>**
                        </Storyboard>
                    </BeginStoryboard>
                </Trigger.EnterActions>
                <Trigger.ExitActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <ColorAnimation Storyboard.TargetProperty="Background.Color" To="Transparent"
                                                Duration="0:0:0.4" DecelerationRatio="1"/>
                            **<ThicknessAnimation Storyboard.TargetProperty="BorderThickness" To="2,2,2,2" Duration="0:0:0.5"/>**
                        </Storyboard>
                    </BeginStoryboard>
                </Trigger.ExitActions>
            </Trigger>

The ColorAnimation works but the ThicknessAnimation still doesn't. Perhaps I need to animate the BorderThickness Property that is in the Template, but I don't know how to access it.

I also tried animating the BorderThickness with 4 DoubleAnimations like this:

<DoubleAnimation Storyboard.TargetProperty="BorderThickness.Top" To="2"  Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetProperty="BorderThickness.Bottom" To="2"  Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetProperty="BorderThickness.Left" To="2"  Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetProperty="BorderThickness.Right" To="2"  Duration="0:0:0.5"/>

But this throws an Exception: **System.InvalidOperationException**: ''BorderThickness' property does not point to a DependencyObject in path 'BorderThickness.Top'.'

Sorry if I messed up the formatting. If I did, I will try to correct it. Again, this is my first question. Thanks in advance!

EDIT: For now, I just added the border on MouseOver without an animation:

<Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type Button}">
                            <Grid Background="{TemplateBinding Background}">
                                <Border BorderThickness="0.5" BorderBrush="White">
                                    <ContentPresenter x:Name="MyContentPresenter" 
                                          Content="{TemplateBinding Content}"
                                          HorizontalAlignment="Center" 
                                          VerticalAlignment="Center" />

                                </Border>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Trigger>

This actually works well enough, but I'd still like to know how I could animate it...


Solution

  • You need to bind the borders BorderThickness to the templated parent.

    <ControlTemplate TargetType="{x:Type Button}">
        <Grid Background="{TemplateBinding Background}">
            <Border BorderThickness="{TemplateBinding BorderThickness}" 
                    BorderBrush="White">
                <ContentPresenter x:Name="MyContentPresenter" 
                                          Content="{TemplateBinding Content}"
                                          HorizontalAlignment="Center" 
                                          VerticalAlignment="Center" />
    
            </Border>
        </Grid>
    </ControlTemplate>
    

    You've hard coded the BorderThickness within your control template which means that changing it through means such as within the button declaration, e.g. <Button BorderThickness="2"/> will not effect the border in any way.