第二十三章:触发器和行为(八)

具有属性的行为
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>

在您输入电子邮件地址时,只有至少有两个字符的顶级域名才会被视为有效:
2019_04_08_100706
这两个位图是常见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中数据绑定的行为是一种强大的技术。

猜你喜欢

转载自yq.aliyun.com/articles/697100