Home > Enterprise >  Screen glitch when validation errors are shown xamarin forms
Screen glitch when validation errors are shown xamarin forms

Time:02-11

I am learning a new way to show validation errors and I am having very weird issue with forms, when I click on Add button the controls that are pushed below the screen disappears or becomes transparent.

here is my form https://i.stack.imgur.com/LIrD6.jpg

when I click on add button this happens https://i.stack.imgur.com/PWZLZ.jpg (Note: this is an extended screenshot and my screen ends on phone number control. when I scroll remaining blank space is for Add button)

when I click/focus on any control the screen is fixes by itself https://i.stack.imgur.com/Exw4z.jpg

I am using shell app and have no idea whats going wrong here. Any help is appreciated.

here is my code

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="PROOF.Views.Patient.NewPatientPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
    xmlns:vm="clr-namespace:PROOF.ViewModels"
    xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
    Title="{Binding PageTitle}"
    ios:Page.UseSafeArea="true"
    x:DataType="vm:NewPatientViewModel"
    Shell.TabBarIsVisible="False"
    Visual="Material">
    <ContentPage.Resources>
        <ResourceDictionary>
            <xct:InvertedBoolConverter x:Key="InvertedBoolConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>
    <ContentPage.Content>
        <ScrollView>
            <Grid Margin="10">
                <StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
                    <Label Style="{DynamicResource HeadingLabel}" Text="Patient Info" />
                    <Entry
                        Placeholder="First Name"
                        Style="{DynamicResource DefaultEntry}"
                        Text="{Binding PatientFirstName}">
                        <Entry.Behaviors>
                            <xct:TextValidationBehavior
                                Flags="ValidateOnValueChanging"
                                IsValid="{Binding PatientFirstNameValid}"
                                RegexPattern="^(?!\s*$). " />
                        </Entry.Behaviors>
                        <Entry.Triggers>
                            <DataTrigger
                                Binding="{Binding PatientFirstNameValid}"
                                TargetType="Entry"
                                Value="False">
                                <Setter Property="PlaceholderColor" Value="{StaticResource Error}" />
                            </DataTrigger>
                        </Entry.Triggers>
                    </Entry>
                    <Label
                        IsVisible="{Binding PatientFirstNameValid, Converter={StaticResource InvertedBoolConverter}}"
                        Style="{DynamicResource ValidationLabel}"
                        Text="First Name is required." />
                    <Entry
                        Placeholder="Last Name"
                        Style="{DynamicResource DefaultEntry}"
                        Text="{Binding PatientLastName}">
                        <Entry.Behaviors>
                            <xct:TextValidationBehavior
                                Flags="ValidateOnValueChanging"
                                IsValid="{Binding PatientLastNameValid}"
                                RegexPattern="^(?!\s*$). " />
                        </Entry.Behaviors>
                        <Entry.Triggers>
                            <DataTrigger
                                Binding="{Binding PatientLastNameValid}"
                                TargetType="Entry"
                                Value="False">
                                <Setter Property="PlaceholderColor" Value="{StaticResource Error}" />
                            </DataTrigger>
                        </Entry.Triggers>
                    </Entry>
                    <Label
                        IsVisible="{Binding PatientLastNameValid, Converter={StaticResource InvertedBoolConverter}}"
                        Style="{DynamicResource ValidationLabel}"
                        Text="Last Name is required." />
                    <Entry
                        Keyboard="Email"
                        Placeholder="Email"
                        Style="{DynamicResource DefaultEntry}"
                        Text="{Binding PatientEmail}">
                        <Entry.Behaviors>
                            <xct:EmailValidationBehavior
                                DecorationFlags="Trim"
                                Flags="ValidateOnValueChanging"
                                IsValid="{Binding PatientEmailValid}" />
                        </Entry.Behaviors>
                        <Entry.Triggers>
                            <DataTrigger
                                Binding="{Binding PatientEmailValid}"
                                TargetType="Entry"
                                Value="False">
                                <Setter Property="PlaceholderColor" Value="{StaticResource Error}" />
                            </DataTrigger>
                        </Entry.Triggers>
                    </Entry>
                    <Label
                        IsVisible="{Binding PatientEmailValid, Converter={StaticResource InvertedBoolConverter}}"
                        Style="{DynamicResource ValidationLabel}"
                        Text="Email is required." />
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>
                        <StackLayout Grid.Column="0">
                            <Picker
                                Title="Year"
                                ItemsSource="{Binding DobYears}"
                                SelectedItem="{Binding SelectedDobYear}"
                                Style="{DynamicResource DefaultPicker}">
                                <Picker.Behaviors>
                                    <xct:TextValidationBehavior
                                        Flags="ValidateOnValueChanging"
                                        IsValid="{Binding SelectedDobYearValid}"
                                        RegexPattern="^(?!\s*$). " />
                                </Picker.Behaviors>
                                <Picker.Triggers>
                                    <DataTrigger
                                        Binding="{Binding SelectedDobYearValid}"
                                        TargetType="Picker"
                                        Value="False">
                                        <Setter Property="TitleColor" Value="{StaticResource Error}" />
                                    </DataTrigger>
                                </Picker.Triggers>
                            </Picker>
                            <Label
                                IsVisible="{Binding SelectedDobYearValid, Converter={StaticResource InvertedBoolConverter}}"
                                Style="{DynamicResource ValidationLabel}"
                                Text="Year is required." />
                        </StackLayout>
                        <StackLayout Grid.Column="1">
                            <Picker
                                Title="Month"
                                ItemsSource="{Binding DobMonths}"
                                SelectedItem="{Binding SelectedDobMonth}"
                                Style="{DynamicResource DefaultPicker}">
                                <Picker.Behaviors>
                                    <xct:TextValidationBehavior
                                        Flags="ValidateOnValueChanging"
                                        IsValid="{Binding SelectedDobMonthValid}"
                                        RegexPattern="^(?!\s*$). " />
                                </Picker.Behaviors>
                                <Picker.Triggers>
                                    <DataTrigger
                                        Binding="{Binding SelectedDobMonthValid}"
                                        TargetType="Picker"
                                        Value="False">
                                        <Setter Property="TitleColor" Value="{StaticResource Error}" />
                                    </DataTrigger>
                                </Picker.Triggers>
                            </Picker>
                            <Label
                                IsVisible="{Binding SelectedDobMonthValid, Converter={StaticResource InvertedBoolConverter}}"
                                Style="{DynamicResource ValidationLabel}"
                                Text="Year is required." />
                        </StackLayout>
                        <StackLayout Grid.Column="2">
                            <Picker
                                Title="Day"
                                ItemsSource="{Binding DobDays}"
                                SelectedItem="{Binding SelectedDobDay}"
                                Style="{DynamicResource DefaultPicker}">
                                <Picker.Behaviors>
                                    <xct:TextValidationBehavior
                                        Flags="ValidateOnValueChanging"
                                        IsValid="{Binding SelectedDobDayValid}"
                                        RegexPattern="^(?!\s*$). " />
                                </Picker.Behaviors>
                                <Picker.Triggers>
                                    <DataTrigger
                                        Binding="{Binding SelectedDobDayValid}"
                                        TargetType="Picker"
                                        Value="False">
                                        <Setter Property="TitleColor" Value="{StaticResource Error}" />
                                    </DataTrigger>
                                </Picker.Triggers>
                            </Picker>
                            <Label
                                IsVisible="{Binding SelectedDobDayValid, Converter={StaticResource InvertedBoolConverter}}"
                                Style="{DynamicResource ValidationLabel}"
                                Text="Year is required." />
                        </StackLayout>
                    </Grid>
                    <Label Style="{DynamicResource HeadingLabel}" Text="Guardian Info" />
                    <Entry
                        Placeholder="First Name"
                        Style="{DynamicResource DefaultEntry}"
                        Text="{Binding GuardianFirstName}">
                        <Entry.Behaviors>
                            <xct:TextValidationBehavior
                                Flags="ValidateOnValueChanging"
                                IsValid="{Binding GuardianFirstNameValid}"
                                RegexPattern="^(?!\s*$). " />
                        </Entry.Behaviors>
                        <Entry.Triggers>
                            <DataTrigger
                                Binding="{Binding GuardianFirstNameValid}"
                                TargetType="Entry"
                                Value="False">
                                <Setter Property="PlaceholderColor" Value="{StaticResource Error}" />
                            </DataTrigger>
                        </Entry.Triggers>
                    </Entry>
                    <Label
                        IsVisible="{Binding GuardianFirstNameValid, Converter={StaticResource InvertedBoolConverter}}"
                        Style="{DynamicResource ValidationLabel}"
                        Text="First Name is required." />
                    <Entry
                        Placeholder="Last Name"
                        Style="{DynamicResource DefaultEntry}"
                        Text="{Binding GuardianLastName}">
                        <Entry.Behaviors>
                            <xct:TextValidationBehavior
                                Flags="ValidateOnValueChanging"
                                IsValid="{Binding GuardianLastNameValid}"
                                RegexPattern="^(?!\s*$). " />
                        </Entry.Behaviors>
                        <Entry.Triggers>
                            <DataTrigger
                                Binding="{Binding GuardianLastNameValid}"
                                TargetType="Entry"
                                Value="False">
                                <Setter Property="PlaceholderColor" Value="{StaticResource Error}" />
                            </DataTrigger>
                        </Entry.Triggers>
                    </Entry>
                    <Label
                        IsVisible="{Binding GuardianLastNameValid, Converter={StaticResource InvertedBoolConverter}}"
                        Style="{DynamicResource ValidationLabel}"
                        Text="Last Name is required." />
                    <Entry
                        Keyboard="Email"
                        Placeholder="Email"
                        Style="{DynamicResource DefaultEntry}"
                        Text="{Binding GuardianEmail}">
                        <Entry.Behaviors>
                            <xct:EmailValidationBehavior
                                DecorationFlags="Trim"
                                Flags="ValidateOnValueChanging"
                                IsValid="{Binding GuardianEmailValid}" />
                        </Entry.Behaviors>
                        <Entry.Triggers>
                            <DataTrigger
                                Binding="{Binding GuardianEmailValid}"
                                TargetType="Entry"
                                Value="False">
                                <Setter Property="PlaceholderColor" Value="{StaticResource Error}" />
                            </DataTrigger>
                        </Entry.Triggers>
                    </Entry>
                    <Label
                        IsVisible="{Binding GuardianEmailValid, Converter={StaticResource InvertedBoolConverter}}"
                        Style="{DynamicResource ValidationLabel}"
                        Text="Email is required." />
                    <Entry
                        Keyboard="Numeric"
                        Placeholder="Phone Number"
                        Style="{DynamicResource DefaultEntry}"
                        Text="{Binding PhoneNumber}">
                        <Entry.Behaviors>
                            <xct:CharactersValidationBehavior
                                Flags="ValidateOnValueChanging"
                                IsValid="{Binding PhoneNumberValid}"
                                MinimumCharacterCount="10" />
                        </Entry.Behaviors>
                        <Entry.Triggers>
                            <DataTrigger
                                Binding="{Binding PhoneNumberValid}"
                                TargetType="Entry"
                                Value="False">
                                <Setter Property="PlaceholderColor" Value="{StaticResource Error}" />
                            </DataTrigger>
                        </Entry.Triggers>
                    </Entry>
                    <Label
                        IsVisible="{Binding PhoneNumberValid, Converter={StaticResource InvertedBoolConverter}}"
                        Style="{DynamicResource ValidationLabel}"
                        Text="Phone Number is required." />
                    <StackLayout IsVisible="{Binding IsAddViewVisible}">
                        <Button
                            Padding="5"
                            Command="{Binding AddCommand}"
                            Style="{x:StaticResource DefaultButton}"
                            Text="Add"
                            TextColor="White" />
                    </StackLayout>
                    <StackLayout Padding="0,10">
                        <StackLayout IsVisible="{Binding IsEditViewVisible}" Orientation="Horizontal">
                            <Button
                                Padding="5"
                                BackgroundColor="{x:StaticResource Error}"
                                Command="{Binding DeleteCommand}"
                                HorizontalOptions="FillAndExpand"
                                Text="Delete" />
                            <Button
                                Padding="5"
                                Command="{Binding EditCommand}"
                                HorizontalOptions="FillAndExpand"
                                Style="{x:StaticResource DefaultButton}"
                                Text="Edit"
                                TextColor="White" />
                        </StackLayout>
                    </StackLayout>
                </StackLayout>
            </Grid>
        </ScrollView>
    </ContentPage.Content>
</ContentPage>

View Model

namespace PROOF.ViewModels
{
    [QueryProperty(nameof(SelectedPatientId), "SelectedPatientId")]
    public class NewPatientViewModel : BaseViewModel
    {
        Page CurrentContext => Application.Current.MainPage;
        public NewPatientViewModel()
        {
            AddCommand = new AsyncCommand(() => AddAsync(), allowsMultipleExecutions: false);
            EditCommand = new AsyncCommand(() => EditAsync(), allowsMultipleExecutions: false);
            DeleteCommand = new AsyncCommand(() => DeleteAsync(), allowsMultipleExecutions: false);

            SetupDaysPicker();
            SetupYearsPicker();
        }

        async Task AddAsync()
        {
            if (ValidateForm())
            {
                // api call
            }
        }

        async Task EditAsync()
        {
            if (ValidateForm())
            {
                // api call
            }
        }

        private bool ValidateForm()
        {
            PatientFirstNameValid = !string.IsNullOrWhiteSpace(PatientFirstName);
            PatientLastNameValid = !string.IsNullOrWhiteSpace(PatientLastName);

            SelectedDobMonthValid = SelectedDobMonth > 0;
            SelectedDobDayValid = SelectedDobDay > 0;
            SelectedDobYearValid = SelectedDobYear > 0;

            GuardianFirstNameValid = !string.IsNullOrWhiteSpace(GuardianFirstName);
            GuardianLastNameValid = !string.IsNullOrWhiteSpace(GuardianLastName);
            //GuardianEmailValid = !string.IsNullOrWhiteSpace(GuardianEmail);

            var isValid = 
                PatientFirstNameValid && PatientLastNameValid && PatientEmailValid &&
                SelectedDobYearValid && SelectedDobMonthValid && SelectedDobDayValid &&
                GuardianFirstNameValid && GuardianLastNameValid && GuardianEmailValid && PhoneNumberValid;

            if (string.IsNullOrWhiteSpace(PatientEmail) && string.IsNullOrWhiteSpace(GuardianEmail) && string.IsNullOrWhiteSpace(PhoneNumber))
            {
                PatientEmailValid = false;
                GuardianEmailValid = false;
                PhoneNumberValid = false;
                isValid = false;
            }

            return isValid;
        }

        async Task DeleteAsync()
        {
           // api call
        }

        private void UpdateSignUpButton()
        {
            ValidateForm();
        }

        async void LoadPatient(string id)
        {
            SelectedPatient = // api call to get patient
            PatientFirstName = SelectedPatient.FirstName;
            PatientLastName = SelectedPatient.LastName;
            PageTitle = "Edit Patient";
            IsEditViewVisible = true;
        }

        private string _pageTitle;
        private string _patientFirstName;
        private string _guardianFirstName;
        private string _patientLastName;
        private string _guardianLastName;
        private string _title;
        private string _patientEmail;
        private string _guardianEmail;
        private string _guardianPhoneNumber;
        private bool _patientFirstNameValid;
        private bool _guardianFirstNameValid;
        private bool _patientLastNameValid;
        private bool _guardianLastNameValid;
        private bool _titleValid;
        private bool _patientEmailValid;
        private bool _guardianEmailValid;
        private bool _phoneNumberValid;
        private bool _isAddEnabled;
        private bool _isEditEnabled;
        private bool _isEditViewVisible;
        private bool _isAddViewVisible;
        private Patient _selectedPatient;

        public AsyncCommand AddCommand { get; }
        public AsyncCommand EditCommand { get; }
        public AsyncCommand DeleteCommand { get; }
        public string PatientId { get; set; }

        public string PageTitle
        {
            get => _pageTitle;
            set => SetProperty(ref _pageTitle, value);
        }

        public string GuardianFirstName
        {
            get => _guardianFirstName;
            set
            {
                SetProperty(ref _guardianFirstName, value);
            }
        }

        public string GuardianLastName
        {
            get => _guardianLastName;
            set
            {
                SetProperty(ref _guardianLastName, value);
            }
        }

        public string GuardianEmail
        {
            get => _guardianEmail;
            set => SetProperty(ref _guardianEmail, value);
        }
        
        public string PatientFirstName
        {
            get => _patientFirstName;
            set
            {
                SetProperty(ref _patientFirstName, value);
            }
        }

        public string PatientLastName
        {
            get => _patientLastName;
            set
            {
                SetProperty(ref _patientLastName, value);
            }
        }

        public string PatientEmail
        {
            get => _patientEmail;
            set => SetProperty(ref _patientEmail, value);
        }

        public string PhoneNumber
        {
            get => _guardianPhoneNumber;
            set
            {
                SetProperty(ref _guardianPhoneNumber, value);
            }
        }

        public bool IsAddViewVisible
        {
            get => _isAddViewVisible;
            set => SetProperty(ref _isAddViewVisible, value);
        }

        public bool IsAddEnabled
        {
            get => _isAddEnabled;
            set => SetProperty(ref _isAddEnabled, value);
        }

        public bool IsEditEnabled
        {
            get => _isEditEnabled;
            set => SetProperty(ref _isEditEnabled, value);
        }

        public bool IsEditViewVisible
        {
            get => _isEditViewVisible;
            set => SetProperty(ref _isEditViewVisible, value);
        }

        public bool GuardianFirstNameValid
        {
            get => _guardianFirstNameValid;
            set
            {
                SetProperty(ref _guardianFirstNameValid, value);
            }
        }

        public bool GuardianLastNameValid
        {
            get => _guardianLastNameValid;
            set
            {
                SetProperty(ref _guardianLastNameValid, value);
            }
        }

        public bool GuardianEmailValid
        {
            get => _guardianEmailValid;
            set
            {
                SetProperty(ref _guardianEmailValid, value);
            }
        }
        
        public bool PatientFirstNameValid
        {
            get => _patientFirstNameValid;
            set
            {
                SetProperty(ref _patientFirstNameValid, value);
            }
        }

        public bool PatientLastNameValid
        {
            get => _patientLastNameValid;
            set
            {
                SetProperty(ref _patientLastNameValid, value);
            }
        }

        public bool PatientEmailValid
        {
            get => _patientEmailValid;
            set
            {
                SetProperty(ref _patientEmailValid, value);
            }
        }

        public bool PhoneNumberValid
        {
            get => _phoneNumberValid;
            set
            {
                SetProperty(ref _phoneNumberValid, value);
            }
        }

        public Patient SelectedPatient
        {
            get => _selectedPatient;
            set => SetProperty(ref _selectedPatient, value);
        }

        public string SelectedPatientId
        {
            set
            {
                if (string.IsNullOrWhiteSpace(value))
                {
                    PageTitle = "Add Patient";
                    IsAddViewVisible = true;
                }
                else
                {
                    LoadPatient(value);
                }

            }
        }

        private List<int> _dobYears;
        private List<Month> _dobMonths = new List<Month>(Enum.GetValues(typeof(Month)).OfType<Month>().ToList());
        private List<int> _dobDays;
        private int _selectedDobYear;
        private bool _selectedDobYearValid;
        private Month _selectedDobMonth;
        private bool _selectedDobMonthValid;
        private int _selectedDobDay;
        private bool _selectedDobDayValid;
        private bool _isDobValid;

        public List<int> DobDays
        {
            get => _dobDays;
            set => SetProperty(ref _dobDays, value);
        }

        public List<Month> DobMonths
        {
            get => _dobMonths;
        }

        public List<int> DobYears
        {
            get => _dobYears;
            set => SetProperty(ref _dobYears, value);
        }

        public int SelectedDobYear
        {
            get => _selectedDobYear;
            set => SetProperty(ref _selectedDobYear, value);
        }

        public bool SelectedDobYearValid
        {
            get => _selectedDobYearValid;
            set => SetProperty(ref _selectedDobYearValid, value);
        }
        
        public Month SelectedDobMonth
        {
            get => _selectedDobMonth;
            set => SetProperty(ref _selectedDobMonth, value);
        }

        public bool SelectedDobMonthValid
        {
            get => _selectedDobMonthValid;
            set => SetProperty(ref _selectedDobMonthValid, value);
        }

        public int SelectedDobDay
        {
            get => _selectedDobDay;
            set => SetProperty(ref _selectedDobDay, value);
        }

        public bool SelectedDobDayValid
        {
            get => _selectedDobDayValid;
            set => SetProperty(ref _selectedDobDayValid, value);
        }
        
        public bool IsDobValid
        {
            get => _isDobValid;
            set => SetProperty(ref _isDobValid, value);
        }


        private void SetupDaysPicker()
        {
            DobDays = new List<int>();

            List<int> days = new List<int>();
            for (int i = 1; i <= 31; i  )
            {
                string day = i.ToString("D2");
                days.Add(int.Parse(day));
            }

            DobDays = days;
        }

        private void SetupYearsPicker()
        {
            DobYears = new List<int>();

            List<int> years = new List<int>();
            for (int i = DateTime.Now.Year; i >= 1900; i--)
            {
                string year = i.ToString("D4");
                years.Add(int.Parse(year));
            }

            DobYears = years;
        }

        private DateTime FormatDateOfBirth()
        {
            return new DateTime(SelectedDobYear, (int)SelectedDobMonth, SelectedDobDay);
        }
    }
}

CodePudding user response:

You can add a StackLayout out of the ScrollView.

Please refer to the following code:

 <!--add a StackLayout out of the ScrollView-->

 <StackLayout>

    <ScrollView VerticalOptions="EndAndExpand">

       <!--other code-->
        
    </ScrollView>

 </StackLayout>
  • Related