Search code examples
c#xamarin.formsrotationlandscape-portrait

Rotate Xamarin.Forms element by 90 degrees (display in landscape without rotating the whole page or device)


Just one element on a page, like a grid with its sub-elements, should be rotated by 90 degrees while beeing normally aligned by its surrounding stacklayouts and so on.

There is a Rotation="90" attribute. It works, but alignment seems to be calculated by its original orientation and the now exchanged height and width is not considered. So, if the element wasn't a square, it now overlaps other elements or draws partially outside the screen. This doesn't make much sense to me. How to do it right?


Solution

  • Put the element into a RelativeLayout, where you can position elements freely.

    <RelativeLayout>
      <Grid Rotation="90" AnchorX="0" AnchorY="0"
            RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width}"
            RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height}"
            RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width}">
    
      </Grid>
    </RelativeLayout>
    

    After exchanging just width and height, the element position is still wrong, because it is rotated around the original center (AnchorX and Y default is 0.5). This could be fixed by a translation by (Width - Height) / 2 (see code-behind solution below), but we get it much easier by changing the rotation anchor to one of its corners as seen above.

    /// <summary>
    /// Rotates an element by 90 or 270 degrees. It is required that you put a RelativeLayout (no parameters needed)
    /// around it and call this method on the element (i.e. in onAppearing).
    /// </summary>
    private void SetLandscape(VisualElement element, bool ccw = false)
    {
        element.Rotation = ccw ? 270.0 : 90.0;
        RelativeLayout.SetHeightConstraint(element, Constraint.RelativeToParent(layout => layout.Width));
        RelativeLayout.SetWidthConstraint(element, Constraint.RelativeToParent(layout => layout.Height));
        RelativeLayout.SetYConstraint(element, Constraint.RelativeToParent(layout => (layout.Height - layout.Width) / 2));
        RelativeLayout.SetXConstraint(element, Constraint.RelativeToParent(layout => (layout.Width - layout.Height) / 2));
    }
    

    Both solutions keep working after layout changes on runtime.