Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Startup state for ToggleButton animation

Tags:

wpf

I want to create an animated "Switch" style for a ToggleButton (like on Smartphones).

The animation itself is not the problem, the switch looks nice and animates when I click on it. But it also animates when it's loaded and is switched on.

When I load my view, it should show some switches switched on and some switched off. But it shows only "off" and automatically starts the "switch on" animation.

The following simple code shows my problem: Instead of switches I make the Button 150px wide when it is checked.

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Class="AnimationTest2.MainWindow"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource {x:Type ToggleButton}}" x:Key="SwitchStyle">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource Self}}" Value="true">
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard Storyboard.TargetProperty="Width">
                                <DoubleAnimation To="150"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                    <DataTrigger.ExitActions>
                        <BeginStoryboard>
                            <Storyboard Storyboard.TargetProperty="Width">
                                <DoubleAnimation To="50"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.ExitActions>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <ToggleButton Width="50" Content="Button" Style="{StaticResource ResourceKey=SwitchStyle}"/>
        <ToggleButton Width="50" Content="Button" Style="{StaticResource ResourceKey=SwitchStyle}" IsChecked="True"/>
    </StackPanel>
</Window>

The first ToggleButton works well. It's not checked and animates to 150px when clicked. The second ToggleButton is checked by default and should be 150px right away, but instead it starts growing when I start my application!

Somehow I need to define an initial state for my ToggleButtons depending on the IsChecked state and start the animation only on click. How can I achieve this?

I've also tried with the VisualStateManager, much like in this answer, but then I have the same problem...

UPDATE

This is the actual working switch, if someone's not only interested in the proof-of-concept:

    <Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource {x:Type ToggleButton}}" x:Key="SwitchToggleButton">
        <Setter Property="BorderBrush" Value="Black"/>
        <Setter Property="Background" Value="Gray"/>
        <Setter Property="Foreground" Value="Black"/>
        <Setter Property="Width" Value="70"/>
        <Setter Property="Height" Value="30"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ToggleButton}">
                    <Grid>
                        <Border x:Name="BackBorder" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}"/>
                        <TextBlock x:Name="Off" Margin="30,0,0,0" VerticalAlignment="Center"  HorizontalAlignment="Center"><Run Text="OFF"/></TextBlock>
                        <TextBlock x:Name="On" Margin="0,0,30,0" VerticalAlignment="Center"  HorizontalAlignment="Center"><Run Text="ON"/></TextBlock>
                        <Border x:Name="Slider" Background="LightGray" Width="30" Height="30" HorizontalAlignment="Left" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <EventTrigger RoutedEvent="Border.Loaded" SourceName="Slider">
                            <SkipStoryboardToFill BeginStoryboardName="checkedSB" />
                            <SkipStoryboardToFill BeginStoryboardName="checkedSB2" />
                            <SkipStoryboardToFill BeginStoryboardName="uncheckedSB" />
                            <SkipStoryboardToFill BeginStoryboardName="uncheckedSB2" />
                        </EventTrigger>
                        <DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource Self}}" Value="true">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard x:Name="checkedSB">
                                    <Storyboard Storyboard.TargetProperty="Margin" Storyboard.TargetName="Slider">
                                        <ThicknessAnimation To="40,0,0,0" Duration="00:00:00.2"/>
                                    </Storyboard>
                                </BeginStoryboard>
                                <BeginStoryboard x:Name="checkedSB2">
                                    <Storyboard Storyboard.TargetProperty="(Control.Background).(SolidColorBrush.Color)">
                                        <ColorAnimation To="LightSeaGreen" Duration="00:00:00.3"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.EnterActions>
                            <DataTrigger.ExitActions>
                                <BeginStoryboard x:Name="uncheckedSB">
                                    <Storyboard Storyboard.TargetProperty="Margin" Storyboard.TargetName="Slider">
                                        <ThicknessAnimation To="0" Duration="00:00:00.2"/>
                                    </Storyboard>
                                </BeginStoryboard>
                                <BeginStoryboard x:Name="uncheckedSB2">
                                    <Storyboard Storyboard.TargetProperty="(Control.Background).(SolidColorBrush.Color)">
                                        <ColorAnimation To="{x:Null}" Duration="00:00:00.3"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.ExitActions>
                        </DataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
like image 216
JCH2k Avatar asked Dec 16 '15 09:12

JCH2k


1 Answers

You can add SkipStoryboardToFill objects into Loaded EventTrigger. So the code would look like this.

<Style.Triggers>
    <EventTrigger RoutedEvent="ToggleButton.Loaded">
        <SkipStoryboardToFill BeginStoryboardName="checkedSB" />
        <SkipStoryboardToFill BeginStoryboardName="uncheckedSB" />
    </EventTrigger>
    <DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource Self}}" Value="true">
        <DataTrigger.EnterActions>
            <BeginStoryboard Name="checkedSB">
                <Storyboard Storyboard.TargetProperty="Width">
                    <DoubleAnimation To="150"/>
                </Storyboard>
            </BeginStoryboard>
        </DataTrigger.EnterActions>
        <DataTrigger.ExitActions>
            <BeginStoryboard Name="uncheckedSB">
                <Storyboard Storyboard.TargetProperty="Width">
                    <DoubleAnimation To="50"/>
                </Storyboard>
            </BeginStoryboard>
        </DataTrigger.ExitActions>
    </DataTrigger>
</Style.Triggers>
like image 94
bars222 Avatar answered Oct 12 '22 23:10

bars222