Creating a custom switch

Fuse has a flexible and powerful system to help you style your own and existing controls. Let's look at how it works by styling a ToggleControl control inspired by this example by Ramotion.

In this example, we'll be using:

  • Subclassing of the ToggleControl to create our own control that has multiple states (in this case, on and off)
  • Allowing the user of the control to choose between swiping the thumb using SwipeGesture, or clicking the control to toggle the state

The main things we are addressing with our styling are:

  • The control should have a border that turns white when it is On
  • The background of the track should turn transparent as the control is turned On

The code for the custom switch is simply:

<ToggleControl ux:Class="MySwitch" Margin="4,4,4,4"
    Width="80"
    Height="48" Focus.IsFocusable="true" ux:Name="_switch">
    <Clicked>
        <Toggle Target="_switch" />
    </Clicked>

    <SwipeGesture Direction="Right" Length="38" Type="Active" ux:Name="swipe" IsActive="{Property _switch.Value}"/>

    <SwipingAnimation Source="swipe">
        <Move Target="thumb" X="38"/>
        <Change thumbFill.Color="#fff"/>
        <Change trackFill.Color="#ffffff00"/>
        <Change strokeColor.Color="#ffffffff"/>
    </SwipingAnimation>
    
    <Panel Layer="Background">
        <Circle ux:Name="thumb"
            Alignment="CenterLeft"
            Width="34"
            Height="34"
            Margin="4,0,14,0">
            <SolidColor ux:Name="thumbFill" Color="#fafafaff" />
            <Shadow Angle="90" Distance="0" Size="2" />
        </Circle>

        <Rectangle
            CornerRadius="23"
            Width="80"
            Height="40"
            Alignment="Center">
            <SolidColor ux:Name="trackFill" Color="#e7e7e7" />
            <Stroke>
                <SolidColor ux:Name="strokeColor" Color="#ffffff00" />
            </Stroke>
        </Rectangle>
    </Panel>

    <WhileDisabled>
        <Change thumbFill.Color="#bdbdbdff" Easing="QuadraticInOut" Duration="0.25" />
        <Change trackFill.Color="#0000001e" Easing="QuadraticInOut" Duration="0.25" />
    </WhileDisabled>

</ToggleControl>

And that's basically it for styling the ToggleControl itself. The rest of the effect is accomplished as we react to the state change of the ToggleControl-control. Let's look at some of the points of interest:

<Clicked>
    <Toggle Target="_switch" />
</Clicked>

A typical behavior for a switch is to toggle it's state when clicked. We can simply modify the state of the ToggleControl directly.

<SwipeGesture Direction="Right" Length="38" Type="Active" ux:Name="swipe" IsActive="{Property _switch.Value}"/>

We add a SwipeGesture with the same length as the travel of the switch thumb. We bind the IsActive state of the gesture to the Value of the ToggleControl. This keeps the states synchronized, and what allows us to just call Toggle on the switch in the Clicked handler.

We use SwipingAnimation to define the change in appearance when the toggle is on. This works both when the user is sliding the thumb and when the state is toggled.

<SwipingAnimation Source="swipe">
    <Move Target="thumb" X="38"/>
    <Change thumbFill.Color="#fff"/>
    <Change trackFill.Color="#ffffff00"/>
    <Change strokeColor.Color="#ffffffff"/>
</SwipingAnimation>
The surrounding areas

In the areas surrounding the switch, we want to:

  • Create a Circle that expands and contracts as the switch is toggled
  • Subtly change the colors of the icons
  • Change the TextColor of the Header (inline class)
  • Change the SolidColor in the background when the Circle has expanded to its maximum value
  • Make sure the expanding Circle never extends outside its containing DockPanel, this is achieved using ClipToBounds="true"
  • Slightly move the Circle as the switch changes state, so it expands from and contracts to slightly different locations
Code

We're using these inline classes:

<Text ux:Class="Header" FontSize="24" TextColor="#11abfe"
    TextAlignment="Center" Margin="0,35,0,5" />
<Text ux:Class="Description" TextWrapping="Wrap" FontSize="14"
    TextAlignment="Center" Margin="35,10,35,5" />
<Image ux:Class="Icon" Alignment="VerticalCenter"
    Height="80" Width="80" Color="#dedede" />

Then the configuration entry becomes just a matter of:

<DockPanel ClipToBounds="true">
    <Rectangle Layer="Background">
        <SolidColor ux:Name="secondBackgroundColor" Color="#fff" />
    </Rectangle>
    <Header Dock="Top" ux:Name="secondHeader">Automatic synchronization</Header>
    <Description Dock="Top">Synchronize all contact information when connecting using USB.</Description>
    <StackPanel Orientation="Horizontal" Alignment="Center">
        <Icon Height="80" File="Assets/Connect.png" ux:Name="connect" />
        <Panel Margin="35,0,0,0">
            <MySwitch Alignment="VerticalCenter">
                  <WhileTrue>
                    <Change greenScaling.Factor="7" Duration="0.25" Easing="QuadraticOut" Delay="0.20" />  
                    <Change secondHeader.TextColor="#fff" Duration="0.25" Delay="0.20" />
                    <Change connect.Color="#fff" Duration="0.25" Delay="0.35" />
                    <Change greenColor.Color="#8cb542" Duration="0.25" Easing="QuadraticOut" Delay="0.20" />
                    <Change secondBackgroundColor.Color="#8cb542" Duration="0.05" Delay="0.35" />
                    <Change greenCircleTranslation.X="19" Duration="0" DurationBack="0" Delay="0.25" Easing="QuadraticInOut"/> 
                </WhileTrue>
            </MySwitch>
            <Circle>
                <Translation ux:Name="greenCircleTranslation" X="-19" />
                <SolidColor ux:Name="greenColor" Color="#ffffffff" />
                <Scaling ux:Name="greenScaling" Factor="0" />
            </Circle>
        </Panel>
    </StackPanel>
</DockPanel>