WordPress-Seite als Xamarin.Forms App – Teil 3

Vor mehr als zwei Jahren habe ich euch bereits WordPressXF vorgestellt. Mein Kollege Thomas Pentenrieder hat eine .NET Library geschrieben, um auf einen WordPress-Blog von einer .NET App zugreifen zu können. Er hat sich dann dann primär um eine  UWP-Version gekümmert, welche die WordPressPCL-Library in Action zeigt. Ich habe mich selbst um eine Xamarin.Forms Version gekümmert. Nun dachte ich mir, dass man doch einmal die Solution updaten könnte, um so die neusten Xamarin.Forms Features verwenden zu können. Da ich jetzt nicht einfacher nur das Projekt updaten wollte, habe ich mir gedacht, dass die Umsetzung in Form von mehreren Blog-Beiträgen passiert und ich euch so zeige, wie eine kleine App entsteht, welche in der Lage ist die Beiträge eines Blogs unter Android und iOS an zu zeigen. Im dritten Teil wollen wir nun den gesamten Blog-Beitrag anzeigen lassen, in dem wir von der Übersichtseite auf eine Detailseite navigieren.

Ausgangslage bildet die Solution, welche im zweiten Teil entwickelt worden ist. Ihr bekommt die Solution entweder im alten Beitrag oder ansonsten über den folgenden Download-Button.

Wir beginnen zunächst damit unsere HtmlTools-Klasse zu erweitern, so dass wir den Content eines Blog-Beitrags so aufarbeiten, dass eine WebView innerhalb der App den Inhalt anzeigen kann. Ich werden den Code an dieser Stelle nicht zeigen, da wir im großen und ganzen nur ein HTML-Gerüst bauen und den Blog-Beitrag als Body hier einfügen. Den Code könnt ihr euch am Ende des Beitrags herunterladen und in euer Projekt übernehmen.

Damit die WebView später unser HTML-Gerüst anzeigen kann, schreiben wir einen HtmlStringToHtmlWebViewSourceConverter, welcher uns eine HtmlWebViewSource erzeugt.

public class HtmlStringToHtmlWebViewSourceConverter : IValueConverter
{
    public object Convert(object value, Type targetType, 
        object parameter, CultureInfo culture)
    {
        if (value is Post post)
        {
            var htmlContent = HtmlTools.CreateHtmlFromPost(post);
            return new HtmlWebViewSource { Html = htmlContent };
        }

        return string.Empty;
    }

    public object ConvertBack(object value, Type targetType, 
        object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Ebenso erzeugen wir ein neues Control mit dem Namen ExternalWebView. Hierbei handelt es sich um eine simple Erweiterung der bestehenden WebView, welche es uns ermöglicht Links im Systembrowser zu öffnen. Dazu nutzen wir die Browser-Klasse des Xamarin.Essentials Packages.

public class ExternalWebView : WebView
{
    public ExternalWebView()
    {
        Navigating += ExternalWebViewOnNavigating;
    }

    private async void ExternalWebViewOnNavigating(object sender, 
        WebNavigatingEventArgs e)
    {
        // check that Url is available
        if (e == null 
            || string.IsNullOrEmpty(e.Url))
            return;

        // check if URL doesn't start with http or https
        if (!e.Url.StartsWith("http") 
            || !e.Url.StartsWith("https"))
            return;

        // check for youtube link
        if (e.Url.StartsWith("https://www.youtube.com/"))
            return;

        // check for twitter link
        if (e.Url.StartsWith("https://syndication.twitter.com/") 
            || e.Url.StartsWith("https://platform.twitter.com"))
            return;

        // cancel WebView navigation
        e.Cancel = true;

        // open external browser to show website
        await Browser.OpenAsync(e.Url, BrowserLaunchMode.External);
    }
}

Damit können wir uns jetzt schon um die Page für den Blog-Beitrag anlegen. Diese Seite benutzt die erstelle ExternalWebView und den Converter, um den eigentlichen Inhalt anzuzeigen.

<?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:converters="clr-namespace:XFWordPress.Converters"
             xmlns:controls="clr-namespace:XFWordPress.Controls"
             x:Class="XFWordPress.Views.PostDetailPage"
             Title="{Binding SelectedPost.Title.Rendered, 
                Converter={StaticResource HtmlStringToDecodedStringConverter}}">

    <ContentPage.Resources>
        <ResourceDictionary>
            <converters:HtmlStringToHtmlWebViewSourceConverter 
                x:Key="HtmlStringToHtmlWebViewSourceConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <ContentPage.Content>
        <controls:ExternalWebView 
            Source="{Binding SelectedPost, 
                Converter={StaticResource HtmlStringToHtmlWebViewSourceConverter}}" />
    </ContentPage.Content>

</ContentPage>

Damit wir jetzt auf unsere neue PostDetailPage navigieren können, müssen wir die Page entsprechend im Bootstrapper registrieren und unser PostControl erweitern. Innerhalb des PostControls fügen wir dem Frame einen GestureRecognizer hinzu, damit wir auf das Klicken durch den Nutzer registrieren können. Innerhalb der Methode verwenden wir die Xamarin Animation ScaleTo, um eine Pressed-Animation anzuzeigen. Anschließend rufen wir einen Command in unserem ViewModel auf, welche nicht nur den aktuellen Beitrag speichert, sondern auch die Navigation auf die PostDetailPage übernimmt.

private async void FrameOnTapped(object sender, EventArgs e)
{
    // get View object from sender
    var viewSender = sender as View;
    if (viewSender == null)
        return;

    // get MeasurementItem from BindingContext
    var post = viewSender.BindingContext as Post;
    if (post == null)
        return;

    // show animation
    var scale = viewSender.Scale;
    await viewSender.ScaleTo(scale * 0.95, 50);
    await viewSender.ScaleTo(scale, 50);

    // click logic
    await ServiceLocator
                .Current
                .GetInstance<PostsViewModel>()
                .SetSelectedPostAsyncCommand.ExecuteAsync(post);
}

An dieser Stelle noch die Anpassungen am PostViewModel. Ich habe hier nicht das gesamte ViewModel aufgeführt, sondern nur die notwendigen Änderung.

private Post _selectedPost;
public Post SelectedPost
{
    get => _selectedPost;
    set { _selectedPost = value; OnPropertyChanged(); }
}

// CODE

private AsyncRelayCommand<Post> _setSelectedPostAsyncCommand;
public AsyncRelayCommand<Post> SetSelectedPostAsyncCommand 
            => _setSelectedPostAsyncCommand 
            ?? (_setSelectedPostAsyncCommand
                = new AsyncRelayCommand<Post>(SetSelectedPostASync));

// CODE

private async Task SetSelectedPostASync(Post selectedPost)
{
    SelectedPost = selectedPost;
    await NavigationService
                .NavigateToAsync(NavigationTarget.PostDetailPage);
}

Damit sind wir mit den notwendigen Änderungen bereits durch und können die App einmal starten. Die folgende Animation zeigt nun, wie wir von der Übersichtsseite die Detailseite öffnen und den vollständigen Blog-Beitrag anzeigen können.

Über den folgenden Download-Button könnt ihr euch den aktuellen Stand des Projekts herunterladen, so dass ihr die gemachten Änderungen mit eurem eigenen Blog ausprobieren könnt.

Im nächsten Beitrag der Serie werden wir uns um die Kommentare zu unserem Blog-Beitrag kümmern, so dass wir diese ebenfalls anzeigen können.

WordPress-Seite als Xamarin.Forms App – Teil 4 Xamarin.Forms: Bilder aufnehmen bzw. auswählen Lokales NuGet Package in Projekt integrieren