Search code examples
wpfbuttonvisualstatemanager

VisualStateManager not working on control extending Button


I have extended a Button with some new properties. I also redefined its template like this:

public class CustomButton : Button
{
    #region ShowLabel
    public bool ShowLabel
    {
        get { return (bool)GetValue( ShowLabelProperty ); }
        set { SetValue( ShowLabelProperty, value ); }
    }

    // Using a DependencyProperty as the backing store for ShowLabel.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ShowLabelProperty =
        DependencyProperty.Register( nameof( ShowLabel ), typeof( bool ), typeof( CustomButton ), new PropertyMetadata( true ) );
    #endregion

    #region LabelText
    public string LabelText
    {
        get { return (string)GetValue( LabelTextProperty ); }
        set { SetValue( LabelTextProperty, value ); }
    }

    // Using a DependencyProperty as the backing store for LabelText.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty LabelTextProperty =
        DependencyProperty.Register( nameof( LabelText ), typeof( string ), typeof( CustomButton ) );
    #endregion

    static CustomButton()
    {
        Type ownerType = typeof( CustomButton );

        DefaultStyleKeyProperty.OverrideMetadata( ownerType,
            new FrameworkPropertyMetadata( ownerType ) );

        StyleProperty.OverrideMetadata( ownerType,
            new FrameworkPropertyMetadata( null, ( depObj, baseValue ) =>
            {
                var element = depObj as FrameworkElement;
                if( element != null && baseValue == null )
                    baseValue = element.TryFindResource( ownerType );

                return baseValue;
            } ) );
    }             
}

<Style x:Key="{x:Type cc:CustomButton}" TargetType="{x:Type cc:CustomButton}">
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Foreground" Value="#FF0072C6"/>
    <Setter Property="BorderBrush" Value="#FF0072C6"/>
    <Setter Property="Padding" Value="0"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
    <Setter Property="VerticalAlignment" Value="Center"/>
    <Setter Property="HorizontalAlignment" Value="Center"/>
    <Setter Property="FontSize" Value="13"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type cc:CustomButton}">

                <DockPanel>
                    <Grid DockPanel.Dock="Top" x:Name="grid" Background="Black">

                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal" />

                                <VisualState x:Name="MouseOver">
                                    <Storyboard>
                                        <ColorAnimation Storyboard.TargetName="grid" Storyboard.TargetProperty="Background.Color" To="Green"/>
                                    </Storyboard>
                                </VisualState>

                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <ColorAnimation Storyboard.TargetName="grid" Storyboard.TargetProperty="Background.Color" To="Red"/>
                                    </Storyboard>
                                </VisualState>

                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <ColorAnimation Storyboard.TargetName="grid" Storyboard.TargetProperty="Background.Color" To="Gray"/>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>

                        <Ellipse x:Name="ellipse" Stroke="{TemplateBinding BorderBrush}" Width="64" Height="64"
                                StrokeThickness="3" />

                        <ContentPresenter
                                x:Name="contentPresenter"
                                Content="{TemplateBinding Content}"
                                ContentTemplate="{TemplateBinding ContentTemplate}"
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                Margin="{TemplateBinding Padding}"/>
                    </Grid>

                    <TextBlock DockPanel.Dock="Top" Text="{TemplateBinding LabelText}" FontSize="{TemplateBinding FontSize}" Foreground="{TemplateBinding Foreground}"
                               FontFamily="{TemplateBinding FontFamily}" FontStyle="{TemplateBinding FontStyle}" FontWeight="{TemplateBinding FontWeight}"
                               FontStretch="{TemplateBinding FontStretch}" Visibility="{calcBinding:Binding ShowLabel, RelativeSource={RelativeSource TemplatedParent}}"
                               VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                               HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
                </DockPanel>

            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

However the VisualState is not updated when the mouse passes over the button or the button gets disabled.

What's the problem?


Solution

  • You should apply the VisualStates to the DockPanel, i.e. the parent of the Grid that you are trying to apply the animations on:

    <ControlTemplate TargetType="{x:Type cc:CustomButton}">
        <DockPanel>
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CommonStates">
                    <VisualState x:Name="Normal" />
    
                    <VisualState x:Name="MouseOver">
                        <Storyboard>
                            <ColorAnimation Storyboard.TargetName="grid" Storyboard.TargetProperty="Background.Color" To="Green"/>
                        </Storyboard>
                    </VisualState>
    
                    <VisualState x:Name="Pressed">
                        <Storyboard>
                            <ColorAnimation Storyboard.TargetName="grid" Storyboard.TargetProperty="Background.Color" To="Red"/>
                        </Storyboard>
                    </VisualState>
    
                    <VisualState x:Name="Disabled">
                        <Storyboard>
                            <ColorAnimation Storyboard.TargetName="grid" Storyboard.TargetProperty="Background.Color" To="Gray"/>
                        </Storyboard>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
            <Grid DockPanel.Dock="Top" x:Name="grid" Background="Black">
    
                <Ellipse x:Name="ellipse" Stroke="{TemplateBinding BorderBrush}" Width="64" Height="64"
                                    StrokeThickness="3" />
    
                <ContentPresenter
                                    x:Name="contentPresenter"
                                    Content="{TemplateBinding Content}"
                                    ContentTemplate="{TemplateBinding ContentTemplate}"
                                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                    Margin="{TemplateBinding Padding}"/>
            </Grid>
    
            <TextBlock DockPanel.Dock="Top" Text="{TemplateBinding LabelText}" FontSize="{TemplateBinding FontSize}" Foreground="{TemplateBinding Foreground}"
                                   FontFamily="{TemplateBinding FontFamily}" FontStyle="{TemplateBinding FontStyle}" FontWeight="{TemplateBinding FontWeight}"
                                   FontStretch="{TemplateBinding FontStretch}" Visibility="{calcBinding:Binding ShowLabel, RelativeSource={RelativeSource TemplatedParent}}"
                                   VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                   HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
        </DockPanel>
    </ControlTemplate>
    

    There is no element named "grid" in the Grid itself.