Я пытаюсь добавить проверки в Xamarin. Для этого я использовал этот пост в качестве ориентира: Проверка с использованием аннотации данных. Следующее - это мое поведение.
public class EntryValidationBehavior : Behavior<Entry>
{
private Entry _associatedObject;
protected override void OnAttachedTo(Entry bindable)
{
base.OnAttachedTo(bindable);
// Perform setup
_associatedObject = bindable;
_associatedObject.TextChanged += _associatedObject_TextChanged;
}
void _associatedObject_TextChanged(object sender, TextChangedEventArgs e)
{
var source = _associatedObject.BindingContext as ValidationBase;
if (source != null && !string.IsNullOrEmpty(PropertyName))
{
var errors = source.GetErrors(PropertyName).Cast<string>();
if (errors != null && errors.Any())
{
var borderEffect = _associatedObject.Effects.FirstOrDefault(eff => eff is BorderEffect);
if (borderEffect == null)
{
_associatedObject.Effects.Add(new BorderEffect());
}
if (Device.OS != TargetPlatform.Windows)
{
//_associatedObject.BackgroundColor = Color.Red;
}
}
else
{
var borderEffect = _associatedObject.Effects.FirstOrDefault(eff => eff is BorderEffect);
if (borderEffect != null)
{
_associatedObject.Effects.Remove(borderEffect);
}
if (Device.OS != TargetPlatform.Windows)
{
_associatedObject.BackgroundColor = Color.Default;
}
}
}
}
protected override void OnDetachingFrom(Entry bindable)
{
base.OnDetachingFrom(bindable);
// Perform clean up
_associatedObject.TextChanged -= _associatedObject_TextChanged;
_associatedObject = null;
}
public string PropertyName { get; set; }
}
В моем Поведении я добавляю фон и границу как красный. Я хочу автоматически добавить ярлык к этой записи. Поэтому я подумал о том, чтобы добавить stacklayout над этой записью и добавить ярлык и эту запись в нем. Очень утомительно писать этикетку для каждого элемента управления. Возможно ли это или может быть каким-то другим способом?
Обновленный метод (неэффективен):
<Entry Text="{Binding Email}" Placeholder="Enter Email ID" Keyboard="Email" HorizontalTextAlignment="Center">
<Entry.Behaviors>
<validation:EntryValidationBehavior PropertyName="Email" />
</Entry.Behaviors>
</Entry>
<Label Text="{Binding Errors[Email], Converter={StaticResource FirstErrorConverter}"
IsVisible="{Binding Errors[Email], Converter={StaticResource ErrorLabelVisibilityConverter}"
FontSize="Small"
TextColor="Red" />
<Entry Text="{Binding Password}" Placeholder="Enter Password" Keyboard="Text" IsPassword="true" HorizontalTextAlignment="Center">
<Entry.Behaviors>
<validation:EntryValidationBehavior PropertyName="Password" />
</Entry.Behaviors>
</Entry>
<Label Text="{Binding Errors[Password], Converter={StaticResource FirstErrorConverter}"
IsVisible="{Binding Errors[Password], Converter={StaticResource ErrorLabelVisibilityConverter}"
FontSize="Small"
TextColor="Red" />
<Entry Text="{Binding ConfirmPassword}" Placeholder="Confirm Password" Keyboard="Text" IsPassword="true" HorizontalTextAlignment="Center">
<Entry.Behaviors>
<validation:EntryValidationBehavior PropertyName="ConfirmPassword" />
</Entry.Behaviors>
</Entry>
конвертер
public class FirstErrorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ICollection<string> errors = value as ICollection<string>;
return errors != null && errors.Count > 0 ? errors.ElementAt(0) : null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Оценщик:
public class ValidationBase : BindableBase, INotifyDataErrorInfo
{
private Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();
public Dictionary<string, List<string>> Errors
{
get { return _errors; }
}
public ValidationBase()
{
ErrorsChanged += ValidationBase_ErrorsChanged;
}
private void ValidationBase_ErrorsChanged(object sender, DataErrorsChangedEventArgs e)
{
OnPropertyChanged("HasErrors");
OnPropertyChanged("Errors");
OnPropertyChanged("ErrorsList");
}
#region INotifyDataErrorInfo Members
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public IEnumerable GetErrors(string propertyName)
{
if (!string.IsNullOrEmpty(propertyName))
{
if (_errors.ContainsKey(propertyName) && (_errors[propertyName].Any()))
{
return _errors[propertyName].ToList();
}
else
{
return new List<string>();
}
}
else
{
return _errors.SelectMany(err => err.Value.ToList()).ToList();
}
}
public bool HasErrors
{
get
{
return _errors.Any(propErrors => propErrors.Value.Any());
}
}
#endregion
protected virtual void ValidateProperty(object value, [CallerMemberName] string propertyName = null)
{
var validationContext = new ValidationContext(this, null)
{
MemberName = propertyName
};
var validationResults = new List<ValidationResult>();
Validator.TryValidateProperty(value, validationContext, validationResults);
RemoveErrorsByPropertyName(propertyName);
HandleValidationResults(validationResults);
RaiseErrorsChanged(propertyName);
}
private void RemoveErrorsByPropertyName(string propertyName)
{
if (_errors.ContainsKey(propertyName))
{
_errors.Remove(propertyName);
}
// RaiseErrorsChanged(propertyName);
}
private void HandleValidationResults(List<ValidationResult> validationResults)
{
var resultsByPropertyName = from results in validationResults
from memberNames in results.MemberNames
group results by memberNames into groups
select groups;
foreach (var property in resultsByPropertyName)
{
_errors.Add(property.Key, property.Select(r => r.ErrorMessage).ToList());
// RaiseErrorsChanged(property.Key);
}
}
private void RaiseErrorsChanged(string propertyName)
{
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
}
public IList<string> ErrorsList
{
get
{
return GetErrors(string.Empty).Cast<string>().ToList();
}
}
}
Проблема с этим решением заключается в том, что FirstErrorConverter вызывается для каждого свойства на странице каждый раз при изменении любого из свойств. Так, например, есть 10 свойств, которые необходимо проверить. Метод будет называться 10 раз. Во-вторых, Красная граница занимает около секунды, чтобы отображать ее в первый раз.