Dev Stories

WordPressReader (Standard) version 1.2.0 focuses on improving performance

WordPressReader (Standard) version 1.2.0 focuses on improving performance

The update contains several improvements to the HttpClientimplementation, which should improve performance of the library a lot:

  • all handlers now use one static HttpClientinstance. HttpClientis built to use it that way, and there is also no problem with multiple request handled by that instance. So everyone should use it in that way
  • the library is now actively enforcing gzip/deflate compression to make responses faster, especially when requesting bigger lists of items (if you want do deactivate it on the managed implementation, pass in falsewith the new useCompressionparameter with your call to the SetupClientmethod
  • deserialization by default now happens directly from the HttpResponsestream instead of first converting it to a string
  • added an API to pass in your own HttpClientinstance instead of the managed one (you could even use one static instance for your whole app this way to improve performance even further). To do so, just use the newly added method SetCustomClient(HttpClient client)on your handler instances.

I made this changes working without breaking existing implementations. Just by updating the library, you will get a better performance.

I also updated the source code on GitHub. If you just want to update the library, the update is already available on NuGet.

Happy coding, everyone!

P.S.
If you want to see the library in a live app, you can download my official blog reader app (which is written around it as a Xamarin.Formsapp) here:

iOS  Android  Windows 10

Posted by msicc in Dev Stories, 1 comment
Xamarin Android: asymmetric encryption without any user input or hardcoded values

Xamarin Android: asymmetric encryption without any user input or hardcoded values

The problem

Android is often said to be one of the most unsecure platforms one can use. This problem is home-made, as there is still a lot of fragmentation. There are thousand of models that do not get the latest updates and security patches, mostly because OEMs (seem to) not care (for different reasons, biggest reason is of course money). On the other side, there are still developers that save user names and passwords in plain text (which is the worst) or have hardcoded values in their code that make it way to easy to compromise encrypted data.

In the past, a lot of us developers made some of those mistakes. Be it because most of the popular samples around the web use hardcoded values (e. g. for the IV) or because of blindly copy & pasting from other websites or by using badly implemented libraries. Everyone should stop using these and use methods that are more secure. One of the most secure ways to do so is to use asymmetric encryption with a private/public key pair. The Android OS is doing a lot to help us generating such a key pair, and I am going to show you how to use it.

AndroidKeyStore

As the name already implies, Android uses the AndroidKeyStoreto keep keys secure. The AndroidKeyStore is derived from the Java Security implementations and provides:

  • generation of keys and key pairs
  • key material that is maintained out of any application process
  • the key material can be bound to security hardware
  • additional usage limits are implemented in the OS
  • certificate store

Read more on that topic in the official Android documentation.

Encryption with a key pair explained

If you want to handle sensitive data securely in your app (and you should), there are only two ways. Either you are not saving them (which will often keep users not returning to your app or even uninstalling it just because they must type them in over and over again) or encrypt these data before saving it. One of the more secure ways to encrypt data is to use a private/public key pair, also known as asymmetric encryption (because you use one key for encryption and the other for decryption).

The private key is only known to the issuer of the key. In the case of Android, it is the OS or the security hardware that is in built into the device. The private key should always be private, and Android does handle this for us. The public key can be given to external parties (like us developers) to use them for decryption of sensitive data. The OS adds an additional layer and makes sure that only your app(s) are able to use the public key (aka ‘key access validation’).

Of course Google does not make all and everything about that encryption and validation process public (for obvious reasons).

In this post, I will focus on the creation of such a key pair, on how to retrieve a key from the AndroidKeyStoreand in the end, we will of course encrypt some data. My implementation is based on this article series, which provides a whole lot of explanation. If you want to know more about this topic, I absolutely recommend reading it. I will not go to deep into details, if you want to know more, once again, just read the articles linked above.

Let the OS create a key pair for you

The Android OS has two generators – a KeyGenerator and a KeyPairGenerator. The KeyGenerator provides a single key, while we will focus on the KeyPairGenerator, which will give us a brand new private/public key pair.

The first step is to initialize the KeyStore itself, which I am doing in the constructor of my helper class:

       public PlatformEncryptionKeyHelper(Context context, string keyName)
       {
           _context = context;
           _keyName = keyName.ToLowerInvariant();

           _androidKeyStore = KeyStore.GetInstance(KEYSTORE_NAME);
           _androidKeyStore.Load(null);
       }

The essential step here is to load the instance with null, otherwise all other operations will not work. You should also never change the keystore’s name unless you know exactly what you are doing.

Now that we have the KeyStore initialized, let’s go ahead and create a new key pair. As I support Android 5.0 (Lollipop) in my apps, I have also a fallback in place, as the current iteration is only available for device with Android 6 (Marshmallow) and above. Here is the code:

        public void CreateKeyPair()
        {
            DeleteKey();

            KeyPairGenerator keyGenerator =
                KeyPairGenerator.GetInstance(KeyProperties.KeyAlgorithmRsa, KEYSTORE_NAME);

            if (Build.VERSION.SdkInt >= BuildVersionCodes.JellyBeanMr2 &&
                Build.VERSION.SdkInt <= BuildVersionCodes.LollipopMr1)
            {
                var calendar = Calendar.GetInstance(_context.Resources.Configuration.Locale);
                var endDate = Calendar.GetInstance(_context.Resources.Configuration.Locale);
                endDate.Add(CalendarField.Year, 20);

                //this API is obsolete after Android M, but I am supporting Android L
#pragma warning disable 618
                var builder = new KeyPairGeneratorSpec.Builder(_context)
#pragma warning restore 618
                              .SetAlias(_keyName).SetSerialNumber(BigInteger.One)
                              .SetSubject(new X500Principal($"CN={_keyName} CA Certificate"))
                              .SetStartDate(calendar.Time)
                              .SetEndDate(endDate.Time).SetKeySize(KeySize);

                keyGenerator.Initialize(builder.Build());
            }
            else if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
            {
                var builder =
                    new KeyGenParameterSpec.Builder(_keyName, KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt)
                        .SetBlockModes(KeyProperties.BlockModeEcb)
                        .SetEncryptionPaddings(KeyProperties.EncryptionPaddingRsaPkcs1)
                        .SetRandomizedEncryptionRequired(false).SetKeySize(KeySize);

                keyGenerator.Initialize(builder.Build());
            }

            keyGenerator.GenerateKeyPair();
        }

As you can see, the creation of such a key pair is way easier with Android 6 (Marshmallow) and above. I will focus on this part, details for the fallback solution can be found in the articles I linked above. I am requesting a RSA key pair for encryption and decryption, which needs to be specified explicitly. We are using the so called ‘ Electronic Codebook’ encryption mode, which will cut the data to encrypt into blocks that will be encrypted. Also important: the key’s size. A bigger key means more security, but also more time for operations done with it. Android defaults to a key size of 2048 bits, which provides a good average of security and execution time. With this method in place, we are already able to create a brand new key pair.

Note: The DeleteKey()method call beforehand just makes sure we have only one valid key pair with that name available. I am also following Google’s recommendations by calling it before creating a new key.

Retrieving the public key for encryption

Now that the AndroidKeyStoreholds a key pair for us, let us have a look on how to retrieve the public key, which is used for encryption:

public IKey GetPublicKey()
{
    if (!_androidKeyStore.ContainsAlias(_keyName))
        return null;

    return _androidKeyStore.GetCertificate(_keyName)?.PublicKey;
}

Android internally creates a self signed certificate for the key pair (that’s why we had to perform this action manually before Android 6 (Marshmallow). The API makes this visible to us in the case of the retrieval of the public key. Xamarin provides the IKey interface, which is once again inherited from the Java Security APIs.

Retrieving the private key for decryption

Of course, we want to decrypt the data we encrypted at some point. That is as easy as getting the public key:

public IKey GetPrivateKey()
{
    if (!_androidKeyStore.ContainsAlias(_keyName))
        return null;

    return _androidKeyStore.GetKey(_keyName, null);
}

As we did not set a password during the key pair creation, we are passing null in here to get our private key.

Deleting a key pair

There may be situations where you want to delete a key. The AndroidKeyStore has an API available for that as well. You may guess it, it is also very easy to use:

public bool DeleteKey()
{
    if (!_androidKeyStore.ContainsAlias(_keyName))
        return false;

    _androidKeyStore.DeleteEntry(_keyName);
    return true;
}

Usage

As you probably remember, I created a helper class for handling all things related to the AndroidKeyStore. Let’s have a look on how to encrypt and decrypt a string with the help of this class.

_encryptionKeyHelper = new PlatformEncryptionKeyHelper(Application.Context, KeyStoreName);
_encryptionKeyHelper.CreateKeyPair();

_privateKey = _encryptionKeyHelper.GetPrivateKey();
_publicKey = _encryptionKeyHelper.GetPublicKey();

After instantiating the helper class, we use the CreateKeyPair()method to get a key pair. In the full class I will share later in this post, I have another helper that will check if the key already exists. You can use this class to step over the creation part if there is already a key pair.

Now let’s see how encryption works:

//we used these values to create the keys
//now we need to tell the OS to use the same values during encryption/decryption
var transformation = "RSA/ECB/PKCS1Padding";

var stringToEncrypt = "This is a simple string for demo purposes only. Nothing special here.";

var cipher = Cipher.GetInstance(transformation);
cipher.Init(CipherMode.EncryptMode, _publicKey);

var encryptedData = cipher.DoFinal(Encoding.UTF8.GetBytes(stringToEncrypt));

We are using the Cipher class provided by Xamarin, which inherits from the Java Crypto API. The transformation string consists of “algorithm/mode/padding” and needs to be passed to the cipher instance. After specifying that we want to encrypt with the public key, the DoFinalmethod encrypts the string and returns it as a byte array, which can be saved pretty easy.

Decryption works in a similar way:

var transformation = "RSA/ECB/PKCS1Padding"; 

var cipher = Cipher.GetInstance(transformation);
cipher.Init(CipherMode.DecryptMode, _privateKey);

var decryptedBytes = cipher.DoFinal(encyrptedData);
var finalString = Encoding.UTF8.GetString(decryptedBytes);

Once again, we are using the Cipherclass. Remember to initialize the cipher instance once again, because we are using now the decryption mode. The DoFinalmethod will decrypt the encrypted byte array, which can be turned into a string once again.

I did not create a sample project this time. However, the full helper class is available here on my GitHub account as Gist.

Xamarin.Forms tipp: You can make this class available by extracting an interface from it and use the DependencyService to get access from your forms project if necessary.

Conclusion

The security of your user’s data should always be something you are concerned about. With this little helper, we are using the OS (and in some cases also the device) to secure data in your Xamarin.Android app. Sadly, a lot of samples require user interaction or even use some hardcoded values. This should not be used in a production app. Feel free to use my helper class as a starting point.

As always, I hope this post is helpful for some of you. In the next post, I will show you how to use a similar mechanism in your Xamarin.iOS app.

Until then, happy coding, everyone!

 

Posted by msicc in Android, Dev Stories, Xamarin, 1 comment
#XfQaD: Limit maximum lines of Label and indicate text truncation

#XfQaD: Limit maximum lines of Label and indicate text truncation

The problem

Xamarin.Forms.Labelhas a common set of properties we can use to configure how our text is shown. However, it does miss a property to limit the maximum of text lines and a proper indication of eventually truncated text. Knowing that UWP, Android and iOS have working and easy-to-use implementations on their platform controls used for the Xamarin.Forms.Label, there is only one solution to the problem: exposing a custom control and its platform renderers. That’s what we are going to do in this #XfQaD.

XfMaxLines Label implementation

Let’s have a look at the Xamarin.Forms implementation first. I am just adding a BindablePropertyto a derived class implementation to define the maximum of lines I want to see:

public class XfMaxLinesLabel : Label
{
    public XfMaxLinesLabel(){ }

    public static BindableProperty MaxLinesProperty = BindableProperty.Create("MaxLines", typeof(int), typeof(XfMaxLinesLabel), int.MaxValue, BindingMode.Default);

    public int MaxLines
    {
        get => (int)GetValue(MaxLinesProperty);
        set => SetValue(MaxLinesProperty, value);
    }
}

UWP

The UWP renderer uses the TextBlock properties MaxLinesto limit the amount of shown lines, while the TextTrimmingproperty is set to ellipsize the last word before reaching the limit. The implementation is pretty straight forward:

[assembly: ExportRenderer(typeof(XfMaxLinesLabel), typeof(XfMaxLinesLabelRenderer))]
namespace MaxLinesLabel.UWP
{
    public class XfMaxLinesLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            if (((XfMaxLinesLabel)e.NewElement).MaxLines == -1 || ((XfMaxLinesLabel)e.NewElement).MaxLines == int.MaxValue)
                return;

            this.Control.MaxLines = ((XfMaxLinesLabel)e.NewElement).MaxLines;
            this.Control.TextTrimming = Windows.UI.Xaml.TextTrimming.WordEllipsis;
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (e.PropertyName == XfMaxLinesLabel.MaxLinesProperty.PropertyName)
            {
                if (((XfMaxLinesLabel)this.Element).MaxLines == -1 || ((XfMaxLinesLabel)this.Element).MaxLines == int.MaxValue)
                    return;

                this.Control.MaxLines = ((XfMaxLinesLabel)this.Element).MaxLines;
                this.Control.TextTrimming = Windows.UI.Xaml.TextTrimming.WordEllipsis;
            }
        }
    }
}

Android

The Android implementation uses the MaxLinesproperty of Android’s TextView control to limit the maximum visible lines. The Ellipsizeproperty is used to show the three dots for truncation at the end of the last visible line. Once again, pretty straight forward.

[assembly: ExportRenderer(typeof(XfMaxLinesLabel), typeof(XfMaxLinesLabelRenderer))]
namespace MaxLinesLabel.Droid
{
    class XfMaxLinesLabelRenderer : LabelRenderer
    {
        public XfMaxLinesLabelRenderer(Context context) : base(context)
        {
        }


        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            if (((XfMaxLinesLabel)e.NewElement).MaxLines == -1 || ((XfMaxLinesLabel)e.NewElement).MaxLines == int.MaxValue)
                return;
            this.Control.SetMaxLines(((XfMaxLinesLabel)e.NewElement).MaxLines);
            this.Control.Ellipsize = TextUtils.TruncateAt.End;
        }


        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (e.PropertyName == XfMaxLinesLabel.MaxLinesProperty.PropertyName)
            {
                if (((XfMaxLinesLabel)this.Element).MaxLines == -1 || ((XfMaxLinesLabel)this.Element).MaxLines == int.MaxValue)
                    return;
                this.Control.SetMaxLines(((XfMaxLinesLabel)this.Element).MaxLines);
                this.Control.Ellipsize = TextUtils.TruncateAt.End;
            }
        }
    }
}

iOS

Like Android and Windows, also the UILabel control on iOS has a MaxLinesproperty. You’re right, we’ll use this one to limit the count of visible lines. Using the LineBreakModeproperty, we can automate the text truncation indicator equally easy as on Android and UWP:

[assembly: ExportRenderer(typeof(XfMaxLinesLabel), typeof(XfMaxLinesLabelRenderer))]
namespace MaxLinesLabel.iOS
{
    public class XfMaxLinesLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            if (((XfMaxLinesLabel)e.NewElement).MaxLines == -1 || ((XfMaxLinesLabel)e.NewElement).MaxLines == int.MaxValue)
                return;

            this.Control.Lines = ((XfMaxLinesLabel)e.NewElement).MaxLines;
            this.Control.LineBreakMode = UILineBreakMode.TailTruncation;
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (e.PropertyName == XfMaxLinesLabel.MaxLinesProperty.PropertyName)
            {
                if (((XfMaxLinesLabel)this.Element).MaxLines == -1 || ((XfMaxLinesLabel)this.Element).MaxLines == int.MaxValue)
                    return;

                this.Control.Lines = ((XfMaxLinesLabel)this.Element).MaxLines;
                this.Control.LineBreakMode = UILineBreakMode.TailTruncation;
            }
        }
    }
}

Conclusion

As you can see, it is pretty easy to create a line limited, truncation indicating custom Label for your Xamarin.Forms app. The implementation is done in a few minutes, but it makes writing your cross platform app a bit easier. I don’t know why this is not (yet) implemented in current Xamarin.Forms iterations, but I do hope they’ll do so to further reduce the number of needed custom renderers.

In the meantime, feel free to check the sample code on GitHub and use it in your apps. As always, I hope this post is helpful for some of you.

Happy coding, everyone!

Posted by msicc in Android, Dev Stories, iOS, UWP, Xamarin, 1 comment
WordPressReader (Standard) library is now available on Nuget

WordPressReader (Standard) library is now available on Nuget

I am happy to announce my new WordPressReader (Standard) library that is available on NuGet now. It is written in .netStandard 1.4 which enables you to use it in a lot of of places. As the name suggests, it focuses on reading tasks against the WordPress API.

Features:

  • get, search and filter posts
  • get pages
  • get and filter categories
  • get, search and filter tags
  • get comments
  • post anonymous comments (needs additional work on the WordPress site)
  • get basic user info

The library uses a generic WordPressEntity model implementation, which makes it easy to implement and extend. You can always get the raw json-value as well. The additional Error property on every model makes it easy to handle API errors properly. You can read the full documentation in the GitHub Wiki.

If you have problems with the library or want to contribute, you can do so on the GitHub repo.

Happy coding, everyone!

P.S.
If you want to see the library in a live app, you can download my official blog reader app (which is written around it) here:

iOS  Android  Windows 10

 

Posted by msicc in Dev Stories, 2 comments
#XfQaD: read package identity and version from platform project with Xamarin.Forms

#XfQaD: read package identity and version from platform project with Xamarin.Forms

All of my apps, no matter on which platform, need to know their version number (for displaying in app) and their package identifier (for opening them in their store). If you are following around for some time, you know I prefer own solutions in a lot of use cases – that’s why I created another #XfQaD for this task, even if there are plugins around for that.

The concept

Once again, I am utilizing the built-in Xamarin.FormsDependencyService for this task. So the concept is pretty easy:

  • interface that dictates the available options
  • platform implementations that execute the code and return the values I want

Let’s have a look at

The interface

namespace PackageInfo
{
    public interface IAppDataHelper
    {
        string GetApplicationPackageName();

        string GetApplicationVersion();

        string GetApplicationVersionName();
    }
}

The interface provides three string returning methods. As the versioning is different on all three platforms, I return two different version strings to cover that fact.

UWP implementation

The UWP implementation uses the Package class, which provides access to the package information, including those we are interested in. As the UWP has just one version type, it returns the same value for version and version name:

using PackageInfo.UWP;
using Windows.ApplicationModel;
using Xamarin.Forms;

[assembly: Dependency(typeof(AppDataHelper))]
namespace PackageInfo.UWP
{
    public class AppDataHelper : IAppDataHelper
    {
        private Package _package;

        public AppDataHelper()
        {
            _package = Package.Current;
        }

        public string GetApplicationPackageName()
        {
            return _package.Id.FamilyName;
        }

        public string GetApplicationVersion()
        {
            return  $"{_package.Id.Version.Major}.{_package.Id.Version.Minor}.{_package.Id.Version.Build}.{_package.Id.Version.Revision}";
        }

        public string GetApplicationVersionName()
        {
            return $"{_package.Id.Version.Major}.{_package.Id.Version.Minor}.{_package.Id.Version.Build}.{_package.Id.Version.Revision}";
        }
    }
}

Android implementation

The Android implementation uses the PackageManager class, which uses the GetPackageInfo method to provide the information about the currently installed package. As Android has a different version structure (see more info here), it returns two different strings for version and version name:

using Android.Content;
using Android.Content.PM;
using PackageInfo.Droid;
using Xamarin.Forms;

[assembly: Dependency(typeof(AppDataHelper))]
namespace PackageInfo.Droid
{
    public class AppDataHelper : IAppDataHelper
    {
        private readonly Context _context;
        private readonly PackageManager _packageManager;
        public AppDataHelper()
        {
            _context = Android.App.Application.Context;
            _packageManager = _context.PackageManager;
        }

        public string GetApplicationPackageName()
        {
            return _context.PackageName;
        }

        public string GetApplicationVersion()
        {
            return _packageManager.GetPackageInfo(_context.PackageName, 0).VersionCode.ToString();
        }

        public string GetApplicationVersionName()
        {
            return _packageManager.GetPackageInfo(_context.PackageName, 0).VersionName;
        }
    }
}

iOS implementation

Even iOS provides a way to get the package identity and version. It uses the NSBundle.MainBundle implementation to get the info. To get those we are interested in, we just query the InfoDictionarythe MainBundleholds:

using Foundation;
using PackageInfo.iOS;
using Xamarin.Forms;

[assembly: Dependency(typeof(AppDataHelper))]
namespace PackageInfo.iOS
{
    public class AppDataHelper : IAppDataHelper
    {
        private readonly NSDictionary _infoDictionary;

        public AppDataHelper()
        {
            _infoDictionary = NSBundle.MainBundle.InfoDictionary;
        }

        public string GetApplicationPackageName()
        {
            return _infoDictionary[new NSString("CFBundleIdentifier")].ToString();
        }

        public string GetApplicationVersion()
        {
            var appVersionString = NSBundle.MainBundle.ObjectForInfoDictionary("CFBundleShortVersionString").ToString();
            var appBuildNumber = NSBundle.MainBundle.ObjectForInfoDictionary("CFBundleVersion").ToString();

            return $"{appVersionString}.{appBuildNumber}";
        }

        public string GetApplicationVersionName()
        {
            return NSBundle.MainBundle.ObjectForInfoDictionary("CFBundleShortVersionString").ToString();
        }
    }
}

That’s all it takes to get your application’s package identity and version. You can have a look yourself in this GitHub sample and play around with it. If you want to extend and read more information, the above implementation is easily expandable.

As always, I hope this post will be helpful for some of you. Happy coding, everyone!

Posted by msicc in Android, Dev Stories, iOS, UWP, 0 comments
#XfQaD: Using ProgressRing for UWP and keep a single activity indicator API in Xamarin.Forms

#XfQaD: Using ProgressRing for UWP and keep a single activity indicator API in Xamarin.Forms

I recently recognized that I have written quite a few “Quick-and-Dirty”-solutions for Xamarin Forms that run well for most scenarios. There is a chance they will not work in all and every scenario, and therefore may need some more work at a later point. I am sharing them to bring the ideas to the community, and often these “QaDs” are enough one needs to solve one particular problem. As they do not fit well into my other series I am writing (“Xamarin Forms, the MVVMLight toolkit and I” for example), I gave them their own tag: #XfQaD.

The scenario

The first scenario may not be important to a lot of people, but I wanted to solve this rather small one quickly for me. The UWP implementation of Xamarin Forms’ ActivityIndicatoruses the ProgressBarinstead of a ring indicator like Android and iOS:

default activity indicator screenshots

image credits: Xamarin

While this will be fine in most cases, I had the problem of limited space, and I wanted a similar UI on all three platforms for that app. The UWP has a perfect matching native control, so I implemented my own ActivityIndicatorimplementation called LoadingRing. It uses the ProgressRingcontrol on UWP and keeps the default ActivityIndicatoron all other platforms. I also wanted to keep a single API I can use throughout my app without always thinking about the platform usings.

Implementation structure

The QaD-solution I came up with has a simple structure:

  • base class implementation providing the API for the custom renderer on UWP
  • the custom renderer in the UWP project
  • a catalyst class that unifies the different implementations

Let’s have a look into the code:

API for the custom renderer

The API for the custom render has the same properties as the Xamarin.Forms.ActivityIndicator has. They are BindableProperties, so they are perfectly prepared for MVVM. Here is all that we need in there:

public class ProgressRingIndicator : View
{
    public ProgressRingIndicator()
    {
        if (Device.RuntimePlatform != Device.UWP)
        {
            throw new NotSupportedException($"{nameof(ProgressRingIndicator)} is just for UWP, use {nameof(ActivityIndicator)} on {Device.RuntimePlatform}");
        }
    }

    public static readonly BindableProperty ColorProperty = BindableProperty.Create("Color", typeof(Color), typeof(ProgressRingIndicator), default(Color), BindingMode.Default);

    public Color Color
    {
        get => (Color)GetValue(ColorProperty);
        set => SetValue(ColorProperty, value);
    }

    public static readonly BindableProperty IsRunningProperty = BindableProperty.Create("IsRunning", typeof(bool), typeof(ProgressRingIndicator), default(bool), BindingMode.Default);

    public bool IsRunning
    {
        get => (bool)GetValue(IsRunningProperty);
        set => SetValue(IsRunningProperty, value);
    }
}

If you need more info on the implementation of BindableProperties, just have a look at the Xamarin.Forms documentation. Basically, they are what Windows developers know as DependencyProperty.

The renderer and two little extensions

One of the great things of Xamarin.Forms is the ability to use native controls via custom renderers. It makes implementing platform specific code easy while keeping the amount of shared code pretty high. As I know that also beginners read my posts, here is once again a link to the Xamarin documentation. Let’s have a look at the two little extension I mentioned first, as they make our renderer code more readable.

Xamarin.Forms and the UWP have different implementations of the Color structure (Xamarin | UWP). In order to connect them, we need to translate the Xamarin.Forms.Colorto a Windows.UI.Colorand pass the later one to a SolidColorBrushto give the ProgressRingthe color we want. The implementation is pretty straight forward:

public static class Extensions
{
    public static Color ToUwPColor(this Xamarin.Forms.Color color)
    {
        return Color.FromArgb(
            Convert.ToByte(color.A * 255),
            Convert.ToByte(color.R * 255),
            Convert.ToByte(color.G * 255),
            Convert.ToByte(color.B * 255));
    }

    public static SolidColorBrush ToUwpSolidColorBrush(this Xamarin.Forms.Color color)
    {
        return new SolidColorBrush(color.ToUwPColor());
    }
}

The Windows.UI.Color.FromArgbmethod is accepting only bytes as value, so we have to convert the Xamarin.Forms.Colorchannels to bytes and pass them along. With these extensions, we will have the color setting in the renderer in just one single line.

So let’s get finally to the renderer:

[assembly: ExportRenderer(typeof(ProgressRingIndicator), typeof(ProgressRingIndicatorRenderer))]
namespace [YourNameSpaceHere].UWP
{
    public class ProgressRingIndicatorRenderer : ViewRenderer<ProgressRingIndicator, ProgressRing>
    {
        private ProgressRing _progressRing;

        protected override void OnElementChanged(ElementChangedEventArgs<ProgressRingIndicator> e)
        {
            base.OnElementChanged(e);

            if (this.Control != null) return;

            _progressRing = new ProgressRing();

            if (e.NewElement != null)
            {
                _progressRing.IsActive = this.Element.IsRunning;
                _progressRing.Visibility = this.Element.IsRunning ? Visibility.Visible : Visibility.Collapsed;
                var xfColor = this.Element.Color;
                _progressRing.Foreground = xfColor.ToUwpSolidColorBrush();

                SetNativeControl(_progressRing);
            }
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (e.PropertyName == nameof(ProgressRingIndicator.Color))
            {
                _progressRing.Foreground = this.Element.Color.ToUwpSolidColorBrush();
            }

            if (e.PropertyName == nameof(ProgressRingIndicator.IsRunning))
            {
                _progressRing.IsActive = this.Element.IsRunning;
                _progressRing.Visibility = this.Element.IsRunning ? Visibility.Visible : Visibility.Collapsed;
            }

            if (e.PropertyName == nameof(ProgressRingIndicator.WidthRequest))
            {
                _progressRing.Width = this.Element.WidthRequest > 0 ? this.Element.WidthRequest : 20;
                UpdateNativeControl();
            }

            if (e.PropertyName == nameof(ProgressRingIndicator.HeightRequest))
            {
                _progressRing.Height = this.Element.HeightRequest > 0 ? this.Element.HeightRequest : 20;
                UpdateNativeControl();
            }
        }
    }
}

ViewRender<TElement, TNativeElement>enables us to use native controls in Xamarin.Forms, so we’re deriving from it. Like any custom renderer, our renderer overrides the OnElementChangedmethod to set the initial rendering values. The Controlproperty is the native control implementation, while the Xamarin.Forms control comes in via ElementChangedEventArgs.NewElementproperty, but you can also use the Elementproperty in most cases.

In order to react to changes of the different properties of the control, we need to handle the OnElementPropertyChangedevent. This event can fire quite often, so it makes absolutely sense to filter code execution to run only when a specific property change happens.

Bring back my single API

With the code above, I am already able to use the ProgressRingIndicator. However, I have to use the On<T>platform implementation everywhere to do so. As I already mentioned before, I want to have a single API when I use the control. To solve this problem, I created a catalyst class:

public class LoadingRing : ContentView
{
    public readonly ProgressRingIndicator UwpProgressRing;
    public readonly ActivityIndicator ActivityIndicator;

    public LoadingRing()
    {
        switch (Device.RuntimePlatform)
        {
            case Device.UWP:
                this.UwpProgressRing = new ProgressRingIndicator();
                this.UwpProgressRing.HorizontalOptions = LayoutOptions.FillAndExpand;
                this.UwpProgressRing.VerticalOptions = LayoutOptions.FillAndExpand;
                this.Content = this.UwpProgressRing;
                break;
            default:
                this.ActivityIndicator = new ActivityIndicator();
                this.ActivityIndicator.HorizontalOptions = LayoutOptions.FillAndExpand;
                this.ActivityIndicator.VerticalOptions = LayoutOptions.FillAndExpand;
                this.Content = this.ActivityIndicator;
                break;
        }

        SizeChanged += LoadingRing_SizeChanged;

    }

    private void LoadingRing_SizeChanged(object sender, EventArgs e)
    {
        switch (Device.RuntimePlatform)
        {
            case Device.UWP:
                this.UwpProgressRing.HeightRequest = this.HeightRequest;
                this.UwpProgressRing.WidthRequest = this.WidthRequest;
                break;
            default:
                this.ActivityIndicator.HeightRequest = this.HeightRequest;
                this.ActivityIndicator.WidthRequest = this.WidthRequest;
                break;
        }
    }

    public static readonly BindableProperty ColorProperty = BindableProperty.Create("Color", typeof(Color), typeof(LoadingRing), default(Color), BindingMode.Default, propertyChanged: OnColorPropertyChanged);

    private static void OnColorPropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
    {
        if (bindable is LoadingRing current)
        {
            switch (Device.RuntimePlatform)
            {
                case Device.UWP:
                    if (current.UwpProgressRing != null) current.UwpProgressRing.Color = (Color)newvalue;
                    break;
                default:
                    if (current.ActivityIndicator != null) current.ActivityIndicator.Color = (Color)newvalue;
                    break;
            }
        }
    }

    public Color Color
    {
        get => (Color)GetValue(ColorProperty);
        set => SetValue(ColorProperty, value);
    }

    public static readonly BindableProperty IsRunningProperty = BindableProperty.Create("IsRunning", typeof(bool), typeof(LoadingRing), default(bool), BindingMode.Default, propertyChanged: OnIsRunningChanged);

    private static void OnIsRunningChanged(BindableObject bindable, object oldvalue, object newvalue)
    {
        if (bindable is LoadingRing current)
        {
            switch (Device.RuntimePlatform)
            {
                case Device.UWP:
                    if (current.UwpProgressRing != null) current.UwpProgressRing.IsRunning = (bool)newvalue;
                    break;
                default:
                    if (current.ActivityIndicator != null) current.ActivityIndicator.IsRunning = (bool)newvalue;
                    break;
            }
        }
    }

    public bool IsRunning
    {
        get => (bool)GetValue(IsRunningProperty);
        set => SetValue(IsRunningProperty, value);
    }

}

The implementation derives from ContentView. Depending on the platform my app is running, I am using my custom implementation of the ProgressRingIndicatorcontrol or the default Xamarin.Forms.ActivityIndicator to set the Contenton it. It is also important to handle the SizeChangedevent properly, otherwise the control will never be resized. As the custom implementation before, this catalyst exposes the same properties as the ActivityIndicator, so it is very easy to replace all existing places where I use the default control with it.

That’s it, we have a QaD-implementation that makes it easier to have a similar activity-indicating UI across platforms now. If you want to see it in action, there is a sample available on GitHub. As always, I hope this post is helpful for some of you.

Happy Coding, everyone!

Posted by msicc in Dev Stories, UWP, Xamarin, 2 comments
Saying Goodbye to 2017 [Editorial]

Saying Goodbye to 2017 [Editorial]

First Half

The first half of the year I wasn’t much into development besides work. I was asked to help building a new German Android news site, which turned out to be an impossible task because of several reasons (high author fluctuation was the baddest thing). In the end, the owners decided to go another route by turning the side into a a site dedicated to Chinese hardware, which is an area I do not have a lot of trust and interest. So I decided to step out of the project and focus again on my software development efforts.

Back to software development (Second Half)

The first thing I was focusing on in that area was to get deeper into web development with ASP.NET Core. I learned a few basics from Pluralsight and started to work on a project that I will (hopefully) bring forward in 2018.

I also got back deeper into cross platform development with Xamarin, especially Xamarin.Forms. As Microsoft killed all mobile efforts in the UWP, this step was one I denied way too long to go. As a logic step  I started with my ongoing series of blog posts about Xamarin Forms and the MVVMLight toolkit. If you missed it, here are the links to the posts:

During the first 8 month of the year, I was running Android as my daily driver. However, I never was really happy with the Android OS (and I am still not), so I decided to switch to the iPhone 8 Plus after its launch. I detailed the reasons why here:

Why I am (once again) using an iPhone [Editorial]

In the last month, I was also looking into some IOT development, and this is were my current focus is. In the next few weeks I have a private project that overlaps with a project at work. I really appreciate it when I can be productive in multiple ways, and those (sadly rare) overlapping projects are just plain awesome to work on.

Private things…

Having a look at my private goals (for those who care), I started with some functional fitness workouts in late summer. I am using the workout app from Skimble, which has some handy video guides and is way cheaper than a gym subscription. In 2018, I want to move on to get even more fit. On top, one of the biggest (and probably hardest) goal is to become a non-smoker. I am hoping that being more active has motivating impacts on the later goal as well. On top, in the last few days I had my first baby steps into meditation as well, but I am still struggling with that one. So, way to go in these parts of my life.

Well, this post is not as long as the ones of the years before, but I really already told you everything that happened this year. To close this post, I wish you all a happy end of the year, an awesome party tonight and I hope to welcome you all again in 2018 here on my personal blog.

Happy new year, everyone!

Posted by msicc in Dev Stories, Editorials, 0 comments
Xamarin Forms, the MVVMLight Toolkit and I: loading bundled assets in the Forms project

Xamarin Forms, the MVVMLight Toolkit and I: loading bundled assets in the Forms project

The scenario …

The reason I came up with this is that I am writing on an Xamarin.Forms web reader app. It is an app that uses a  WebView to display the contents of web articles. Of course, I am using a CSS-file to style the content that gets displayed. I am using the default font of every platform, plus some platform specific settings in there. The easiest way to get it working right is to give every platform its own CSS-file. In the Xamarin.Forms project however, I just want to call one method that gets the thing done.

For this scenario, the well documented Files access in Xamarin.Forms does not work.

This post will not yet be reflected in my ongoing XfMvvmLight project on Github as I have another one building on top of this in my queue. Once the second one is written, the project will show these changes, too. This post will contain the full classes however, so you could C&P them if you want/need.

DependencyService and another interface

If you are following this series already, you might already know that the easiest way to achieve my goal is to use the built-in Xamarin.FormsDependencyService and the needed interface with the native implementations.

So let’s start with the interface:

namespace XfMvvmLight.Abstractions
{
    public interface IAssetPathHelper
    {
        string GetResourceFolderPath(string folderName, bool forWeb = false);

        string GetResourcePath(bool forWeb = false);

        string GetResourceFilePath(string folder, string fileName, bool forWeb = false);
    }
}

The interface dictates three string-returning methods that will either return the base path of the platform resources, a specific folder or the full path to the bundled file. This interface covers most usage scenarios I came across. Feel free to leave any feedback if I am missing out a common one.

The only thing left to do is to register the interface in our ViewModelLocator, like we did already before in the RegisterServices() method:

var assetPathHelper = DependencyService.Get();
SimpleIoc.Default.Register(()=> assetPathHelper);

We are getting the platform implementation via the built in DependecyService and assign in to our Xamarin.Forms interface (like we have done already before). By registering it with our SimpleIoc instance, we can now use it wherever we want in our Xamarin.Forms project.

Platform implementations

Android

If you add files in the Resources folder, you can easily access them via the Resource class in your Android project. However, files like CSS-files are normally placed within the Assets folder of your Xamarin.Android project.

Depending on the usage scenario, we have two ways to access the files in the ‘Assets’ folder. If we are residing in the Xamarin.Android project and want to access the content of those bundled assets, we are able to access them using the Android.App.Context.Assets property and assign it to the Android.Content.Res.AssetManager class. We can then use streams to get the data contained in those files.

This does not help however if we want to access those files from a WebView (both in the Android and the Xamarin.Forms project), that’s why we have to use the ‘file:///android_asset‘ uri-scheme. Here is the platform implementation:

using XfMvvmLight.Abstractions;
using XfMvvmLight.Droid.PlatformImplementation;
using System.Diagnostics;
using System.IO;

[assembly: Xamarin.Forms.Dependency(typeof(AssetPathHelper))]
namespace XfMvvmLight.Droid.PlatformImplementation;
{
    public class AssetPathHelper : IAssetPathHelper
    {
        public string GetResourceFolderPath(string folderName, bool forWeb = false)
        {
            return Path.Combine(GetResourcePath(),folderName);
        }

        public string GetResourcePath(bool forWeb = false)
        {
            //reminding ourselves to double check if this way is really necessary
            if (!forWeb)
            {
                Debug.WriteLine("**********************************");
                Debug.WriteLine("You should consider using AssetManager if you are not using this in a WebView.");
                Debug.WriteLine("See: https://developer.xamarin.com/guides/android/application_fundamentals/resources_in_android/part_6_-_using_android_assets/");
                Debug.WriteLine("**********************************");
            }
            
            //but we are always returning the uri scheme 
            return $"file:///android_asset"; 
        }

        public string GetResourceFilePath(string folder, string fileName, bool forWeb = false)
        {
            var folderPath = string.IsNullOrEmpty(folder) ? GetResourcePath() : GetResourceFolderPath(folder);

            return Path.Combine(folderPath,fileName);
        }
    }
}

The implementation is pretty straight forward. Although we could call all three methods, the one we use probably the most is the GetResourceFilePath method. It will give us the complete path to the resource file, which we can then use in our calling code of our Xamarin.Forms project.

By using the Path.Combine method we make sure we get a valid file path string, which is exactly what we need if we are accessing assets in this way. As most of the scenarios for accessing assets could be easily covered by using AssetManager (see above), I am printing a reminder message that it exists to the output window of VisualStudio.

Important: you have to make sure the Build Action of your files is set to AndroidAsset, otherwise you’ll see nothing, in some scenarios it will even throw exceptions. This accounts for the AssetManager as well as for the AssetPathHelper implementations.

iOS

On iOS, we are able to access bundled assets via the NSBundle class. The implementation is even easier than the one for Android, as this is the only way to get those assets. That’s why we are ignoring the forWeb parameter in this case. Here is the implementation:

using System.IO;
using Foundation;
using XfMvvmLight.Abstractions;
using XfMvvmLight.iOS.PlatformImplementation;

[assembly: Xamarin.Forms.Dependency(typeof(AssetPathHelper))]
namespace XfMvvmLight.iOS.PlatformImplementation
{
    //forWeb is ignored on iOS!
    public class AssetPathHelper : IAssetPathHelper
    {
        public string GetResourceFolderPath(string folderName, bool forWeb = false)
        {
            return Path.Combine(GetResourcePath(), folderName);
        }

        public string GetResourcePath(bool forWeb = false)
        {
            return NSBundle.MainBundle.BundlePath;
        }

        public string GetResourceFilePath(string folder, string fileName, bool forWeb = false)
        {
            var folderPath = string.IsNullOrEmpty(folder) ? GetResourcePath() : GetResourceFolderPath(folder);

            return Path.Combine(folderPath, fileName);
        }
    }
}

Important: Make sure your files have the Build Action set to BundleResource, because otherwise you will once again get some errors flying around your head.

UWP

The implementation of the UWP Assets is once again the one with the most places involved. Let’s have a look at the implementation itself first:

using System.IO;
using XfMvvmLight.Abstractions;

namespace XfMvvmLight.UWP.PlatformImplementations
{
    public class AssetPathHelper : IAssetPathHelper
    {
        public string GetResourceFolderPath(string folderName, bool forWeb = false)
        {
            return Path.Combine(GetResourcePath(forWeb),folderName);
        }

        public string GetResourcePath(bool forWeb = false)
        {
            if (forWeb)
            {
                return $"ms-appx-web:///";
            }
            else
            {
                return $"ms-appx:///";
            }
        }

        public string GetResourceFilePath(string folder, string fileName, bool forWeb = false)
        {
            var folderPath = string.IsNullOrEmpty(folder) ? GetResourcePath(forWeb) : GetResourceFolderPath(folder, forWeb);

            return Path.Combine(folderPath,fileName);
        }
    }
}

The UWP platform uses a separate uri-scheme for all web related things. That’s where the  forWeb parameter comes in handy. If we are not loading a bundled asset for the web, we can use this implementation for other resources as well (bundled placeholder images are a good example here).

The next step is to add the assembly again to the list of assemblies that must be included, like we have done before in the OnLaunched method within App.xaml.cs:

//modified for .NET Compile
//see https://developer.xamarin.com/guides/xamarin-forms/platform-features/windows/installation/universal/#Target_Invocation_Exception_when_using_Compile_with_.NET_Native_tool_chain
List<Assembly> assembliesToInclude =
    new List<Assembly>
    {
        typeof(OsVersionService).GetTypeInfo().Assembly,
        typeof(PlatformDialogService).GetTypeInfo().Assembly, 
        typeof(AssetPathHelper).GetTypeInfo().Assembly
    };

The last step involved in the UWP project is to register the implementation with the DependencyService
after the Xamarin.Forms framework is initialized:

Xamarin.Forms.DependencyService.Register<AssetPathHelper>();

The resources should be packed with a Build Action of Content for the UWP platform.

Back to the Xamarin.Forms project

Now that we have everything in place on our platform projects as well as our Xamarin.Forms project, we finally can start using these methods. Here is an example of loading a CSS-file into a string. We can pass this string together with an HTML-string into a HtmlWebViewSource:

private static string GetCssString(string cssFileName)
{
    var resourcePath = SimpleIoc.Default.GetInstance<IAssetPathHelper>().GetResourceFolderPath("HtmlResources", true);

    return $"<link rel=\"stylesheet\" href=\"{resourcePath}/{cssFileName}\">";
}

Summary

Using the DependencyService of Xamarin.Forms allows us once again to use platform specific functionality very easily. When we are working with WebView and HTML, this comes in handy. If you have other valid scenarios for this implementations or even ideas on how to improve them, feel free to leave a comment below or ping me on my social network accounts. Otherwise, I hope this post is helpful for some of you.

As this is the last post before xmas, I wish you all a merry xmas in addition to my traditional

Happy coding, everyone!

Posted by msicc in Android, Dev Stories, iOS, UWP, Xamarin, 1 comment
[Updated] A workaround for Xamarin Forms 2.5 bug that prevents resource declaration in App.xaml

[Updated] A workaround for Xamarin Forms 2.5 bug that prevents resource declaration in App.xaml

Update: Xamarin appearently solved this problem with Service Release 3 for Xamarin Forms 2.5. I can confirm it works in the app that caused me to write this post.

Additional note: the forms:prefix is no longer needed, just insert the <ResourceDictionary>tag.


If you have a Windows background like I do, one of the most normal things for applications is to create keyed Resources in App.xaml to make them available throughout the app. Something like this should look familiar:

<forms:ResourceDictionary >
    <viewModels:ViewModelLocator x:Key="Locator"></viewModels:ViewModelLocator>
    <forms:Color x:Key="MainAccentColor">#1e73be</forms:Color>
    <forms:Color x:Key="LightAccentColor">#61a1f1</forms:Color>
    <forms:Color x:Key="DarkAccentColor">#00488d</forms:Color>
    <forms:Color x:Key="MainBackgroundColor">#f4f4f4</forms:Color>
</forms:ResourceDictionary>

This is also possible in Xamarin.Forms. Sadly, Xamarin.Forms 2.5 introduced an ugly bug where this declarations throw an ArgumentException, telling us the key(s) already exist in the dictionary (see Bugzilla here). I can confirm that this bug affects at least UWP, Android and iOS applications which use such an implementation.

As this is a show-stopping bug, I had to find a way to work around it for the moment. In such cases, I always try to find a way that has only very little impact. For this particular bug, I just moved the declaration of the resources into the code-behind file, which keeps the rest of my code unchanged. I just created a method that does the work I originally had in the .xaml-file:

//needed because of Xamarin Bug  https://bugzilla.xamarin.com/show_bug.cgi?id=60788
private void CreateResourceDictionary()
{
    //making sure there is only one dictionary
    if (this.Resources == null)
        this.Resources = new ResourceDictionary();

    //making sure there is only one key
    if (!this.Resources.ContainsKey("Locator"))
    {
        this.Resources.Add("Locator", ViewModels.ViewModelLocator.Instance);
    }

    if (!this.Resources.ContainsKey("MainAccentColor"))
    {
        this.Resources.Add("MainAccentColor", Color.FromHex("#1e73be"));
    }

    if (!this.Resources.ContainsKey("LightAccentColor"))
    {
        this.Resources.Add("LightAccentColor", Color.FromHex("#61a1f1"));
    }

    if (!this.Resources.ContainsKey("DarkAccentColor"))
    {
        this.Resources.Add("DarkAccentColor", Color.FromHex("#00488d"));
    }

    if (!this.Resources.ContainsKey("MainBackgroundColor"))
    {
        this.Resources.Add("MainBackgroundColor", Color.FromHex("#f4f4f4"));
    }
}

This makes the application running again like it did before. Once the bug in Xamarin.Forms is fixed, I just have to delete this method and uncomment the XAML-declarations to get back to the state where I was prior to Xamarin.Forms 2.5.

If you are experiencing the same bug, I recommend to also comment on the Bugzilla-Entry (link).

As always, I hope this post is helpful for some of you.

Happy coding!

 

 

Posted by msicc in Android, Dev Stories, iOS, UWP, Xamarin, 4 comments
Xamarin Forms, the MVVMLight Toolkit and I: taking control over the back buttons

Xamarin Forms, the MVVMLight Toolkit and I: taking control over the back buttons

Why do I need to take control over the back button behavior?

The back button is available on Windows, Android and under certain conditions on iOS. It is one of the key navigation hooks. While the implementations vary between platforms, the functionality is always the same – go back one step in navigation. Sometimes, we need to execute actions before going back, like notifying other parts of our application or even blocking back navigation because we need to perform actions on the local page (think of a WebViewthat has internal navigation capabilities for example). While we do need only a few lines to intercept the hardware back button on Android and UWP, the software back buttons on Android and iOS need some additional code.

Xamarin.Forms – View and ViewModel implementations

Based on the code we have already written in the past posts of this series, we are already able to get events pretty easy into our ViewModel, utilizing the EventToCommandBehavior approach. To get them into our ViewModel, we will throw an own created event. You can do so pretty easy by overriding the OnBackButtonPressed()method every Xamarin.Forms pages come with:

protected override bool OnBackButtonPressed()
{
    base.OnBackButtonPressed();
    BackButtonPressed?.Invoke(this, EventArgs.Empty);
    
    //return true; //breaks navigation    
    return false; //executes navigation
}

Depending on the boolean you will return here, the back navigation will be executed or not. The next step is to pass the event into our ViewModel, like we have done already with the ViewAppearingand ViewDisappearing events before:

private void XfNavContentPage_BindingContextChanged(object sender, EventArgs e)
{
    if (this.BindingContext is XfNavViewModelBase @base)
    {
        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "Appearing",
            Command = @base.ViewAppearingCommand
        });

        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "Disappearing",
            Command = @base.ViewDisappearingCommand
        });

        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "BackButtonPressed",
            Command = @base.BackButtonPressedCommand
        });
    }
}

As you can see from the snippet above, the next step is to add the BackButtonPressedCommandto our base ViewModel. There is nothing special on that, so here we go:

private RelayCommand _backButtonPressedCommand; 

public RelayCommand BackButtonPressedCommand =>
    _backButtonPressedCommand ?? (_backButtonPressedCommand = new RelayCommand(ExecuteBackButtonPressedCommand, CanExecuteBackButtonPressedCommand));

public virtual void ExecuteBackButtonPressedCommand() { }

public virtual bool CanExecuteBackButtonPressedCommand() 
{
    return true;
}

And that’s it, if you just want to get informed or handle back navigation on your own. However, from some of the projects I have worked on, I know that I may need to prevent back navigation. So let’s extend our code to reach that goal as well.

Blocking back navigation

Back in our base class implementation, let’s add a BindableProperty. This boolean property makes it very easy to block the back navigation (no matter if you’re doing so from a ViewModel or a View):

public static BindableProperty BlockBackNavigationProperty = BindableProperty.Create("BlockBackNavigation", typeof(bool), typeof(XfNavContentPage), default(bool), BindingMode.Default, propertyChanged: OnBlockBackNavigationChanged);

private static void OnBlockBackNavigationChanged(BindableObject bindable, object oldvalue, object newvalue)
{
    //not used in this sample
    //valid scneario would be some kind of validation or similar tasks
}

public bool BlockBackNavigation
{
    get => (bool) GetValue(BlockBackNavigationProperty);
    set => SetValue(BlockBackNavigationProperty, value);
}

The next part involves once again the already overridden OnBackButtonPressed() method. If we are blocking the back navigation, we are throwing another event:

protected override bool OnBackButtonPressed()
{
    if (this.BlockBackNavigation)
    {
        BackButtonPressCanceled?.Invoke(this, EventArgs.Empty);
        return true;
    }

    base.OnBackButtonPressed();
    BackButtonPressed?.Invoke(this, EventArgs.Empty);

    if (this.StackState.isModal) 
        return true; 
    else 
    { 
       return false; 
    }
}

Notice that I added an additional step for modal pages. Without that, the hardware button back press code will be executed twice on Android on modal pages. Of course, we are rooting also the BackButtonPressCanceledevent into our ViewModel, so let’s add it to our BindingContextChanged handler:

private void XfNavContentPage_BindingContextChanged(object sender, EventArgs e)
{
    if (this.BindingContext is XfNavViewModelBase @base)
    {
        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "Appearing",
            Command = @base.ViewAppearingCommand
        });

        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "Disappearing",
            Command = @base.ViewDisappearingCommand
        });

        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "BackButtonPressed",
            Command = @base.BackButtonPressedCommand
        });

        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "BackButtonPressCanceled",
            Command = @base.BackButtonPressCanceledCommand
        });
    }
}

To complete the code, we need to add a boolean property and the BackButtonPressCanceledCommand to our base ViewModel implementation:

private bool _blockBackNavigation;
private RelayCommand _backButtonPressCanceledCommand;

public virtual bool BlockBackNavigation
{
    get => _blockBackNavigation;

    set => Set(ref _blockBackNavigation, value);
}

public RelayCommand BackButtonPressCanceledCommand =>
    _backButtonPressCanceledCommand ?? (_backButtonPressCanceledCommand = new RelayCommand(ExecuteBackButtonPressCanceledCommand, CanExecuteBackButtonPressCanceledCommand));

public virtual void ExecuteBackButtonPressCanceledCommand() { }

public virtual bool CanExecuteBackButtonPressCanceledCommand()
{
    return true;
}

And that’s it. We already implemented everything we need in our Xamarin.Forms project.

Platform implementations

As often, we need to write some platform specific code to make our Xamarin.Forms code work in all scenarios.

Universal Windows Platform

As the Universal Windows Platforms handles the back button globally, no matter if you’re on a PC, tablet or a phone, there’s no need for additional code. Really. It’s already done with the Xamarin.Forms implementation.

Android

For the part of the hardware back button on Android devices, we are already done as well. But Android has also a software back button (eventually), which is in the toolbar (pretty similar to iOS).  There are two options we can use for Android. The first one involves just one line of code in our base page implementation’s constructor:

NavigationPage.SetHasBackButton(this, false);

This will hide the software back button on Android (and iOS as well). It would be perfectly fine on Android because all (phone and tablet) devices have a hardware back button. However, often, we do not have the possibility to go down the easy route. So let’s fully handle the toolbar button. It does not involve a lot of code, and it’s all in the MainActivity class:

protected override void OnPostCreate(Bundle savedInstanceState)
{
    var toolBar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
    SetSupportActionBar(toolBar);

    base.OnPostCreate(savedInstanceState);
}


public override bool OnOptionsItemSelected(IMenuItem item)
{
    //if we are not hitting the internal "home" button, just return without any action
    if (item.ItemId != Android.Resource.Id.Home)
        return base.OnOptionsItemSelected(item);

    //this one triggers the hardware back button press handler - so we are back in XF without even mentioning it
    this.OnBackPressed();
    // return true to signal we have handled everything fine
    return true;
}

The first step is to override the OnPostCreate method. Within the override, we are just setting the toolbar to be the SupportActionBar. If we would not do so, the more important override OnOptionsItemSelected would never get triggered. The back button in the toolbar has the internal resource name ‘Home’ (with a value of 16908332). If this button is hit, I am triggering the hardware back button press handler, which will get code execution routed back into the Xamarin.Formscode. By returning true we are telling Android we have handled this on our own. And that’s all we have to do in the Android project.

taking-over-back-button-android

iOS

On iOS,  a custom renderer for our XfNavContentPage is needed to get the same result. I was trying a few attempts that are floating around the web, but in the end this post was the most helpful to reach my goal also on iOS. Here is my version:

[assembly: ExportRenderer(typeof(XfNavContentPage), typeof(XfNavigationPageRenderer))] 
namespace XfMvvmLight.iOS.Renderer 
{ 
    public class XfNavigationPageRenderer : PageRenderer 
    { 
        public override void ViewWillAppear(bool animated) 
        { 
            base.ViewWillAppear(animated); 
  
            //making sure to use this only with non-modal pages 
            if (Element is XfNavContentPage page && this.NavigationController != null) 
            { 
                var thisPageIndex = page.Navigation.NavigationStack.IndexOf(page); 
                if (thisPageIndex >= 1) 
                { 
                    //disabling back swipe complettely: 
                    this.NavigationController.InteractivePopGestureRecognizer.Enabled = false; 
  
                    var backarrowImg = UIImage.FromBundle("arrow-back.png") 
                        .ImageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate); 
  
                    var backButton = new UIButton(UIButtonType.Custom) 
                    { 
                        HorizontalAlignment = UIControlContentHorizontalAlignment.Left, 
                        TitleEdgeInsets = new UIEdgeInsets(11.5f, 0f, 10f, 0f), 
                        //we need to move the image a bit more left to get closer to the OS-look 
                        ImageEdgeInsets = new UIEdgeInsets(1f, -8f, 0f, 0f) 
                    }; 
  
                    //this makes sure we use the same behavior as the OS 
                    //if there is no parent, it must throw an exception because something is wrong 
                    //with the navigation structure 
                    var parent = page.Navigation.NavigationStack[thisPageIndex - 1]; 
                    backButton.SetTitle(string.IsNullOrEmpty(parent.Title) ? "Back" : parent.Title, 
                        UIControlState.Normal); 
  
                    backButton.SetTitleColor(this.View.TintColor, UIControlState.Normal); 
                    backButton.SetImage(backarrowImg, UIControlState.Normal); 
                    backButton.SizeToFit(); 
  
                    backButton.TouchDown += (sender, e) => 
                    { 
                        if (!page.BlockBackNavigation) 
                        { 
                            this.NavigationController.PopViewController(animated); 
                        } 
                        page.SendBackButtonPressed(); 
                    }; 
  
                    backButton.Frame = new CGRect(0, 0, UIScreen.MainScreen.Bounds.Width / 4, 
                        NavigationController.NavigationBar.Frame.Height); 
  
                    var view = new UIView(new CGRect(0, 0, backButton.Frame.Width, backButton.Frame.Height)); 
                    view.AddSubview(backButton); 
  
  
                    var backButtonItem = new UIBarButtonItem(string.Empty, UIBarButtonItemStyle.Plain, null) 
                    { 
                        CustomView = backButton 
                    }; 
  
                    NavigationController.TopViewController.NavigationItem 
                        .SetLeftBarButtonItem(backButtonItem, animated); 
                } 
            } 
        } 
    } 
}

Let me explain the snippet. On iOS, we do not have direct access to the back button events in the navigation bar. We are able to override the back button, though. The first thing we have to make sure is that there is a UINavigationControlleraround. This way, we are still able to use our base page class implementation and its features for modal pages. The next step is to create a button with an image (which needs to be bundled).

Of course, we want the button’s text to behave exactly like the OS one does. That’s why we are going to get the parent view. We can easily use the current view’s NavigationStackindex for that – as long as we do not have cross navigation but a continuous one. In this case, the page before the current page is our parent. If the parent’s Titleproperty is empty, we are setting the title to “Back”, pretty much the same like the OS itself does. If you want it to be empty, just add a Title to the page with ” ” as content. This works also if you do not want your MasterPagein a Xamarin.Forms.MasterDetailPage to have a visible title, btw.

The most important thing to note is the button’s TouchDownevent – which is why we are doing this whole thing. First, we manually navigate back in iOS via the PopViewControllermethod (if necessary). After that, we are once again invoking our Xamarin.Formsimplementation via the SendBackButtonPressed method of the Xamarin.Formspage, which will then trigger our EventToCommandBehavior we implemented earlier.

The last step is to create an UIViewcontainer for the button and assign it as a UIBarButtonItemto the UINavigationController via the SetLeftBarButtonItemmethod the UINavigationItem provides. And that’s it, we now also have control over the back button on iOS.

taking-over-back-button-ios

 

Lust but not least, we need to handle also the swipe-back-gesture. This can be done the hard way by disabling the gesture completelly:

//disabling back swipe complettely:
this.NavigationController.InteractivePopGestureRecognizer.Enabled = false;

I do not have an implementation for handling that in a better way, but I will update the sample with another post in this series later on. At least I have full control over the back navigation, which is (for the moment) all I need.

As always, I hope this post will be helpful for some of you. I also updated the source code of my XfMvvmLight sample on Github to match this blog post. If you have feedback or questions, sound off below in the comments or via my social channels.

Until then, happy coding, everyone!

 

Posted by msicc in Android, Dev Stories, iOS, UWP, Xamarin, 1 comment