Slider On/Off Switch in WPF

前端 未结 6 664
灰色年华
灰色年华 2020-12-22 21:39

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-tunn

相关标签:
6条回答
  • 2020-12-22 21:47

    Well I set out to accomplish this same task but the examples fell a little short of production code. For instance what happens if you put text into the control like On/Off or a localized version of the text, answer is you have to define the size of the control because you cannot modify an animation or template triggers after you have rendered the control. This gives you a one-size-fits-all solution that will not work with localized text.

    My solution was to create the template for the control dynamically using the control's Loaded event and the FrameworkElementFactory class. I have to say it was by far complicated but could be achieved. Simply put, in your code behind, create a template as described, setup dependency properties for your on/off text, use the text metrics to determine the control's width, height and animation values.

    I'm sorry that I cannot post the solution since it belongs to Avanquest now. :(

    Look for it in SystemSuite and Fix-It 12.

    0 讨论(0)
  • 2020-12-22 21:48

    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.

    0 讨论(0)
  • 2020-12-22 21:48

    I created some styles based on the post by arconaut in orange and blue.

    Screenshot: click

    I wanted my style to closer match the On/Off Switch style of iOS devices. One difference in this style is, that the sliding animation does only slide the switch and not the indicators below. Maybe if i can find some time I will modify it that way. Until then I want to share my result. It's not perfect, but here is the code.

    <LinearGradientBrush x:Key="CheckedBlue" StartPoint="0,0" EndPoint="0,1">
      <GradientStop Color="#FF285AB3" Offset="0" />
      <GradientStop Color="#FF4184EC" Offset="0.5" />
      <GradientStop Color="#FF558BED" Offset="0.5" />
      <GradientStop Color="#FF7DACF0" Offset="1" />
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="CheckedOrange" StartPoint="0,0" EndPoint="0,1">
      <GradientStop Color="#FFCA6A13" Offset="0" />
      <GradientStop Color="#FFF67D0C" Offset="0.2" />
      <GradientStop Color="#FFFE7F0C" Offset="0.2" />
      <GradientStop Color="#FFFA8E12" Offset="0.5" />
      <GradientStop Color="#FFFF981D" Offset="0.5" />
      <GradientStop Color="#FFFCBC5A" Offset="1" />
    </LinearGradientBrush>
    <SolidColorBrush x:Key="CheckedOrangeBorder" Color="#FF8E4A1B" />
    <SolidColorBrush x:Key="CheckedBlueBorder" Color="#FF143874" />
    <Style x:Key="OrangeSwitchStyle" 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.1000000" Value="53" />
                </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.1000000" Value="0" />
                </DoubleAnimationUsingKeyFrames>
              </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>
                <Border x:Name="BackgroundBorder" BorderBrush="#FF939393" BorderThickness="1" CornerRadius="3" Height="27" Width="94">
                  <Border.Background>
                    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                      <GradientStop Color="#FFB5B5B5" Offset="0" />
                      <GradientStop Color="#FFDEDEDE" Offset="0.1" />
                      <GradientStop Color="#FFEEEEEE" Offset="0.5" />
                      <GradientStop Color="#FFFAFAFA" Offset="0.5" />
                      <GradientStop Color="#FFFEFEFE" Offset="1" />
                    </LinearGradientBrush>
                  </Border.Background>
                  <Grid>
                    <Grid.ColumnDefinitions>
                      <ColumnDefinition /><ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <Ellipse x:Name="Off" Width="14" Height="14" Stroke="#FF7A7A7A" StrokeThickness="2" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" />
                    <Line x:Name="On" X1="0" Y1="0" X2="0" Y2="14" Stroke="#FF7A7A7A" StrokeThickness="2" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" />
                  </Grid>
                </Border>
                <Border BorderBrush="#FF939393" HorizontalAlignment="Left" x:Name="slider" Width="41" Height="27" BorderThickness="1" CornerRadius="3" RenderTransformOrigin="0.5,0.5" Margin="0">
                  <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.Background>
                    <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
                      <GradientStop Color="#FFF0F0F0" Offset="0" />
                      <GradientStop Color="#FFCDCDCD" Offset="0.1" />
                      <GradientStop Color="#FFFBFBFB" Offset="1" />
                    </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>
                <Setter TargetName="On" Property="Stroke" Value="White" />
                <Setter TargetName="Off" Property="Stroke" Value="White" />
                <!-- Change Orange or Blue color here -->
                <Setter TargetName="BackgroundBorder" Property="Background" Value="{StaticResource CheckedOrange}" />
                <Setter TargetName="BackgroundBorder" Property="BorderBrush" Value="{StaticResource CheckedOrangeBorder}" />
              </Trigger>
              <Trigger Property="IsEnabled" Value="False">
                <!-- ToDo: Add Style for Isenabled == False -->
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
    
    0 讨论(0)
  • 2020-12-22 21:56

    I like the nice prototype from arconaut and I added some code to get the border rounded.

    You have to remove the "Background" property from the Grid declaration and add this lines between the Grid declaration and the first TextBlock declaration:

    <Border
        x:Name="back"
        CornerRadius="3,3,3,3"
        BorderThickness="1"
        BorderBrush="#FFC0CCD9"
        >
        <Rectangle Fill="#FFC0CCD9"/>
    </Border>
    
    0 讨论(0)
  • 2020-12-22 22:00

    A few open source ToggleSwitches:

    • ToggleSwitch looks good, available by NuGet, too.
    • MahApps ToggleSwitch is a part of bigger MahApps library of Metro like controls.
    0 讨论(0)
  • 2020-12-22 22:02

    I just posted an entry in regards to this which provides a proper sliding action, full configurability and doesn't require to be a fixed size (got annoyed as I couldn't find anything on the web that provided a solution like that).

    You can find it here: http://itsallaboutthexaml.blogspot.com/2012/01/variables-in-animation.html

    Know it's late in answering this question, but hope it helps anyone in the future.

    0 讨论(0)
提交回复
热议问题