具有属性的行为
Behavior 类派生自Behavior类,该类派生自BindableObject。这表明您的Behavior 派生可以定义自己的可绑定属性。
之前你看过一些Action 衍生产品,比如ScaleAction和ShiverAction,它们定义了一些属性以赋予它们更大的灵活性。但是Behavior 派生可以定义可绑定属性,可以作为数据绑定的源属性。这意味着您不必对行为进行硬编码以修改特定属性,例如将Entry的TextColor属性设置为Red。您可以稍后决定希望行为如何影响用户界面,并在XAML文件中实现该权限。这为行为提供了更大的灵活性,并允许XAML在与用户界面相关的行为方面发挥更大的作用。
这是一个名为ValidEmailBehavior的Xamarin.FormsBook.Toolkit库中的类,它与NumericValidationBehavior类似,只是它使用正则表达式来确定Entry的Text属性是否是有效的电子邮件地址:
amespace Xamarin.FormsBook.Toolkit
{
public class ValidEmailBehavior : Behavior<Entry>
{
static readonly BindablePropertyKey IsValidPropertyKey =
BindableProperty.CreateReadOnly("IsValid",
typeof(bool),
typeof(ValidEmailBehavior),
false);
public static readonly BindableProperty IsValidProperty =
IsValidPropertyKey.BindableProperty;
public bool IsValid
{
private set { SetValue(IsValidPropertyKey, value); }
get { return (bool)GetValue(IsValidProperty); }
}
protected override void OnAttachedTo(Entry entry)
{
entry.TextChanged += OnEntryTextChanged;
base.OnAttachedTo(entry);
}
protected override void OnDetachingFrom(Entry entry)
{
entry.TextChanged -= OnEntryTextChanged;
base.OnDetachingFrom(entry);
}
void OnEntryTextChanged(object sender, TextChangedEventArgs args)
{
Entry entry = (Entry)sender;
IsValid = IsValidEmail(entry.Text);
}
bool IsValidEmail(string strIn)
{
if (String.IsNullOrEmpty(strIn))
return false;
try
{
// from https://msdn.microsoft.com/en-us/library/01escwtf(v=vs.110).aspx
return Regex.IsMatch(strIn,
@"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|" +
@"[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)" +
@"(?<=[0-9a-z])@))(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|" +
@"(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$",
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));
}
catch (RegexMatchTimeoutException)
{
return false;
}
}
}
}
ValidEmailBehavior不是将Entry的Text属性设置为Red,而是定义由可绑定属性支持的IsValid属性。因为此类外部的代码设置IsValid属性没有意义,所以它是只读的可绑定属性。 Bindable.CreateReadOnly调用创建一个私有的可绑定属性密钥,该密钥由IsValid的私有集访问器中的SetValue调用使用。像往常一样,GetValue调用引用公共IsValidProperty可绑定属性。
您如何使用该IsValid属性完全取决于您。
例如,EmailValidationDemo程序将IsValid属性绑定到显示“拇指向上”图片的图像的IsVisible属性。 “拇指向上”位图位于另一个带有“拇指向下”的Image元素的顶部,以指示何时键入了有效的电子邮件地址。 IsValid属性也绑定到“发送”按钮的IsEnabled属性。请注意,两个数据绑定的Source都是ValidEmailBehavior对象:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:EmailValidationDemo"
xmlns:toolkit=
"clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
x:Class="EmailValidationDemo.EmailValidationDemoPage"
Padding="20, 50">
<StackLayout>
<StackLayout Orientation="Horizontal">
<Entry Placeholder="Enter email address"
Keyboard="Email"
HorizontalOptions="FillAndExpand">
<Entry.Behaviors>
<toolkit:ValidEmailBehavior x:Name="validEmail" />
</Entry.Behaviors>
</Entry>
<Grid HeightRequest="40">
<Image Source=
"{local:ImageResource EmailValidationDemo.Images.ThumbsDown.png}" />
<Image Source="{local:ImageResource EmailValidationDemo.Images.ThumbsUp.png}"
IsVisible="{Binding Source={x:Reference validEmail},
Path=IsValid}"/>
</Grid>
</StackLayout>
<Button Text="Send!"
FontSize="Large"
HorizontalOptions="Center"
IsEnabled="{Binding Source={x:Reference validEmail},
Path=IsValid}" />
</StackLayout>
</ContentPage>
在您输入电子邮件地址时,只有至少有两个字符的顶级域名才会被视为有效:
这两个位图是常见EmailValidationDemo项目的一部分。 用于引用位图的ImageResource标记扩展类在第13章“位图”中讨论过,它必须是包含位图的同一程序集的一部分:
namespace EmailValidationDemo
{
[ContentProperty ("Source")]
public class ImageResourceExtension : IMarkupExtension
{
public string Source { get; set; }
public object ProvideValue (IServiceProvider serviceProvider)
{
if (Source == null)
return null;
return ImageSource.FromResource(Source);
}
}
}
如果您在同一页面上有多个需要检查有效电子邮件地址的条目视图,该怎么办? 你可以在Style的Behaviors集合中包含ValidEmailBehavior类吗?
你不能。 ValidEmailBehavior类定义名为IsValid的属性。 这意味着ValidEmailBehavior的特定实例始终具有特定状态,即此属性的值。 这具有重要意义:
维护状态(例如字段或属性)的行为无法共享,这意味着它不应包含在样式中。
如果需要在同一页面上对多个Entry视图使用ValidEmailBehavior,请不要将其放在样式中。 将单独的实例添加到每个Entry视图的Behaviors集合中。
但是,这种IsValid属性的优点胜过缺点,因为您可以通过各种方式使用该属性。 这是一个名为EmailValidationConverter的程序,它使用IsValid属性和Xamarin.FormsBook.Toolkit库中已有的绑定转换器来在两个文本字符串之间进行选择:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit=
"clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
x:Class="EmailValidationConverter.EmailValidationConverterPage"
Padding="50">
<StackLayout>
<StackLayout Orientation="Horizontal">
<Entry Placeholder="Enter email address"
HorizontalOptions="FillAndExpand">
<Entry.Behaviors>
<toolkit:ValidEmailBehavior x:Name="validEmail" />
</Entry.Behaviors>
</Entry>
<Label HorizontalTextAlignment="Center"
VerticalTextAlignment="Center">
<Label.Text>
<Binding Source="{x:Reference validEmail}"
Path="IsValid">
<Binding.Converter>
<toolkit:BoolToObjectConverter x:TypeArguments="x:String"
FalseObject="Not yet!"
TrueObject="OK!" />
</Binding.Converter>
</Binding>
</Label.Text>
</Label>
</StackLayout>
<Button Text="Send!"
FontSize="Large"
HorizontalOptions="Center"
IsEnabled="{Binding Source={x:Reference validEmail},
Path=IsValid}" />
</StackLayout>
</ContentPage>
BoolToObjectConverter在两个文本字符串“Not yet!”和“OK!”之间进行选择。
但是,正如EmailValidationTrigger程序演示的那样,使用DataTrigger可以通过更直接的逻辑和无绑定转换器来执行相同的操作。 “Not yet!”文本被分配给Label的Text属性,而Label上的DataTrigger包含对IsValid属性的绑定以设置“OK!”文本:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit=
"clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
x:Class="EmailValidationTrigger.EmailValidationTriggerPage"
Padding="50">
<StackLayout>
<StackLayout Orientation="Horizontal">
<Entry Placeholder="Enter email address"
HorizontalOptions="FillAndExpand">
<Entry.Behaviors>
<toolkit:ValidEmailBehavior x:Name="validEmail" />
</Entry.Behaviors>
</Entry>
<Label Text="Not yet!"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center">
<Label.Triggers>
<DataTrigger TargetType="Label"
Binding="{Binding Source={x:Reference validEmail},
Path=IsValid}"
Value="True">
<Setter Property="Text" Value="OK!" />
</DataTrigger>
</Label.Triggers>
</Label>
</StackLayout>
<Button Text="Send!"
FontSize="Large"
HorizontalOptions="Center"
IsEnabled="{Binding Source={x:Reference validEmail},
Path=IsValid}" />
</StackLayout>
</ContentPage>
引用DataTrigger中数据绑定的行为是一种强大的技术。