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

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:

主站蜘蛛池模板: 云和县| 贵定县| 开化县| 轮台县| 保德县| 东山县| 洪湖市| 体育| 区。| 宁明县| 英德市| 莒南县| 宁波市| 衡水市| 玉龙| 绵阳市| 晴隆县| 惠安县| 射阳县| 贵港市| 密云县| 宿迁市| 扶风县| 孝昌县| 松滋市| 鹤壁市| 嘉鱼县| 新丰县| 开封县| 黄浦区| 军事| 板桥市| 兴海县| 梓潼县| 铁岭市| 利川市| 石景山区| 石门县| 玉环县| 阿拉尔市| 库尔勒市|