XF: Custom Control – LabeledSwitch
Lesedauer: 3 Minuten

Wenn man mit Xamarin.Forms arbeitet, um eine mobile App für Android und/oder iOS zu entwickeln, so kann man bereits auf eine Vielzahl von vordefinierten Controls zurückgreifen, welche sich einfach verwenden lassen. Aber manchmal kommt man in die Situation, wo diese vordefinierten Controls nicht mehr passen und man muss sich eine Alternative überlegen. In diesem Beitrag möchte ich nun zeigen, wie man aus den Standard-Controls ein neues Control für Android und iOS erstellen kann. Dabei handelt es sich um den LabeledSwitch. Dabei handelt es sich um einen Switch, welcher mit zwei Labels versehen ist, welche einen Titel und eine kurze Beschreibung anzeigen können.

Xamarin.Forms besitzt bereits ein Control, welches auf den Namen Switch hört. Wir wollen nun dieses Control erweitern, so dass wir einen Titel und einen Untertitel oder Beschreibung übergeben können und wenn der Nutzer auch auf dieses Label klickt, so wird der Switch entsprechend getoggelt.

Hier nun ein Screenshot vom fertigen LabeledSwitch für Xamarin.Forms:

Beginnen wir nun mit der Umsetzung. Wie bereits angekündigt, besteht der LabeledSwitch aus zwei Labels und einem Switch mit verschiedenen Styles. Hierzu erstellen wir uns zunächst eine ContentView und nennen diese LabeledSwitch.

Beginnen wir nun mit dem Layout. Hierzu öffnen wir die Datei LabeledSwitch.xaml und ersetzen den Content mit einem zweispaltigem Grid. In der ersten Spalte befindet sich ein StackLayout für die beiden Labeles und in der zweiten Spalte befindet sich dann der Switch. Gleichzeitig setzen wir die notwendigen Properties bereits, obwohl wir die BindableProperties erst später hinzufügen. Zu beachten ist, dass wir dem Control die x:Name-Eigenschaft auf LabeledSwitchRoot setzen, damit das Binding funktioniert.

<ContentView.Content>
    <Grid InputTransparent="True">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>

        <StackLayout Spacing="0"
                     Grid.Row="0"
                     VerticalOptions="Center">
            <Label Text="{Binding Title, Source={x:Reference LabeledSwitchRoot}}" />
            <Label Text="{Binding Detail, Source={x:Reference LabeledSwitchRoot}}" />
        </StackLayout>

        <Switch IsToggled="{Binding IsChecked, Source={x:Reference LabeledSwitchRoot},
                              Mode=TwoWay}"
                VerticalOptions="Center"
                Grid.Column="1" />

    </Grid>
</ContentView.Content>

Nun erstellen wir die notwendigen BindableProperties in der Code-Behind-Datei von unserem LabeledSwitch. Wir benötigen zwei Strings für den Titel und das Detail und ein Bool-Wert um den Zustand des Switchs nach außen zu reichen.

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class LabeledSwitch : ContentView
{
    public static readonly BindableProperty TitleProperty =
       BindableProperty.Create(nameof(Title), typeof(string), typeof(LabeledSwitch));

    public string Title
    {
        get => (string)GetValue(TitleProperty);
        set => SetValue(TitleProperty, value);
    }


    public static readonly BindableProperty DetailProperty =
        BindableProperty.Create(nameof(Detail), typeof(string), typeof(LabeledSwitch));

    public string Detail
    {
        get => (string)GetValue(DetailProperty);
        set => SetValue(DetailProperty, value);
    }


    public static readonly BindableProperty IsCheckedProperty =
        BindableProperty.Create(nameof(IsChecked), typeof(bool), typeof(LabeledSwitch));

    public bool IsChecked
    {
        get => (bool)GetValue(IsCheckedProperty);
        set => SetValue(IsCheckedProperty, value);
    }


    public LabeledSwitch()
    {
        InitializeComponent();
    }
}

Nun können wir bereits einen ersten Blick auf unsere aktuelle Implementierung werfen, zum Beispiel auf Android. Wir sehen bereits den Titel, das Detail und auch den Switch, aber wir können derzeit den Switch nicht bedienen und Titel und Detail unterscheiden sich auch noch nicht im Aussehen.

Also müssen wir das noch korrigieren. Wir fügen einen GestureRecognizer zu unserer ContentView hinzu, damit wir bei jedem Klick den Zustand des Switches manipulieren können.

<ContentView.GestureRecognizers>
    <TapGestureRecognizer Tapped="OnTapped" />
</ContentView.GestureRecognizers>

Nun müssen wir natürlich noch OnTapped in unserer Code-Behind-Datei implementieren. Hierzu müssen wir nur den Wert unserer IsChecked-Property verändern.

private void OnTapped(object sender, EventArgs e)
{
    IsChecked = !IsChecked;
}

Nun fehlt nur noch das Styling für die beiden Labels. Hierzu fügen wir ein paar Ressourcen ein, welche wir dann auf die beiden Labels anwenden.

<ContentView.Resources>
    <ResourceDictionary>
        <Style x:Key="TitleLabelStyle"
               TargetType="Label">
            <Setter Property="FontSize"
                    Value="15" />
            <Setter Property="TextColor"
                    Value="#000000" />
        </Style>

        <Style x:Key="DetailLabelStyle"
               TargetType="Label">
            <Setter Property="FontSize"
                    Value="13" />
            <Setter Property="TextColor"
                    Value="#A4A4A4" />
        </Style>
    </ResourceDictionary>
</ContentView.Resources>

Hier ist nun also das Endergebnis. Mit nur ein paar Zeilen Code haben wir ein eigenes Control erschaffen, welches wir jederzeit wiederverwenden können.

Natürlich kann das Control jetzt noch erweitert werden. Zum Beispiel könnte man die Styling-Informationen auch über BindableProperties abbilden, so dass man einfach von außen das Aussehen manipulieren kann. Ebenso könnte man darüber nachdenken, dass sich der Switch auf der linken Seite und die Texte auf der rechten Seite befindet. Der Fantasie sind hier keine Grenzen gesetzt.

Den gesamten Code findet ihr auf GitHub und kann als Grundlage für eigene Erweiterungen des Controls dienen. Viel Spaß beim Programmieren.

Foto-Galerie App mit Xamarin.Forms Material Icons für alle Lebenslagen Xamarin.Forms: Welche NuGet Pakete verwende ich?