Will
Will

Reputation: 3

Xamarin Forms layout does not fit on smaller screen

I've been given an existing Xamarin Forms project that works as expected on one device and I'm trying to correct some issues with a screen layout when running the project on another device with a slightly smaller screen. On the smaller device, some items at the top and bottom of the screen are inaccessible (SKU/PO number, Save and Continue button, and Count). My goal is to have the layout scale with the screen size so it works on both devices. I would like to avoid using a scrollview if possible. I am new to Xamarin Forms but I have some experience working with Xamarin Android. Here are some screenshots of the layout when displayed on both devices Correct display on existing device. Incorrect display on new device.

And here is the layout.

<ContentPage.Resources>
    <ResourceDictionary Source="../Styles/ReceivingBaseViewStyle.xaml"/>
    <ResourceDictionary Source="../Styles/BaseLotViewStyle.xaml" />
</ContentPage.Resources>

<ContentPage.Content>
    <!--<ScrollView>-->
        <Grid Style="{StaticResource ScannerGridStyle}"
                RowSpacing="0"
                ColumnSpacing="0"
              >
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="10" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>

            <StackLayout Grid.Row="0" 
                            Style="{StaticResource BasePageIdRowStyle}">
                <Grid ColumnSpacing="0" RowSpacing="0" VerticalOptions="EndAndExpand">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="9" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>

                    <Label Grid.Column="0"
                            Text="{Binding Model.AdvanceShipNoticeIdentifier, StringFormat={x:Static resources:Labels.Global_AsnNumber_Template}}" 
                            Style="{StaticResource AsnIdRowLabelStyle}"
                            AutomationId="AsnNumber" />
                    <Image Grid.Column="1" 
                            Style="{StaticResource SeparatorDarkStyle}" />
                    <Label Grid.Column="2"
                            Text="{Binding Model.PurchaseOrderNumber, StringFormat={x:Static resources:Labels.Global_PoNumber_Template}}" 
                            Style="{StaticResource PoIdRowLabelStyle}"
                            AutomationId="PoNumber" />
                </Grid>
            </StackLayout>

            <StackLayout Grid.Row="1" 
                            Style="{StaticResource BasePageTitleWithIdRowStyle}">
                <Label Text="{x:Static resources:Labels.Receiving_SelectLot}" 
                        Style="{StaticResource BasePageTitleLabelStyle}" />
            </StackLayout>

            <StackLayout Grid.Row="2"
                            Style="{StaticResource SingleRowHighlightedStyle}">
                <controls:HintLabel Text="{Binding Model.ItemSku, StringFormat={x:Static resources:Labels.Global_SkuNumber_Template}}"
                        Style="{StaticResource HighlightedRowValueStyle}" />
            </StackLayout>

            <StackLayout Grid.Row="3"
                            Spacing="0"
                            Style="{StaticResource DescriptionRowStyle}">
                <StackLayout>
                    <Label x:Name="DescriptionTitle"
                            Text="{x:Static resources:Labels.Global_Description_Title}"
                            Style="{StaticResource DefaultLabelStyle}" />
                </StackLayout>
                <StackLayout>
                    <controls:HintLabel x:Name="DescriptionText"
                            Text="{Binding Model.ItemDescription}"
                            Style="{StaticResource DefaultTextStyle}" />
                </StackLayout>
            </StackLayout>

            <StackLayout Grid.Row="4"
                            Style="{StaticResource SeparatorRowStyle}">
                <StackLayout Style="{StaticResource SeparatorContentStyle}" />
            </StackLayout>

            <StackLayout Grid.Row="5"
                            Style="{StaticResource LotInputRowStyle}">

                <templates:CustomToggle LeftState="{Binding ItemLotModel.IsNewLotCreating, Converter={StaticResource InverseBoolConverter}}"
                                        ToggleName="{x:Static resources:Labels.Receiving_LotType}"
                                        LeftStateText="{x:Static resources:Labels.Receiving_UseExisting}"
                                        RightStateText="{x:Static resources:Labels.Receiving_CreateNew}"
                                        LeftCommand="{Binding ItemLotModel.ToggleExistingCommand}"
                                        RightCommand="{Binding ItemLotModel.ToggleNewCommand}" />

                <!-- Select Lot + New lot fields in the validation container -->
                <StackLayout>

                    <!-- Select Lot picker -->
                    <Grid RowSpacing="0"
                            ColumnSpacing="5"
                            Padding="0"
                            Style="{StaticResource ScannerGridStyle}">

                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*">
                            </ColumnDefinition>
                            <ColumnDefinition Width="140">
                            </ColumnDefinition>
                        </Grid.ColumnDefinitions>

                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto">
                            </RowDefinition>
                        </Grid.RowDefinitions>

                        <StackLayout Grid.Column="0"
                                        Grid.Row="0">
                            <Label Style="{StaticResource LotLabelStyle}"
                                    Text="{x:Static resources:Labels.Receiving_LotNumber}">
                            </Label>
                            <StackLayout IsVisible="{Binding ItemLotModel.IsNewLotCreating, Converter={StaticResource InverseBoolConverter}}">
                                <StackLayout.Triggers>
                                    <MultiTrigger TargetType="StackLayout">
                                        <MultiTrigger.Conditions>
                                            <BindingCondition Binding="{Binding ItemLotModel.SelectedLot.Errors, Converter={StaticResource ErrorsListToBoolConverter}}"
                                                                Value="True" />
                                        </MultiTrigger.Conditions>
                                        <Setter Property="Style"
                                                Value="{StaticResource ValidatableTextInputContainerActive}" />
                                    </MultiTrigger>
                                    <MultiTrigger TargetType="StackLayout">
                                        <MultiTrigger.Conditions>
                                            <BindingCondition Binding="{Binding ItemLotModel.SelectedLot.Errors, Converter={StaticResource ErrorsListToBoolConverter}}"
                                                                Value="False" />
                                        </MultiTrigger.Conditions>
                                        <Setter Property="Style"
                                                Value="{StaticResource LotInputContainerStyle}" />
                                    </MultiTrigger>
                                </StackLayout.Triggers>

                                <StackLayout Style="{StaticResource LotPickerStackLayoutStyle}"
                                                AutomationId="ExistingLotInput">
                                    <Label Text="{Binding ItemLotModel.SelectedLot.Value.Identifier}"
                                            Style="{StaticResource LotPickerLabelStyle}">

                                        <Label.GestureRecognizers>
                                            <TapGestureRecognizer 
                                            Command="{Binding ShowLotSelectionCommand}"
                                            NumberOfTapsRequired="1" />
                                        </Label.GestureRecognizers>

                                    </Label>
                                </StackLayout>

                            </StackLayout>

                            <StackLayout IsVisible="{Binding ItemLotModel.IsNewLotCreating}">
                                <StackLayout.Triggers>
                                    <MultiTrigger TargetType="StackLayout">
                                        <MultiTrigger.Conditions>
                                            <BindingCondition Binding="{Binding ItemLotModel.NewLot.Errors, Converter={StaticResource ErrorsListToBoolConverter}}"
                                                                Value="True" />
                                        </MultiTrigger.Conditions>
                                        <Setter Property="Style"
                                                Value="{StaticResource ValidatableTextInputContainerActive}" />
                                    </MultiTrigger>
                                    <MultiTrigger TargetType="StackLayout">
                                        <MultiTrigger.Conditions>
                                            <BindingCondition Binding="{Binding ItemLotModel.NewLot.Errors, Converter={StaticResource ErrorsListToBoolConverter}}"
                                                                Value="False" />
                                        </MultiTrigger.Conditions>
                                        <Setter Property="Style"
                                                Value="{StaticResource LotInputContainerStyle}" />
                                    </MultiTrigger>
                                </StackLayout.Triggers>

                                <!-- New Lot input box -->
                                <Grid RowSpacing="0"
                                        ColumnSpacing="0"
                                        Padding="0"
                                        Style="{StaticResource ScannerGridStyle}">

                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="*">
                                        </ColumnDefinition>
                                        <ColumnDefinition Width="Auto">
                                        </ColumnDefinition>
                                        <ColumnDefinition Width="Auto">
                                        </ColumnDefinition>
                                    </Grid.ColumnDefinitions>

                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto">
                                        </RowDefinition>
                                    </Grid.RowDefinitions>

                                    <controls:TextInput Grid.Column="0"
                                                        Grid.Row="0"
                                                        Style="{StaticResource LotInputStyle}"
                                                        AutomationId="NewLotInput"
                                                        Text="{Binding ItemLotModel.NewLot.Value, Mode=TwoWay}">
                                        <Entry.Behaviors>
                                            <behaviors:EventToCommandBehavior EventName="Completed"
                                                                                Command="{Binding ItemLotModel.NewLotEnteredCommand}" />
                                            <behaviors:EventToCommandBehavior EventName="TextChanged"
                                                                                Command="{Binding ItemLotModel.ValidateNewLotCommand}" />
                                        </Entry.Behaviors>
                                    </controls:TextInput>

                                </Grid>
                            </StackLayout>
                        </StackLayout>

                        <!-- Expiration box -->
                        <StackLayout Grid.Column="1"
                                        Grid.Row="0">
                            <Label Style="{StaticResource LotLabelStyle}"
                                    Text="{x:Static resources:Labels.Receiving_Expiration}">
                            </Label>
                            <StackLayout Style="{StaticResource LotInputContainerStyle}">
                                <StackLayout.Triggers>
                                    <MultiTrigger TargetType="StackLayout">
                                        <MultiTrigger.Conditions>
                                            <BindingCondition Binding="{Binding ItemLotModel.ExpirationDate.Errors, Converter={StaticResource ErrorsListToBoolConverter}}"
                                                                Value="True" />
                                        </MultiTrigger.Conditions>
                                        <Setter Property="Style"
                                                Value="{StaticResource ValidatableTextInputContainerActive}" />
                                    </MultiTrigger>
                                    <MultiTrigger TargetType="StackLayout">
                                        <MultiTrigger.Conditions>
                                            <BindingCondition Binding="{Binding ItemLotModel.ExpirationDate.Errors, Converter={StaticResource ErrorsListToBoolConverter}}"
                                                                Value="False" />
                                        </MultiTrigger.Conditions>
                                        <Setter Property="Style"
                                                Value="{StaticResource LotInputContainerStyle}" />
                                    </MultiTrigger>
                                </StackLayout.Triggers>

                                <StackLayout IsVisible="{Binding ItemLotModel.IsNewLotCreating}">
                                    <controls:NullableDatePicker Style="{StaticResource LotInputStyle}"
                                                                    NullableDate="{Binding ItemLotModel.ExpirationDate.Value}"
                                                                    AutomationId="NewLotExpirationDateInput"
                                                                    ValueUpdatedCommand="{Binding ItemLotModel.DateSelectedCommand}" >
                                    </controls:NullableDatePicker>
                                </StackLayout>

                                <StackLayout IsVisible="{Binding ItemLotModel.IsNewLotCreating, Converter={StaticResource InverseBoolConverter}}">
                                    <controls:NullableDatePicker Style="{StaticResource LotInputStyle}"
                                                                    NullableDate="{Binding ItemLotModel.SelectedLot.Value.ExpirationDate}"
                                                                    AutomationId="ExistingLotExpirationDateInput"
                                                                    IsEnabled="False">
                                    </controls:NullableDatePicker>
                                </StackLayout>

                            </StackLayout>
                        </StackLayout>
                    </Grid>
                </StackLayout>

                <!-- Error message box -->
                <StackLayout Style="{StaticResource ErrorMessageContainer}"
                                IsVisible="{Binding ItemLotModel.Errors, Converter={StaticResource ErrorsListToBoolConverter}}"
                                Margin="5">
                    <Label Text="{Binding ItemLotModel.Errors, Converter={StaticResource FirstValidationErrorConverter}}"
                            Style="{StaticResource ErrorMessageLabel}"
                            AutomationId="LoginErrorMessageLabel">
                    </Label>
                </StackLayout>

            </StackLayout>

            <StackLayout Grid.Row="6">
                <Image Style="{StaticResource ScannerRoundedCornerStyle}" />
            </StackLayout>

            <StackLayout Grid.Row="7" 
                            Style="{StaticResource BasePageActionButtonRowStyle}" Margin="0,18,0,0"
                            AutomationId="SkipItemButton">
                <Frame Style="{StaticResource BasePageActionButtonFrameStyle}">
                    <StackLayout Style="{StaticResource BasePageActionButtonContainerStyle}">
                        <Label Text="{x:Static resources:Labels.Receiving_SaveAndContinue}"
                                Style="{StaticResource BasePageActionButtonLabelStyle}"
                                IsEnabled="{Binding ItemLotModel.IsUpdating, Converter={StaticResource InverseBoolConverter}}" />
                        <Image Style="{StaticResource BasePageContinueButtonImageStyle}" />
                    </StackLayout>
                </Frame>
                <StackLayout.GestureRecognizers>
                    <TapGestureRecognizer NumberOfTapsRequired="1"
                        Command="{Binding SubmitItemLotCommand}" />
                </StackLayout.GestureRecognizers>
            </StackLayout>

            <StackLayout Grid.Row="8" Style="{StaticResource BasePageRemainingItemsRowStyle}">
                <Label Text="{Binding Model.CountNumber, StringFormat={x:Static resources:Labels.Receiving_CountName_Template}}"
                        Style="{StaticResource BasePageRemainingItemRowLabelStyle}"
                        AutomationId="CountNumber" />
            </StackLayout>

            <Label Grid.Row="8" x:Name="KeepVisibleLabel" />

        </Grid>
    <!--</ScrollView>-->
</ContentPage.Content>

Any help would be greatly appreciated. Thanks!

Upvotes: 0

Views: 970

Answers (1)

Junior Jiang
Junior Jiang

Reputation: 12723

If using Grid as root layout, we can define the RowDefinitions to show proportionally.

For example:

<Grid.RowDefinitions>
    <RowDefinition Height="0.8*" />
    <RowDefinition Height="2*" />
    <RowDefinition Height="0.8*" />
    <RowDefinition Height="1.2*" />
    <RowDefinition Height="10" />
    <RowDefinition Height="0.8*" />
    <RowDefinition Height="1.2*" />
    <RowDefinition Height="1.2*" />
    <RowDefinition Height="1*" />
</Grid.RowDefinitions>

Now above code will show full content even on smaller screen of device.

The 0.8* means 8% of screen height, and 1.2* means 12% of the screen height. If not above the height of screen , just make their sum less then 10*.

In addition, we also can use Xamarin.Forms RelativeLayout to show proportionally no matter the screen of device is large or small.

Upvotes: 1

Related Questions