Xamarin.Forms 2.5 – Forms.Context .Confusion

TAB

Software consultant Matthew Clarke shares his experience of moving to version 2.5 of Xamarin.Forms.

Despite only being a minor version bump over Xamarin.Forms 2.4, version 2.5 has made some rather radical breaking changes. These are currently only “Obsolete” warnings but I’d advise you to act now before they become build failures.

The most disruptive of deprecations affect Android but they touch all parts of your code. The chances are they’ll affect you, as they touch some fundamental areas of Xamarin.Forms.

Forms.Context

Forms.Context allowed you to hook into the activity your Android app was running and had a few common usages:

  • to show overlays on your application, such as pop-ups used by UserDialogs (an invaluable library)
  • to call out to other activities such as the camera and get a response back

Forms.Context was static and always defined, which meant your code was able to access the activity without a headache. So now it’s deprecated, what should you do? Well, the obsolete message clears that up, doesn’t it?

    'Forms.Context' is obsolete: 'Context is obsolete as of version 2.5. Please use a local context instead.'

Fine, settled, you know exactly what to do now …

Well, clearly not, and Xamarin have been more than tight lipped about what to do. So here are the answers depending on exactly what you were doing.

  • Acr.UserDialogs users will be familiar with the following in their MainActivity.cs:
    • UserDialogs.Init(() => (Activity)Forms.Context);

    It’s a very simple change to pass in the MainActivity, and it works:

      UserDialogs.Init(() => this);

  • Services injected via DependencyService are usually configured in the MainActivity like this:
    • DependencyService.Register<IEvidenceCaptureService, EvidenceCaptureService>();

    This can stay the same but you’ll need a static initialize method that takes a reference to your MainActivity. Then replace any existing usage of Forms.Context to reference the new static member set by the initialize method.

  • Everything else, for example starting an external activity, uses a static reference provided in the Android namespace:
    • Android.App.Application.Context.StartActivity(intent);

    Until Xamarin update us officially on what to do this is a way, seemingly correct, to handle the change.

    Android renderers

    For all but the simplest of applications, you’ll need to add some custom renderer logic to get the look and feel you want. Renderers are a useful tool, but I advise against their overuse, as it can make your native application look more like a hybrid app. For the times you need to use a renderer, you’ll be familiar with the following code to register your renderer:

      [assembly: ExportRenderer(typeof(TextBox), typeof(CustomTextboxRenderer))]

    And that was it: just override the OnElementChanged or other methods and add your custom styling.

    Now that the default constructors for renderers have been deprecated, you’ll see the following messages:

      'EntryRenderer.EntryRenderer()' is obsolete: 'This constructor is obsolete as of version 2.5. Please use EntryRenderer(Context) instead.'

    But I’m not calling any constructors, you think to yourself. Is Xamarin voodoo happening for me?

    Well the answer is quite simple, though also quite irritating. Add a constructor to your renderers, then the Xamarin voodoo will sort out the rest for you.

      /// <summary> 
      /// Default constructor
      /// </summary>
      /// <param name="context">Context</param>
      /// <remarks>Required to suppress obsolete warning.</remarks>
      public CustomTextboxRenderer(Context context) : base(context)

    EditorEditText (deprecated in 2.4)

    This is a nice and simple change which is for the better. Previously in Android custom renderers, there were a lot of properties on the native control that you couldn’t access without casting:

      this.Control as EditorEditText

    Xamarin have introduced a new FormsEditText and eliminated the need to cast. Now this.Control.xx will give you access to all those previously elusive properties.

    OnPlatform and Device.OS

    Although Xamarin.Forms does a fantastic job of Write Once Run Anywhere, sometimes you need conditional logic depending on the platform your app is running:

      if (Device.OS != TargetPlatform.iOS)

    A subtle but welcome change has happened here:

      if (Device.RuntimePlatform == Device.iOS)

    Along with this, there is now a way to identify Windows Phone, Windows RT and UWP independently!

    For XAML, the syntax has changed quite a bit. Previously, your conditionality looked like this:

      <OnPlatform x:TypeArguments="FileImageSource">
      <OnPlatform.iOS>FinancialAssessments</OnPlatform.iOS>
      </OnPlatform>

    Now it looks like this:

      <OnPlatform x:TypeArguments="FileImageSource">
      <On Platform="iOS">FinancialAssessments</On>
      </OnPlatform>

    The C# variant looked like this:

      return Device.OnPlatform(iOS: Skin.FontName, Android: Skin.FontName, WinPhone: Skin.FontName);

    Now it’s a standard switch, which gives you much more flexibility:

      switch (Device.RuntimePlatform)
      {
          case Device.iOS:
          case Device.Android:
          case Device.WinRT:
              return Skin.FontName;
          default:
              throw new System.Exception
                ($"Unknown platform {Device.RuntimePlatform}");
      }

    Let’s hope it improves performance rather than being change for the sake of change.

    So that’s my round-up of the changes in Xamarin.Forms 2.5 that will affect you on upgrade. I always recommend keeping Xamarin.Forms upgraded, as sudden jumps in versions can be very disruptive.