Pokédex – Kleines Xamarin.Forms Projekt – Teil 1

Wie bereits im vergangenen Jahr habe ich mir überlegt das Jahr wieder mit einem kleinen Projekt abzuschließen. Dieses Mal habe ich mir überlegt einen kleinen Pokédex zu entwickeln. Dieser greift auf eine öffentliche API zu, um Informationen über die verschiedenen Pokémon zu erhalten. Anschließend sollen diese lokal in der App in einer Datenbank gespeichert und visualisiert werden. Dies ist der erste Teil einer kleinen Serie und beschreibt den generellen Aufbau der Xamarin.Forms App.

Zunächst öffnen wir Visual Studio und erstellen eine neue Xamarin.Forms App. Dazu drücken wir auf Create a new project in der Übersicht und suchen anschließend Mobile App (Xamarin.Forms) als Vorlage. Als Projektnamen geben wir Pokedex ein. Bei Bedarf kann der Speicherort noch angepasst werden.

Im nächsten Schritt wählen wir Blank und setzen noch das Häkchen bei Windows (UWP), bevor wir mit einem Klick auf Create die eigentliche Solution anlegen können.

Wir müssen nun zunächst einige NuGet-Pakete installieren. Dazu klicken wir mit der rechten Maustaste auf die Solution und wählen den Eintrag Manage NuGet Packages for Solution aus dem Kontextmenü aus. Anschließend auf dem Tab Updates wechseln und die angezeigten Updates installieren.

Nun wechseln wir in den Bereich Browse und fügen noch ein paar notwendige Packages hinzu. Dazu gehören LiteDB, Microsoft.Extensions.DependencyInjection, Microsoft.Extensions.DependencyInjection.Abstractions und Newtonsoft.Json. Diese Pakete müssen nur dem .NET Standard-Projekt hinzugefügt werden. Außerdem werden noch die Pakete Rg.Plugins.Popup und Xamarin.CommunityToolkit in alle Projekte installiert. Leider gibt es bei dem Rg.Plugins.Popup-Projekt derzeit ein kleines Problem, so dass hier nur die Version 2.0.0.13 installiert werden kann, da ansonsten die UWP-Version nicht funktioniert. Außerdem wir noch Xamarin.Forms.Visual.Material benötigt, welches jedoch nur in das Android- und das iOS-Projekt installiert werden muss.

Nun müssen wir die einzelnen Packages noch initialisieren. Wir beginnen zunächst mit Android. Dafür öffnen wir die Datei MainActivity.cs und ergänzen die OnCreate-Methode.

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);

    Rg.Plugins.Popup.Popup.Init(this);

    Xamarin.Essentials.Platform.Init(this, savedInstanceState);
    global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
    Xamarin.Forms.FormsMaterial.Init(this, savedInstanceState);
    LoadApplication(new App());
}

Als nächstes geht mit iOS weiter. Dafür öffnen wir nun die Datei AppDelegate.cs und passen hier die Methode FinishedLaunching an.

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    Rg.Plugins.Popup.Popup.Init();

    global::Xamarin.Forms.Forms.Init();
    Xamarin.Forms.FormsMaterial.Init();
    LoadApplication(new App());

    return base.FinishedLaunching(app, options);
}

Nun folgt noch zu guter Letzt UWP. Hierfür öffnen wir die Datei App.xaml.cs im UWP-Projekt und passen die OnLaunched-Methode entsprechend an.

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    Frame rootFrame = Window.Current.Content as Frame;

    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active
    if (rootFrame == null)
    {
        // Create a Frame to act as the navigation context and navigate to the first page
        rootFrame = new Frame();

        rootFrame.NavigationFailed += OnNavigationFailed;

        Rg.Plugins.Popup.Popup.Init();

        Xamarin.Forms.Forms.Init(e, Rg.Plugins.Popup.Popup.GetExtraAssemblies());

        if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
        {
            //TODO: Load state from previously suspended application
        }

        // Place the frame in the current Window
        Window.Current.Content = rootFrame;
    }

    if (rootFrame.Content == null)
    {
        // When the navigation stack isn't restored navigate to the first page,
        // configuring the new page by passing required information as a navigation
        // parameter
        rootFrame.Navigate(typeof(MainPage), e.Arguments);
    }
    // Ensure the current window is active
    Window.Current.Activate();
}

Damit sind die Initialisierungen für die einzelnen Plattformen abgeschlossen und wir können uns nun um weitere Anpassungen kümmern. Auch wenn unsere App zunächst nur auf Englisch zur Verfügung stehen wird, wollen wir uns um eine Lokalisierung kümmern, so dass im weiteren Verlauf vielleicht weitere Sprachen hinzugefügt werden können. Dazu erstellen wir in unserem .NET Standard Projekt einen Ordner Resources und darin eine Resource-Datei. Hierfür einfach nach res suchen und die Datei AppResources.resx nennen.

Nun einfach die Datei AppResources.resx öffnen und einen Eintrag OverviewPageTitle mit dem Wert Pokédex Overview anlegen. Nun öffnen wir die Datei App.xaml.cs und initialisieren den LocalizationResourceManager.

public App()
{
    InitializeComponent();

    LocalizationResourceManager.Current.Init(AppResources.ResourceManager);

    MainPage = new MainPage();
}

Da wir gegebenenfalls planen, die App in verschiedenen Styles anzubieten, wollen wir uns auch gleich um das Styling kümmern und eine einzelne Style-Datei anlegen, welche wir dann später erweitern und auch austauschen können. Dazu erstellen wir einen neuen Ordner Styles und in diesem Ordner dann eine ContentPage mit den Namen Styles.xaml. Wir müssen nun den Root-Knoten von ContentPage auf ResourceDictionary ändern und ebenso in der Code-Behind Datei. Anschließend können wir die ersten Farben und Styles für unsere kleine App definieren.

<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    x:Class="Pokedex.Styles.Styles">

    <!-- Colors -->
    <Color x:Key="NavigationBarBackgroundColor">#1E3D59</Color>
    <Color x:Key="NavigationBarTextColor">#FFFFFF</Color>

    <Color x:Key="DefaultTextColor">#DE000000</Color>


    <!-- Styles -->
    <Style TargetType="NavigationPage">
        <Setter Property="BarBackground"
                Value="{StaticResource NavigationBarBackgroundColor}" />
        <Setter Property="BarTextColor"
                Value="{StaticResource NavigationBarTextColor}" />
    </Style>

    <Style TargetType="Label">
        <Setter Property="TextColor"
                Value="{StaticResource DefaultTextColor}" />
    </Style>

</ResourceDictionary>

Damit diese Styles nun auch verwendet werden können, öffnen wir die App.xaml-Datei und wir mergen hier nun unser neues Styles-Dictionary hinzu.

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:styles="clr-namespace:Pokedex.Styles"
             x:Class="Pokedex.App">
    
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <styles:Styles />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
    
</Application>

Wir haben den Einrichtungsprozess nun fast abgeschlossen. Als letztes erstellen wir noch einen neuen Ordner Views und erstellen darin eine ContentPage mit dem Namen OverviewPage.xaml. Hier wollen wir nun unsere Lokalisierung verwenden, um die Title-Property zu setzen. Dafür nutzen wir die TranslateExtension aus dem Xamarin Community Toolkit.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="Pokedex.Views.OverviewPage"
             Title="{xct:Translate OverviewPageTitle}">

    <ContentPage.Content>
        <StackLayout>
            <Label Text="Welcome to Xamarin.Forms!"
                   VerticalOptions="CenterAndExpand"
                   HorizontalOptions="CenterAndExpand" />
        </StackLayout>
    </ContentPage.Content>

</ContentPage>

Nun können wir die MainPage löschen und passen die Startseite unserer App in der App.xaml.cs an.

public App()
{
    InitializeComponent();

    LocalizationResourceManager.Current.Init(AppResources.ResourceManager);

    MainPage = new NavigationPage(new OverviewPage());
}

Damit sind wir jetzt tatsächlich für diesen Teil der Serie fertig und können uns das Ergebnis einmal anschauen. Ich habe dafür die UWP-Version gestartet.

Das aktuelle Projekt könnt ihr über den folgenden Download-Button herunterladen. Freut euch nun schon auf den nächsten Teil, in dem wir beginnen wollen die notwendigen Daten von der API abzurufen und entsprechend anzuzeigen.

Hot Restart: iOS-Apps unter Windows deployen SignalR: Echtzeitkommunikation zwischen Backend und Frontend XFWeather – Kleine Xamarin.Forms-App