Xamarin Forms iOS Validate Texbox and show error

I've customized the Xamarin Forms Entry control for Android,

enter image description here

I'm using MVVM Validation for validating my Inputs via MVVM and generate a message for each form element.

 Validator.AddRequiredRule(() => UserName, Messages.FieldCannotBlank);
 Validator.AddRequiredRule(() => Password, Messages.FieldCannotBlank);

The customised entry I've used is an extended version of the Extended Entry by Xlabs. Here's the code for the Entry

 public class ExtendedEntry : Entry
{
    public ExtendedEntry()
    {
        BorderColor = Color.Default;
        BGColorForRounded = Color.Default;
    }

    #region XAlign
    /// <summary>
    /// The XAlign property
    /// </summary>
    public static readonly BindableProperty XAlignProperty =
        BindableProperty.Create(nameof(XAlign), typeof(TextAlignment), typeof(ExtendedEntry),
        TextAlignment.Start);

    /// <summary>
    /// Gets or sets the X alignment of the text
    /// </summary>
    public TextAlignment XAlign
    {
        get { return (TextAlignment)GetValue(XAlignProperty); }
        set { SetValue(XAlignProperty, value); }
    }
    #endregion

    #region BGColorForRounded
    public static BindableProperty BGColorForRoundedProperty =
        BindableProperty.Create(nameof(BGColorForRounded), typeof(Color), typeof(ExtendedEntry), default(Color));

    public Color BGColorForRounded
    {
        get { return (Color)GetValue(BGColorForRoundedProperty); }
        set { SetValue(BGColorForRoundedProperty, value); }
    }
    #endregion

    #region Padding

    public static BindableProperty PaddingProperty =
        BindableProperty.Create(nameof(Padding), typeof(Thickness), typeof(ExtendedEntry), default(Thickness), defaultBindingMode: BindingMode.OneWay);

    public Thickness Padding
    {
        get { return (Thickness)GetValue(PaddingProperty); }
        set { SetValue(PaddingProperty, value); }
    }

    #endregion Padding

    #region IsReadOnly

    public static readonly BindableProperty IsReadOnlyProperty =
        BindableProperty.Create("IsReadOnly", typeof(bool), typeof(ExtendedEntry), false);

    public bool IsReadOnly
    {
        get { return (bool)GetValue(IsReadOnlyProperty); }
        set { SetValue(IsReadOnlyProperty, value); }
    }
    #endregion

    #region IsValid
    public static readonly BindableProperty IsValidProperty =
        BindableProperty.Create("IsValid", typeof(bool), typeof(ExtendedEntry), true);

    public bool IsValid
    {
        get { return (bool)GetValue(IsValidProperty); }
        set { SetValue(IsValidProperty, value); }
    }
    #endregion

    #region ErrorHint
    public static readonly BindableProperty ErrorHintProperty =
        BindableProperty.Create("ErrorHint", typeof(string), typeof(ExtendedEntry), "");

    public string ErrorHint
    {
        get { return (string)GetValue(ErrorHintProperty); }
        set { SetValue(ErrorHintProperty, value); }
    }

    #endregion

    #region PlaceholderTextColor
    /// <summary>
    /// The PlaceholderTextColor property
    /// </summary>
    public static readonly BindableProperty PlaceholderTextColorProperty =
        BindableProperty.Create("PlaceholderTextColor", typeof(Color), typeof(ExtendedEntry), Color.Default);
    /// <summary>
    /// Sets color for placeholder text
    /// </summary>
    public Color PlaceholderTextColor
    {
        get { return (Color)GetValue(PlaceholderTextColorProperty); }
        set { SetValue(PlaceholderTextColorProperty, value); }
    }
    #endregion

    #region HasBorder
    /// <summary>
    /// The HasBorder property
    /// </summary>
    public static readonly BindableProperty HasBorderProperty =
        BindableProperty.Create("HasBorder", typeof(bool), typeof(ExtendedEntry), true);




    /// <summary>
    /// Gets or sets if the border should be shown or not
    /// </summary>
    public bool HasBorder
    {
        get { return (bool)GetValue(HasBorderProperty); }
        set { SetValue(HasBorderProperty, value); }
    }
    #endregion

    #region BorderRadius
    public int BorderRadius
    {
        get { return (int)GetValue(BorderRadiusProperty); }
        set { SetValue(BorderRadiusProperty, value); }
    }

    // Using a DependencyProperty as the backing store for BorderRadius.  This enables animation, styling, binding, etc...
    public static readonly BindableProperty BorderRadiusProperty =
       BindableProperty.Create("BorderRadius", typeof(int), typeof(ExtendedEntry), 0);
    #endregion

    #region BorderColor
    /// <summary>
    /// The Border Color Property
    /// </summary>
    public static readonly BindableProperty BorderColorProperty =
        BindableProperty.Create("BorderColor", typeof(Color), typeof(ExtendedEntry), default(Color));

    /// <summary>
    /// Gets or sets the border color
    /// </summary>
    public Color BorderColor
    {
        get { return (Color)GetValue(BorderColorProperty); }
        set { SetValue(BorderColorProperty, value); }
    }
    #endregion
}

Whenever a button is pressed, I call the Validators ValidateAll method to validate all my fields

 var result = Validator.ValidateAll();
        if (!result.IsValid)
        {
            App.Current.MainPage.DisplayAlert("Error", "The forms has errors, please review the errors and fix them.", "Ok");
        }
        Errors = result.AsDictionary();

Errors is another property of the Viewmodel which is basically a dictionary which will hold a list of key-value pairs of property names and errors after validation. I map this value to the ErrorHint property of my ExtendedEntry in Xaml

 <controls:ExtendedEntry ErrorHint="{Binding Errors[UserName]}"
       IsValid="{Binding Errors[UserName],                                                                           Converter={StaticResource DictBool},
                                                  Mode=TwoWay}"
                                Placeholder="sallysmith@gmail.com"
                                Style="{DynamicResource WhiteEntryStandard}"
                                Text="{Binding UserName}" />

And to finish off, this is the implementation for Android

 private void SetValidityAndHint(ExtendedEntry view)
    {

        if (!view.IsValid)
        {
            if (!string.IsNullOrEmpty(view.ErrorHint))
            {
                Control.Error = view.ErrorHint;
            }
        }
    }

This works fine for Android. But I haven't been able to find a solution for iOS. It looks like iOS doesn't have any kind of error property at all. Any pointers anyone??

Answers

  • JohnMillerJohnMiller USForum Administrator, Xamarin Team Xamurai

    @mshenoyOcean,

    I've not seen a custom solution to do that on iOS yet, so hopefully someone else can comment if they have.

    However, I do know that iOS does not have an Error property like Android provides to do things automatically.

  • madhav.shenoy83madhav.shenoy83 AUMember ✭✭

    So does that mean no one does form validation on iOS in this manner? Do we do this via popups?

  • EBREBR FRMember

    @mshenoyOcean

    Hi,
    Does anyone have found the best practice for this point ?
    I'm lookink for a way to do this for UWP and iOS

  • madhav.shenoy83madhav.shenoy83 AUMember ✭✭

    @EBR Since our design team wanted a common visual for both Android and iOS, we changed this. Now we show a label below the textbox, but it still uses the same Dictionary Errors
    <Label IsVisible="{Binding Errors[Password], Converter={StaticResource DictBool}}" Style="{StaticResource ErrorStyleSmall}" Text="{Binding Errors[Password]}" />
    And it looks like this

  • @madhav.shenoy83
    this is exactly what I am looking for do have any small demo to download and have a play?
    many thanks

Sign In or Register to comment.