Search code examples
wpfwpf-controls

Slider On/Off Switch in WPF


I am looking for a WPF tutorial on creating a slide ON/OFF switch like on the iPhone.

Here is an example (the "Send all traffic" option) https://secure-tunnel.com/support/software_setup/iphone/images/iphone_vpn_settings.jpg


Solution

  • I haven't seen a tutorial on this exact problem, but I guess you can start by launching Expression Blend and putting a CheckBox on it. Then select the CheckBox, go to main menu - Object -> Edit Style -> Edit a Copy.

    This will make Blend generate the default style of CheckBox so you're able to modify it. Look at how things work there and you'll be able to achieve some results.

    Basically (besides colors and brushes stuff) you'll need to look at triggers hooked up to the IsChecked property. E.g. when IsChecked is True you move the rectangle to one of the sides, show the ON word and hide the OFF word. To animate this you only need to add trigger in- and out- animations.

    UPD: I've spent 10-15 minutes in blend to make a "prototype":

    <Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Class="CheckBoxIPhone.Window1"
        x:Name="Window"
        Title="Window1"
        Width="320" 
        Height="240" 
        FontFamily="Segoe UI" 
        FontSize="20" 
        WindowStartupLocation="CenterScreen"
        >
    
        <Window.Resources>
            <Style x:Key="CheckBoxStyle1" TargetType="{x:Type CheckBox}">
                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
                <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type CheckBox}">
                            <ControlTemplate.Resources>
                                <Storyboard x:Key="OnChecking">
                                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="slider" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)">
                                        <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="25"/>
                                    </DoubleAnimationUsingKeyFrames>
                                </Storyboard>
                                <Storyboard x:Key="OnUnchecking">
                                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="slider" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)">
                                        <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
                                    </DoubleAnimationUsingKeyFrames>
                                    <ThicknessAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="slider" Storyboard.TargetProperty="(FrameworkElement.Margin)">
                                        <SplineThicknessKeyFrame KeyTime="00:00:00.3000000" Value="1,1,1,1"/>
                                    </ThicknessAnimationUsingKeyFrames>
                                </Storyboard>
                            </ControlTemplate.Resources>
    
                            <DockPanel x:Name="dockPanel">
                                <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" ContentTemplate="{TemplateBinding ContentTemplate}" RecognizesAccessKey="True" VerticalAlignment="Center"/>
                                <Grid Margin="5,5,0,5" Width="50" Background="#FFC0CCD9">
                                    <TextBlock Text="ON" TextWrapping="Wrap" FontWeight="Bold" FontSize="12" HorizontalAlignment="Right" Margin="0,0,3,0"/>
                                    <TextBlock HorizontalAlignment="Left" Margin="2,0,0,0" FontSize="12" FontWeight="Bold" Text="OFF" TextWrapping="Wrap"/>
                                    <Border HorizontalAlignment="Left" x:Name="slider" Width="23" BorderThickness="1,1,1,1" CornerRadius="3,3,3,3" RenderTransformOrigin="0.5,0.5" Margin="1,1,1,1">
                                        <Border.RenderTransform>
                                                <TransformGroup>
                                                    <ScaleTransform ScaleX="1" ScaleY="1"/>
                                                    <SkewTransform AngleX="0" AngleY="0"/>
                                                    <RotateTransform Angle="0"/>
                                                    <TranslateTransform X="0" Y="0"/>
                                                </TransformGroup>
                                            </Border.RenderTransform>
                                            <Border.BorderBrush>
                                                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                                    <GradientStop Color="#FFFFFFFF" Offset="0"/>
                                                    <GradientStop Color="#FF4490FF" Offset="1"/>
                                                </LinearGradientBrush>
                                            </Border.BorderBrush>
                                            <Border.Background>
                                                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                                    <GradientStop Color="#FF8AB4FF" Offset="1"/>
                                                    <GradientStop Color="#FFD1E2FF" Offset="0"/>
                                                </LinearGradientBrush>
                                            </Border.Background>
                                        </Border>
                                    </Grid>
                            </DockPanel>
    
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsChecked" Value="True">
                                    <Trigger.ExitActions>
                                        <BeginStoryboard Storyboard="{StaticResource OnUnchecking}" x:Name="OnUnchecking_BeginStoryboard"/>
                                    </Trigger.ExitActions>
                                    <Trigger.EnterActions>
                                        <BeginStoryboard Storyboard="{StaticResource OnChecking}" x:Name="OnChecking_BeginStoryboard"/>
                                    </Trigger.EnterActions>
                                </Trigger>
                                <Trigger Property="IsEnabled" Value="False">
                                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Window.Resources>
    
        <Grid x:Name="LayoutRoot">
            <CheckBox HorizontalAlignment="Center" Style="{DynamicResource CheckBoxStyle1}" VerticalAlignment="Center" Content="CheckBox"/>
        </Grid>
    </Window>
    

    I'm also suggesting you to read about Styles and Templates in WPF if you're interested.