5

What I need to achieve is, Adding a Layout to the existing page on Changing the screen from Portrait to Landscape. I have managed to detect orientation change using void OnSizeAllocated(double width, double height) . But I cannot add a layout on this event.

My sample C# Code is

   public class MyLayoutView : ContentPage
   {
      StackLayout portraitcontent = null;
      StackLayout landscapecontent = null;
      StackLayout footer = null;

   public MyLayoutView ()
    {

    portraitcontent = new StackLayout ();
    landscapecontent = new StackLayout ();
    footer = new StackLayout ();

    this.Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0);
    this.BackgroundColor = Color.FromHex ("#ffffff");
    this.Content = new StackLayout
        {
            Children =
            {
                portraitcontent ,
                landscapecontent ,
                footer 
            }
            };
         }
   }

OnSizeAllocated Method

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

        if (width < height) {
            // In Portrait
            portraitcontent .IsVisible = true;
            landscapecontent.IsVisible = false;
            Debug.WriteLine ("Application Is in portrait");
        } else {
            // In Landscape
            portraitcontent .IsVisible = false;
            landscapecontent.IsVisible = true;

            landscapecontent.Children.Clear ();

            Label LandscapeLabel= new Label
            {
              Text = "<Each Time Text Changes>",
              HorizontalOptions = LayoutOptions.StartAndExpand,
              TextColor=Xamarin.Forms.Color.FromHex("#000000")
            };

            landscapecontent.Children.Add(LandscapeLabel); 

            Debug.WriteLine ("Application Is in Landscape");
        }
    }

It throws cannot modify the collection while reenterancy is blocked error. I need to add a new label in stacklayout each time when orientation changes. How can I achieve it in a proper way??

Thanks in advance

Sudha
  • 375
  • 1
  • 5
  • 16

4 Answers4

4

Overriding OnSizeAllocated may not be best way. I just put it in my code and going from portrait to landscape calls this method around 25 times.

The other thing I am trying is hooking an event handler on the forms page:

this.SizeChanged += (sender, e) => setOrientation(this);    // Connect Orientation Switcher

To keep track of true page rotations and avoid unneeded work for the processor I created a class variable to store the current state then added that to the height/width comparison.

void setOrientation (Page p)
     if ( isPortrait && (p.Width > p.Height) ) { do stuff }
     if ( !isPortrait && (p.Width < p.Height) ) { do stuff }

That is quicker but currently there seems to be a memory leak with some layouts being switched back and forth. Still working on it. Cannot believe that with mobile having had screen rotations for years that a dev tool would be available that didn't have a stock easy way to do this. Seems like they could allow us to hook up several different layouts to a page and have the runtime automatically select based on the environment. I'm not a dev do maybe it is a lot harder than it seems like it should be to me.

If I ever get rotation working I'll try to remember to come back and post code.

mkobit
  • 43,979
  • 12
  • 156
  • 150
Howard
  • 41
  • 1
3

On Size allocated is invoked multiple times when orientation change. I think you have to do more validations like checking the number of chidren to avoid duplication of views.

    if(landscapecontent.Children.Count>1)
     { 
       //Don't add any views
     }

As long as I tried there is no event handler for rotation. Itz kind of hack in the code to change views when orientation changes.

Even though you can use the accepted answer here.. Hope it serves your purpose..

Community
  • 1
  • 1
Femil Shajin
  • 1,768
  • 6
  • 24
  • 38
0

For Xamarin forms the PCL is the wrong layer to detect orientation changes. You need to detect them with platform specific code and then communicate that up to the PCL. For a project that I am working on just now here is my solution: In the PCL Code:

public interface IOrientation
    {
        AppOrientation CurrentOrientation { get; }
        IObservable<AppOrientation> Orientation { get; }
    }

    public enum AppOrientation
    {
        Unknown = 0,
        Portrait,
        Landscape
    }

In any view (xaml page) that cares I can observe orientation changes do : I use autofac to initialize IOrientation in the ctor, use whatever Ioc or service resolution works best for you.

public HomePage(IViewResolver resolver,IOrientation orientation)
{
    InitializeComponent();
     _resolver = resolver;
     this.SetContent();
     orientation.Orientation.ObserveOn(SynchronizationContext.Current)
     .Subscribe(x => this.SetContent());
}

For Android:

In MainActivity.cs, implement IOrientation off MainActivity. public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity , IErrorReporting,IOrientation And somewhere in the ctor : _lastOrientation = this.Resources.Configuration.Orientation;

 #region IOrientation
        private Android.Content.Res.Orientation _lastOrientation;
        private readonly Subject<AppOrientation> _orientation = new Subject<AppOrientation>();

        public AppOrientation CurrentOrientation
        {
            get
            {
                return _lastOrientation == Android.Content.Res.Orientation.Portrait ? AppOrientation.Portrait :
                    _lastOrientation == Android.Content.Res.Orientation.Landscape ? AppOrientation.Landscape : 
                    AppOrientation.Unknown;
            }
        }
    public IObservable<AppOrientation> Orientation { get { return _orientation; } }

    public override void OnConfigurationChanged(Configuration newConfig)
    {
        base.OnConfigurationChanged(newConfig);
        if (newConfig.Orientation == this._lastOrientation)  return; 
        this._lastOrientation = newConfig.Orientation;

        this._orientation.OnNext(this.CurrentOrientation);

    }

    #endregion

In iOS extend Appdelegate public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate ,IOrientation and somewhere in the ctor: _lastOrientation = UIDevice.CurrentDevice.Orientation;

#region IOrientation

    private UIDeviceOrientation _lastOrientation = UIDeviceOrientation.Unknown;
    private readonly Subject<AppOrientation>  _orientation=new Subject<AppOrientation>();

    public AppOrientation CurrentOrientation
    {
        get
        {
            return
                (_lastOrientation == UIDeviceOrientation.LandscapeLeft || _lastOrientation == UIDeviceOrientation.LandscapeRight) ? AppOrientation.Landscape :
                (_lastOrientation == UIDeviceOrientation.Portrait || _lastOrientation == UIDeviceOrientation.PortraitUpsideDown) ? AppOrientation.Portrait : 
                AppOrientation.Unknown;
        }
    }

    public IObservable<AppOrientation> Orientation { get { return _orientation; } }

    #endregion

I don't have WP implementation but I can't believe it would be that hard to create one....

Valery Viktorovsky
  • 6,487
  • 3
  • 39
  • 47
Charles
  • 11
  • 3
0

Hope I'm not too late. Using Xamarin.Essentials is an easier solution for me.

Model.cs

using Xamarin.Essentials;
.
.
.
private DisplayOrientation orientation = DeviceDisplay.MainDisplayInfo.Orientation;
public DisplayOrientation Orientation
{
    get => orientation;
    set => orientation = value;
}

View.xaml.cs

using Xamarin.Essentials;
.
.
.
protected override void OnSizeAllocated(double width, double height)
{
    base.OnSizeAllocated(width, height);
    model.Orientation = DeviceDisplay.MainDisplayInfo.Orientation;
}

View.xaml

<ContentPage xmlns:ess="clr-namespace:Xamarin.Essentials;assembly=Xamarin.Essentials">
    <ContentPage.BindingContext>
        <!--Model here-->
    </ContentPage.BindingContext>
    <DataTrigger TargetType="..."
        Binding="{Binding Orientation}"
        Value="{x:Static ess:DisplayOrientation.Portrait}">
            <Setter Property="..." Value="..." />
    </DataTrigger>
    <DataTrigger TargetType="..."
        Binding="{Binding Orientation}"
        Value="{x:Static ess:DisplayOrientation.Landscape}">
            <Setter Property="..." Value="..." />
    </DataTrigger>
</ContentPage>

Don't forget the INotifyPropertyChanged implications.

tjvg1991
  • 382
  • 1
  • 5
  • 10