Xamarin Forms iOS Validate Texbox and show error

madhav.shenoy83madhav.shenoy83 ✭✭AUMember ✭✭

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="[email protected]"
                                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 Xamurai 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

  • learningMobilelearningMobile ✭✭ USMember ✭✭

    @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.