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

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:

主站蜘蛛池模板: 土默特右旗| 龙陵县| 长海县| 图木舒克市| 云霄县| 明溪县| 壤塘县| 浦东新区| 上林县| 常宁市| 天门市| 西乡县| 肇州县| 陈巴尔虎旗| 林口县| 自治县| 泸水县| 越西县| 灌云县| 锡林郭勒盟| 太原市| 汽车| 阜南县| 中宁县| 临沭县| 托里县| 郑州市| 延庆县| 方城县| 藁城市| 藁城市| 大方县| 应用必备| 东阿县| 广汉市| 卓尼县| 锡林浩特市| 红安县| 绥江县| 育儿| 沽源县|