I have a custom Xamarin Forms component with a Required property that I am setting to True in my viewmodel. In the constructor, I call a method CheckValidity() which checks whether the entry is required. For some reason, Required is showing up as false until I type in the entry (triggering the Text property to update) or click in or out of the entry (triggering the Unfocused event).
Any idea why the initial True value of Required is not taking effect until some activity occurs in my component? Thanks!
Use in View
<ui:ValidatingEntry Text="{Binding MyText}" Required="True" />
Component XAML
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MyPackage.ValidatingEntry">
<ContentView.Content>
<StackLayout x:Name="entryContainer">
<Entry x:Name="entry" />
<Label x:Name="message" />
</StackLayout>
</ContentView.Content>
</ContentView>
Component C#
public partial class ValidatingEntry : ContentView
{
private enum ValidationErrorType
{
NONE,
REQUIRED,
CUSTOM
}
private ValidationErrorType validationErrorType;
public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(ValidatingEntry), default(string), BindingMode.TwoWay);
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
Debug.WriteLine("set text to: " + value);
CheckValidity();
UpdateMessage();
}
}
public static readonly BindableProperty RequiredProperty = BindableProperty.Create("Required", typeof(bool), typeof(ValidatingEntry), false);
public bool Required
{
get
{
Debug.WriteLine("getting required property: " + (bool)GetValue(RequiredProperty));
return (bool)GetValue(RequiredProperty);
}
set
{
SetValue(RequiredProperty, value);
//THIS NEVER PRINTS
Debug.WriteLine("set required property to: " + value);
CheckValidity();
}
}
public static readonly BindableProperty IsValidProperty = BindableProperty.Create("IsValid", typeof(bool), typeof(ValidatingEntry), true, BindingMode.OneWayToSource);
public bool IsValid
{
get
{
return (bool)GetValue(IsValidProperty);
}
set
{
SetValue(IsValidProperty, value);
}
}
private void CheckValidity()
{
Debug.WriteLine("checking validity");
Debug.WriteLine("required? " + Required); //prints False until Entry is unfocused or user types in Entry
Debug.WriteLine("string empty? " + string.IsNullOrEmpty(Text));
if (Required && string.IsNullOrEmpty(Text))
{
Debug.WriteLine("required but not provided");
IsValid = false;
validationErrorType = ValidationErrorType.REQUIRED;
}
else
{
IsValid = true;
validationErrorType = ValidationErrorType.NONE;
}
}
private void UpdateMessage()
{
switch (validationErrorType)
{
case ValidationErrorType.NONE:
message.Text = "";
break;
case ValidationErrorType.REQUIRED:
message.Text = "This field is required.";
break;
}
}
public ValidatingEntry()
{
InitializeComponent();
entry.SetBinding(Entry.TextProperty, new Binding("Text", source: this));
CheckValidity(); //at this point, Required is always false
entry.Unfocused += (sender, e) =>
{
CheckValidity();
UpdateMessage();
};
}
}