Afraz Ali
Afraz Ali

Reputation: 2752

WPF Toggle button style gets affected by hover

I have applied custom styling to a toggle button with control template. I have different background colour and border for default state, checked state and mouse hover. The problem is that when I hover over the checked state, WPF resets the style as if button is not checked (to default state appearance) even though actually the toggle button is checked.

Here is my code

<Style x:Key="MenuItem" TargetType="ToggleButton">
    <Setter Property="Background" Value="Transparent"/>
    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Cursor" Value="Hand"/>
        </Trigger>
    </Style.Triggers>
</Style>

<ControlTemplate x:Key="MenuButton" TargetType="{x:Type ToggleButton}">

    <Border x:Name="border" Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" BorderThickness="4,0,0,0">
        <StackPanel x:Name="MenuItemContainer" Orientation="Horizontal" Height="46" Margin="14,0,0,0" Background="Transparent">
            <TextBlock x:Name="icon" Grid.Column="0" VerticalAlignment="Center" FontFamily="{StaticResource FontAwesome}"  Foreground="#a7b1c2" FontSize="13" Text="{TemplateBinding Tag}" Margin="0,0,6,0"/>
            <TextBlock x:Name="menuText" VerticalAlignment="Center" Foreground="#a7b1c2" FontSize="13" Text="{TemplateBinding Property=ContentControl.Content}"/>
        </StackPanel>

        <VisualStateManager.VisualStateGroups>

            <VisualStateGroup x:Name="CommonStates">
                <VisualState x:Name="MouseOver">
                    <Storyboard>
                        <ColorAnimation Storyboard.TargetName="menuText" Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)" To="White" Duration="0:0:0" />
                        <ColorAnimation Storyboard.TargetName="icon" Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)" To="White" Duration="0:0:0"/>

                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background)" Storyboard.TargetName="border">
                            <DiscreteObjectKeyFrame KeyTime="0">
                                <DiscreteObjectKeyFrame.Value>
                                    <SolidColorBrush Color="{StaticResource ColorMenuItemBg}" />
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush)" Storyboard.TargetName="border">
                            <DiscreteObjectKeyFrame KeyTime="0">
                                <DiscreteObjectKeyFrame.Value>
                                    <SolidColorBrush Color="{StaticResource ColorMenuItemBorderHover}"/>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="Normal"/>
            </VisualStateGroup>

            <VisualStateGroup x:Name="CheckStates">
                <VisualState x:Name="Checked">
                    <Storyboard>
                        <ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderThickness)" Storyboard.TargetName="border">
                            <EasingThicknessKeyFrame KeyTime="0" Value="4,0,0,0"/>
                        </ThicknessAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush)" Storyboard.TargetName="border">
                            <DiscreteObjectKeyFrame KeyTime="0">
                                <DiscreteObjectKeyFrame.Value>
                                    <SolidColorBrush Color="{StaticResource ColorMenuItemBorder}"/>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                        <ColorAnimation Storyboard.TargetName="menuText" Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)" To="White" Duration="0:0:0" />
                        <ColorAnimation Storyboard.TargetName="icon" Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)" To="White" Duration="0:0:0"/>

                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background)" Storyboard.TargetName="border">
                            <DiscreteObjectKeyFrame KeyTime="0">
                                <DiscreteObjectKeyFrame.Value>
                                    <SolidColorBrush Color="{StaticResource ColorMenuItemBg}"/>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="Unchecked"/>
            </VisualStateGroup>


        </VisualStateManager.VisualStateGroups>
    </Border>
</ControlTemplate>

<StackPanel>
            <ToggleButton Tag="&#xf015;" Content="Home" Template="{StaticResource MenuButton}" Style="{StaticResource MenuItem}" />

            <ToggleButton Tag="&#xf1fe;" Content="Reporting" Template="{StaticResource MenuButton}" Style="{StaticResource MenuItem}"/>
</StackPanel>

If you will click a toggle button you will see that it will get the green border and different background colour, however if you will hover over it and leave it, it will seem to appear as default button.

What am I missing here?

Thank you in Advance!

Upvotes: 1

Views: 1166

Answers (1)

Tobias Hoefer
Tobias Hoefer

Reputation: 1118

You defined two VisualtStateGroups in your template. This way you will always have two active states (one per group) for your ToggleButton. The problem is that you are setting the same object properties from VisualStates within two different groups. When you check a ToggleButton and the mouse leaves the button, the control updates its active VisualState for every group. Since your setting Border.Background and Border.BorderBrush from active states in both groups, the last state that gets applied wins (In your case CommonStates/Normal).

To fix this issue, try to avoid setting the same properties from mulitply groups. You can for example set the background of the Stackpanel in the MouseOver-State instead of the border background. This way the ToggleButton stays checked if the mouse leaves it because the "CommonState/Normal"-State will not reset the background of the border.

                    <VisualStateGroup x:Name="CommonStates">
                        <VisualState x:Name="MouseOver">
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetName="menuText" Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)" To="White" Duration="0:0:0" />
                                <ColorAnimation Storyboard.TargetName="icon" Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)" To="White" Duration="0:0:0"/>

                                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background)" Storyboard.TargetName="MenuItemContainer">
                                    <DiscreteObjectKeyFrame KeyTime="0">
                                        <DiscreteObjectKeyFrame.Value>
                                            <SolidColorBrush Color="{StaticResource ColorMenuItemBg}" />
                                        </DiscreteObjectKeyFrame.Value>
                                    </DiscreteObjectKeyFrame>
                                </ObjectAnimationUsingKeyFrames>

                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="Normal"/>
                    </VisualStateGroup>

Hope this was helpful!

Upvotes: 2

Related Questions