Search code examples
mauimaui-community-toolkit

StackLayout HeightRequest/WidthRequest based on orientation


In a .NET MAUI App I use a CommunityToolkit Popup with a Grid or any other Layout inside.

This Layout (in code below the outer Grid) should get a HightRequest and WidthRequest based on the Orientation of the Screen so that the Content is always visible nicely and the Buttons for instance can be clicked. On the inner Grid, I also want to use other controls to select values from later on.

I could set a HeightRequest="500" and WidthRequest="300" which works well when in Portrait View, but doesn't look good when in Landscape View.

Is there a good way, I can set these Requests based on the Orientation and adapt when the View has changed?

<?xml version="1.0" encoding="utf-8" ?>
<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
           xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
           xmlns:toolkit="clr-namespace:CommunityToolkit.Maui.Views;assembly=CommunityToolkit.Maui"
           xmlns:ios="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;assembly=Microsoft.Maui.Controls"
           xmlns:loc="clr-namespace:LocalizationResourceManager.Maui;assembly=LocalizationResourceManager.Maui"
           xmlns:viewModels="clr-namespace:OptimizationApp.ViewModels"
           xmlns:databaseModels="clr-namespace:OptimizationApp.DatabaseModels"
           x:DataType="viewModels:CustomPopupViewModel"
           x:Class="MacroOptimizerApp.Pages.Views.Controls.CustomPicker.CustomPopupContentPage"
           CanBeDismissedByTappingOutsideOfPopup="False">
           
           
           
    <Grid RowDefinitions="Auto, Auto, Auto" Background="White">

        <SearchBar Grid.Row="0" ios:SearchBar.SearchBarStyle="Minimal" Text="{Binding SearchText}" Placeholder="{loc:Translate CustomPickerPopup_SearchPlaceholder}" Margin="0, 0, 0, 5"  />

        
        
        <!-- Cancel/OK-Button -->
        <Grid Grid.Row="2" RowDefinitions="35" ColumnDefinitions="Auto, Auto" ColumnSpacing="5" HorizontalOptions="End">

            <Grid.Resources>
                <Style TargetType="Button">
                    <Setter Property="FontAutoScalingEnabled" Value="True" />
                    <Setter Property="HeightRequest" Value="35" />
                    <Setter Property="WidthRequest" Value="100" />
                    <Setter Property="Padding" Value="15, 7, 15, 10" />
                </Style>
            </Grid.Resources>

            <Button Grid.Column="0" Text="{loc:Translate CancelButtonText}" Clicked="CancelButton_Clicked" />
            <Button Grid.Column="1" Text="{loc:Translate OKButtonText}" Clicked="OkButton_Clicked" Command="{Binding OkButtonCommand}" CommandParameter="{Binding SelectedValue}" />

        </Grid>
        
    </Grid>
           
           
</toolkit:Popup>

Solution

  • You can define two properties on your view model and bind them to the properties HeightRequest and WidthRequest of your layout.

    You can refer to the following code:

    1.create a view model and implement interface INotifyPropertyChanged , then add two properties Height and Width.

    public class TestViewModel: INotifyPropertyChanged 
    {
    
        Double _height;
        public Double Height
        {
            get => _height;
            set => SetProperty(ref _height, value);
        }
    
        Double _width;
        public Double Width
        {
            get => _width;
            set => SetProperty(ref _width, value);
        }
    
        public TestViewModel()
        {
            Width = 200;
            Height = 200;
        }
    
        bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (Object.Equals(storage, value))
                return false;
    
            storage = value;
            OnPropertyChanged(propertyName);
            return true;
        }
    
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    }
    

    2.Usage example:

    MainPage.xaml.cs

    On your page, try to override method OnSizeAllocated, we can detect the orientation of the screen, then based on the result, we can change the value of the property that defined on the viewmodel.

     public partial class MainPage : ContentPage 
    {
        TestViewModel viewModel; 
    
        public MainPage()
          {
                InitializeComponent();
    
            viewModel = new TestViewModel();    
            this.BindingContext = viewModel;
          }
    
        private double width = 0;
        private double height = 0;
    
        protected override void OnSizeAllocated(double width, double height)
        {
            base.OnSizeAllocated(width, height);
            //var state = (width > height) ? "Landscape" : "Portrait";
    
            if (width != this.width || height != this.height)
            {
                this.width = width;
                this.height = height;
                if (width > height)//Landscape
                {
                    viewModel.Width = 100;
                    viewModel.Height = 100;
                }
                else  //Portrait
                {
                    viewModel.Width = 200;
                    viewModel.Height = 200;
    
                }
            }
    

    MainPage.xaml

      <Grid  BackgroundColor="Yellow" RowDefinitions="Auto, Auto, Auto"   HeightRequest="{Binding Height}" WidthRequest="{Binding Width}"  > 
           
     </Grid>
    

    Note:

    For more information, you can check document: Device Orientation.

    Although this document is about Xamarin.Forms, it also applies to MAUI.