0

Essentially, we have a ToggleButton with custom styling that includes different colors for the unchecked, checked, and mouse hover states. The ToggleButton correctly displays the color for the checked state after being clicked. However, when the mouse hovers over the ToggleButton while it's in the checked state, the color changes as expected, but once the mouse is moved away, the color reverts back to that of the unchecked state, even though the ToggleButton remains in the checked state.

This is my custom style code:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<Style TargetType="ToggleButton" x:Key="ToggleButtonTheme">
    <Setter Property="Foreground" Value="Black"/>
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ToggleButton">
                <Grid>
                    <Grid.Resources>
                        <Storyboard x:Key="ToggleOnAnimation">
                            <DoubleAnimation Storyboard.TargetName="SlideButton"
                                             Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
                                             To="12" Duration="0:0:0.1"/>
                        </Storyboard>
                        <Storyboard x:Key="ToggleOffAnimation">
                            <DoubleAnimation Storyboard.TargetName="SlideButton"
                                             Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
                                             To="-12" Duration="0:0:0.1"/>
                        </Storyboard>
                    </Grid.Resources>
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="Disabled"/>
                            <VisualState x:Name="MouseOver">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="Border"
                                                    Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
                                                    To="#848383" Duration="0:0:0.1"/>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="CheckStates">
                            <VisualState x:Name="Unchecked">
                                <Storyboard>
                                    <DoubleAnimation Storyboard.TargetName="SlideButton"
                                                     Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
                                                     To="-12" Duration="0:0:0.1"/>
                                    <ColorAnimation Storyboard.TargetName="Border"
                                                    Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
                                                    To="#a3a2a2" Duration="0:0:0.2"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Checked">
                                <Storyboard>
                                    <DoubleAnimation Storyboard.TargetName="SlideButton"
                                                     Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
                                                     To="12" Duration="0:0:0.1"/>
                                    <ColorAnimation Storyboard.TargetName="Border"
                                                    Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
                                                    To="#474646" Duration="0:0:0.2"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="CheckedMouseOver">
                                <Storyboard>
                                    <DoubleAnimation Storyboard.TargetName="SlideButton"
                                                     Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
                                                     To="12" Duration="0:0:0.1"/>
                                    <ColorAnimation Storyboard.TargetName="Border"
                                                    Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
                                                    To="#848383" Duration="0:0:0.2"/>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Border x:Name="Border"
                            Background="#a3a2a2"
                            CornerRadius="12"
                            BorderThickness="0">
                        <Grid>
                            <ContentPresenter HorizontalAlignment="Center"
                                              VerticalAlignment="Center"
                                              Margin="5"/>
                            <Border x:Name="SlideButton"
                                    Background="White"
                                    CornerRadius="13"
                                    BorderThickness="0"
                                    Width="16" Height="16">
                                <Border.RenderTransform>
                                    <TranslateTransform X="0"/>
                                </Border.RenderTransform>
                            </Border>
                        </Grid>
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

I would be very grateful if somebody could help me with this matter as I am not yet very familiar with custom button styles

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Biswind
  • 25
  • 7
  • See [this answer](https://stackoverflow.com/a/14653539/1136211). I am also unsure whether CheckedMouseOver is a known state at all. See [ToggleButton Styles and Templates](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/togglebutton-styles-and-templates?view=netframeworkdesktop-4.8) – Clemens Jul 22 '23 at 06:04

1 Answers1

0

You set the Background as the target of multiple VisualStateGroups. As the result, its color will be determined by the last executed Storyboard among those VisualStateGroups. You can check it by listening Completed event of each Storyboard.

Looking at the internal code of normal ToggleButton (I ignore your mysterious CheckedMouseOver), the VisualState of CommonStates VisualStateGroup is managed by ChangeVisualState method of ButtonBase class. And that of CheckStates VisualStateGroup is managed by ChangeVisualState method of ToggleButton class. The latter overrides the former and calls the former internally at the top of method. Thus when ToggleButton's ChangeVisualState method is called, both CommonStates and CheckStates VisualStateGroups will be set.

In this ChangeVisualState method, VisualStateManager.GoToState method is called to set the VisualState. I cound find a notable explanation about this method at Use the VisualStateManager to Manage States.

If the control is already in the state that is specified, GoToState takes no action and returns true.

So, thinking about the case where IsChecked property has been already set to true and thus the VisualState of CheckStates VisualStateGroup has been Checked, when IsMouseOver property is changed and invokes ChangeVisualState method, VisualStateManager.GoToState method will take no action for CheckStates VisualStateGroup because its VisualState is already Checked. Consequently, the Storyboard in the CommonStates VisualStateGroup will be that executed last.

In conclusion, you should not make a same property the target of multiple VisualStateGroups. If you want to change the same property depending on MouseOver and Checked/Unchecked states, create a custom VisualStateGroup which holds all those states and manage them on your own.

emoacht
  • 2,764
  • 1
  • 13
  • 24