官术网_书友最值得收藏!

Creating behaviors

Behaviors are an eloquent use of the decorator pattern, allowing the developers to modify the Xamarin.Forms controls without having to create derived controls.

A simple example of creating a behavior would be to implement a validation behavior in our LoginView. As you may remember, we actually used the Command.CanExecute delegate to validate our fields. In this example, we will separate the validators for the email field and the password field. This way, we can allow the UI to give feedback to the user as a result of an incorrect entry. This would be more user-friendly than only disabling the login window. To set this up, follow these steps:

  1. First, we need to create a validation rule infrastructure, starting with the validation interface:
 public interface IValidationRule<T>
{
string ValidationMessage { get; set; }
bool Validate (T value);
}
  1. A simple implementation of this rule would be required so that we can check whether we have a short validation message stating that the field is a required field:
 public class RequiredValidationRule : IValidationRule<string>
{
public string ValidationMessage { get; set; } = "This field is
a required field";

public bool Validate (string value)
{
return !string.IsNullOrEmpty(value);
}
}

  1. Now, we can create our validation behavior for the Entry field, which will make use of any given validation rule (starting with RequiredValidationRule, which we just implemented):
 public class ValidationBehavior : Behavior<Entry>
{

protected override void OnAttachedTo(Entry bindable)
{
base.OnAttachedTo(bindable);

bindable.TextChanged += ValidateField;
}

protected override void OnDetachingFrom(Entry bindable)
{
base.OnDetachingFrom(bindable);

bindable.TextChanged -= ValidateField;
}

private void ValidateField(object sender, TextChangedEventArgs
args)
{
if (sender is Entry entry)
{
// TODO:
}
}
}

In this implementation, the OnAttachedTo and OnDetachingFrom methods are the crucial access points and the teardown logic implementations. In this case, when the behavior is attached to a target control, we are subscribing to the TextChanged event, and when the behavior is removed, we are unsubscribing from the event so that undesired memory leak issues are avoided.

  1. The next order of business will be to implement a bindable property for the validation rule so that the validation rules are dictated by the view model (or another business logic module), decoupling it from the view:
public static readonly BindableProperty ValidationRuleProperty =
BindableProperty.CreateAttached("ValidationRule", typeof(IValidationRule<string>), typeof(ValidationBehavior), null);


public static readonly BindableProperty HasErrorProperty =
BindableProperty.CreateAttached("HasError", typeof(bool), typeof(ValidationBehavior), false, BindingMode.TwoWay);

public IValidationRule<string> ValidationRule
{
get { return this.GetValue(ValidationRuleProperty) as IValidationRule<string>; }
set { this.SetValue(ValidationRuleProperty, value); }
}

public bool HasError
{
get { return (bool) GetValue(HasErrorProperty); }
set { SetValue(HasErrorProperty, value); }
}
  1. Now that we have an outlet for the validation rule and an output field (so that we can attach additional UX logic to it), we can implement the validate method:
 private void ValidateField(object sender, TextChangedEventArgs 
args)
{
if (sender is Entry entry && ValidationRule != null)
{
if (!ValidationRule.Validate(args.NewTextValue))
{
entry.BackgroundColor = Color.Crimson;
HasError = true;
}
else
{
entry.BackgroundColor = Color.White;
HasError = false;
}
}
}

  1. After adding the appropriate rule to the view model property (in this case, UserNameValidation), we can bind the behavior to the validation rule that's exposed from the view model and observe the entry field behavior according to the text input:
<Entry x:Name="usernameEntry" Placeholder="username" Text="{Binding UserName, Mode=OneWayToSource}" >
<Entry.Behaviors>
<behaviors:ValidationBehavior x:Name="UserNameValidation"
ValidationRule="{Binding
BindingContext.UserNameValidation,
Source={x:Reference RootView}}" />

</Entry.Behaviors>
</Entry>

Here, the main benefit is that we do not have to modify the Entry field, and the implemented behavior can be maintained as a separate module.

It is important to note that the binding context for a behavior is not the same as the page layout or the view, which is why the source of the binding value for the validation rule has to reference the page itself and use BindingContext as part of the binding path.
  1. To extend this implementation, we can add a validation error message label that will display in accordance with the HasError bindable property (anywhere on the page layout, as long as the UserNameValidation element is accessible):
<Label Text="UserName is required" FontSize="12" TextColor="Gray" 
IsVisible="{Binding HasError, Source={x:Reference UserNameValidation}}"/>
  1. The outcome would look similar to the following:

主站蜘蛛池模板: 闽侯县| 虞城县| 盐源县| 宜君县| 麻江县| 合水县| 那坡县| 金昌市| 温州市| 海原县| 固始县| 绵阳市| 湟源县| 任丘市| 本溪市| 屯门区| 容城县| 定南县| 西华县| 龙海市| 平乐县| 泰宁县| 定远县| 建平县| 泊头市| 中西区| 凤城市| 大关县| 沂南县| 枞阳县| 集安市| 澄江县| 太湖县| 稻城县| 方山县| 清新县| 阜新市| 克拉玛依市| 湄潭县| 宿迁市| 客服|