Handling orientation in Xamarin Forms

Many years ago I wrote some Windows CE/Pocket PC code. One of the problems was handling different orientation in the same UI, so for example when the device is switched from Portrait to Landscape – in some cases we can use the same layout in both orientations other times and probably in more cases, this isn’t so simple.

Xamarin Forms does not raise orientation events or the likes, instead we will need to override a Pages OnSizeAllocated method, for example

protected override void OnSizeAllocated(double width, double height)
{
   base.OnSizeAllocated(width, height);

   if (width < height)
   {
       // portrait orientation
   }
   else if (height < width)
   {
       // landscape orientation
   }
   else
   {
      // square layout
   }
}

Note: There is also a Device Orientation plugin/nuget package for Xamarin Forms which raises events when orientation changes.

Now we can handle changes to our layout in one of few ways.

We might be to reparent controls, changing layout orientation and/or changes to rows/columns and so on, all in code. This is fairly efficient in terms of reusing the existing controls but is obviously less useful from a design perspective.

We might be able to create the layout in such a way that we can just change orientation of StackLayout’s etc. Hence changing a few properties to reflect the new orientation. This might be more difficult to setup successfully on complex pages.

An alternative method, which is a little wasteful in turns of control creation, but gives us a good design story, is two design two ContentView’s, one for Portrait and one for Landscape. Obviously we’ll need to bind the controls to the same view model properties etc. However, with this solution we can more rapidly get our UI up and running.

<controls:OrientationView>
   <controls:OrientationView.Landscape>
      <views:LandscapeView />
   </controls:OrientationView.Landscape>
   <controls:OrientationView.Portrait>
      <views:PortraitView />
   </controls:OrientationView.Portrait>
</controls:OrientationView>

The OrientationView, might look like this

public class OrientationView : ContentView
{
   public View Landscape { get; set; }
   public View Portrait { get; set; }
   public View Square { get; set; }

   private Page _parentPage;

   protected override void OnParentSet()
   {
      base.OnParentSet();

      _parentPage = this.GetParentPage();
      if (_parentPage != null)
      {
         _parentPage.SizeChanged += PageOnSizeChanged;
      }
   }

   private void PageOnSizeChanged(object sender, EventArgs eventArgs)
   {
      if (_parentPage.Width < _parentPage.Height)
      {
         Content = Portrait ?? Landscape ?? Square;
      }
      else if (_parentPage.Height < _parentPage.Width)
      {
         Content = Landscape ?? Portrait ?? Square;
      }
      else
      {
         Content = Square ?? Portrait ?? Landscape;
      }
   }
}

Here’s the GetParentPage extension method

public static class ViewExtensions
{
   public static Page GetParentPage(this VisualElement element)
   {
      if (element != null)
      {
         var parent = element.Parent;
         while (parent != null)
         {
            if (parent is Page parentPage)
            {
               return parentPage;
            }
            parent = parent.Parent;
         }
      }
      return null;
   }
}

This allows us to basically design totally different UI’s for portrait and landscape, maybe adding extra controls in landscape and removing in portrait. The obvious downside is the duplication of controls.