The first thing, maybe you should look at this post:
https://blog.xamarin.com/behaviors-in-xamarin-forms/
Base on this, I do some changing code.
- In the real thing, you may like to validate many condition for one controls (such as: Require and email format, require and maxlength...). To make it easier I create validation class to validate for each control type, instead of each rule. Let's see the code is changed:
1:
3: using Xamarin.Forms;
4:
5: namespace MyProject.Client.Framework.Validators
6: {
7: public class EntryValidatorBehavior : Behavior<Entry>
8: {
9: #region Properties
10:
11: //Result Boolean
12: private static readonly BindablePropertyKey IsValidPropertyKey = BindableProperty.CreateReadOnly("IsValid",
13: typeof(bool), typeof(EntryValidatorBehavior), false);
14:
15: public static readonly BindableProperty IsValidProperty = IsValidPropertyKey.BindableProperty;
16:
17: public bool IsValid
18: {
19: get { return (bool)GetValue(IsValidProperty); }
20: private set { SetValue(IsValidPropertyKey, value); }
21: }
22:
23: //Result message
24: public static readonly BindableProperty MessageProperty = BindableProperty.Create("Message",
25: typeof(string), typeof(EntryValidatorBehavior), string.Empty);
26:
27: public string Message
28: {
29: get { return (string)GetValue(MessageProperty); }
30: private set { SetValue(MessageProperty, value); }
31: }
32:
33: //Is check empty
34: public static BindableProperty IsCheckEmptyProperty = BindableProperty.Create("IsCheckEmpty",
35: typeof(bool), typeof(EntryValidatorBehavior), false);
36:
37: public bool IsCheckEmpty
38: {
39: get { return (bool)GetValue(IsCheckEmptyProperty); }
40: set { SetValue(IsCheckEmptyProperty, value); }
41: }
42:
43: //Is check email
44: public static BindableProperty IsCheckEmailProperty = BindableProperty.Create("IsCheckEmail",
45: typeof(bool), typeof(EntryValidatorBehavior), false);
46:
47: public bool IsCheckEmail
48: {
49: get { return (bool)GetValue(IsCheckEmailProperty); }
50: set { SetValue(IsCheckEmailProperty, value); }
51: }
52:
53: #endregion Properties
54:
55: protected override void OnAttachedTo(Entry bindable)
56: {
57: bindable.TextChanged += HandleTextChanged;
58: }
59:
60: private void HandleTextChanged(object sender, TextChangedEventArgs e)
61: {
62: if (IsCheckEmpty)
63: {
64: IsValid = ValidatorsFactory.IsValidEmpty(e.NewTextValue);
65: Message = Messages.FieldCannotBlank;
66: if (!IsValid)
67: {
68: ((Entry)sender).TextColor = Color.Red;
69: return;
70: }
71: }
72: if (IsCheckEmail)
73: {
74: IsValid = ValidatorsFactory.IsValidEmail(e.NewTextValue);
75: Message = Messages.EmailIncorrectFormat;
76: if (!IsValid)
77: {
78: ((Entry)sender).TextColor = Color.Red;
79: return;
80: }
81: }
82: //TODO: add more validation
83:
84: //Default
85: IsValid = true;
86: Message = string.Empty;
87: ((Entry)sender).TextColor = AppColors.EntryTextColor;
88: }
89:
90: protected override void OnDetachingFrom(Entry bindable)
91: {
92: bindable.TextChanged -= HandleTextChanged;
93: }
94: }
95: }
* Note: It's contain some constants and reference to my source code so if you just copy&paste, it will not work, let's try to understand it.
Some thing changed here:
- The first thing I add Message property to provide different validate message of each error for client to display. You see I set value for the Message in HandleTextChanged method.
- Each kind of validate will have an option to validate it or not. In my code you will see: IsCheckEmpty, IsCheckEmail that you can control what you wanna validate for Entry or not.
- The HandleTextChanged method ( or handle another event) will do validate and set value for IsValid and Message. You may wonder what is ValidatorsFactory, that the logic of validate I moved to other class call ValidatorsFactory.
Let's see how that factory work:
1: using System;
2: using System.Text.RegularExpressions;
3:
4: namespace MyProject.Client.Framework.Validators
5: {
6: public static class ValidatorsFactory
7: {
8: private const string EmailRegex =
9: @"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
10: @"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$";
11:
12: public static bool IsValidEmail(string input)
13: {
14: return (Regex.IsMatch(input, EmailRegex, RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250)));
15: }
16:
17: public static bool IsValidEmpty(string input)
18: {
19: return !string.IsNullOrWhiteSpace(input);
20: }
21:
22: public static bool IsValidMaxlength(string input, int maxlength)
23: {
24: return input.Length <= maxlength;
25: }
26:
27: //TODO: add more validation
28: }
29: }
Then, how to use. This example I used in sign-in page:
1: <controls:ExtendedEntry Grid.Row="0" Keyboard="Email" Text="{Binding Model.Email, Mode=OneWayToSource}" Placeholder="Email">
2: <Entry.Behaviors>
3: <validators:EntryValidatorBehavior IsCheckEmpty="True" IsCheckEmail="True" x:Name="EmailValidator"/>
4: </Entry.Behaviors>
5: </controls:ExtendedEntry>
6: <Label Grid.Row="1" TextColor="{x:Static styles:AppColors.LabelErrorTextColor}"
7: Text="{Binding Source={x:Reference EmailValidator}, Path=Message}"
8: VerticalOptions="Start" HorizontalOptions="Start"
9: IsVisible="{Binding Source={x:Reference EmailValidator}, Path=IsValid,Converter={StaticResource InvertBoolConverter}}"/>
Thank you for reading.
Edit: Please see the updated version here: http://blog.zquanghoangz.com/?p=139