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

简介: 具有属性的行为Behavior 类派生自Behavior类,该类派生自BindableObject。这表明您的Behavior 派生可以定义自己的可绑定属性。之前你看过一些Action 衍生产品,比如ScaleAction和ShiverAction,它们定义了一些属性以赋予它们更大的灵活性。

具有属性的行为
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中数据绑定的行为是一种强大的技术。

目录
相关文章
|
JavaScript Android开发
第二十三章:触发器和行为(十二)
淡化和定向在本书中,您已经看到了几个颜色选择程序,可以通过使用三个Slider元素以交互方式形成颜色。 本章的最后一个示例是另一个颜色选择程序,但是这个程序为您提供了选项:它包含三个标记为“RGB Hex”,“RGB Float”和“HSL”的单选按钮(实际上是简单的Label元素)。
710 0
|
JavaScript Android开发
第二十三章:触发器和行为(七)
行为 触发器和行为通常是串联讨论的,因为它们具有一些应用重叠。 有时候你会感到困惑是否使用触发器或行为,因为似乎要么这样做工作。你可以用触发器做任何事情,你也可以做一个行为。 但是,行为总是涉及一些代码,这是一个派生自Behavior 的类。
950 0
|
JavaScript Android开发
第二十三章:触发器和行为(十一)
单选按钮内置于旧汽车仪表板中的无线电通常具有一排六个(左右)按钮,可以为各种无线电台“编程”。 按下其中一个按钮会导致无线电跳转到该预选电台,并且还会弹出前一个选择按钮。那些旧的汽车收音机现在是古董,但我们的电脑屏幕上的互斥选项仍然由我们称为单选按钮的视觉对象表示。
912 0
|
JavaScript Android开发
第二十三章:触发器和行为(十)
响应水龙头切换视图的各种表现形式演示了一种响应XAML文件中的点击的方法。 如果将tap事件集成到VisualElement类中,您可以使用EventTrigger更直接且更轻松地获取它们。 但是您无法将EventTrigger附加到TapGestureRecognizer。
546 0
|
JavaScript Android开发 Windows
第二十三章:触发器和行为(九)
切换和复选框在第15章“交互式界面”和第16章“数据绑定”中,您了解了如何构造传统的CheckBox视图。 但是,自定义视图的另一种方法是将视图的交互逻辑合并到行为中,然后完全在XAML中实现视觉效果。
731 0
|
JavaScript Android开发
第二十三章:触发器和行为(六)
MultiTrigger中的组合条件Trigger和DataTrigger都有效地监视属性以确定它是否等于特定值。 这称为触发器的条件,如果条件为真,则调用Setter对象的集合。作为程序员,您可能会开始怀疑是否可以在触发器中具有多个条件。
656 0
|
JavaScript Android开发 iOS开发
第二十三章:触发器和行为(五)
数据触发器到目前为止,您只看到在特定对象的上下文中运行的触发器。 触发器通过更改同一对象的另一个属性或通过调用影响该对象的Action来响应对象属性的更改。 EventTrigger同样响应一个对象触发的事件,以在同一个对象上调用Action。
942 0
|
JavaScript Android开发 Windows
第二十三章:触发器和行为(四)
更多事件触发器前一章关于动画的章节展示了一个按钮,它在点击时旋转或缩放。 虽然大多数动画示例都是为了制作有趣的演示而采取极端措施,但是按钮用一点动画来响应点击并不是不合理的。 这是EventTrigger的完美工作。
782 0
|
JavaScript Android开发
第二十三章:触发器和行为(三)
触发动作和动画虽然某些触发器可以完全在XAML中实现,但其他触发器需要一些代码支持。 如您所知,Xamarin.Forms没有直接支持在XAML中实现动画,因此如果您想使用触发器为元素设置动画,则需要一些代码。
1027 0
|
JavaScript Android开发
第二十三章:触发器和行为(二)
触发器 在最普遍(和最模糊)的意义上,触发器是导致响应的条件。 更具体地说,触发器通过设置另一个属性或运行一些代码来响应属性更改或触发事件。 几乎总是,设置的属性或运行的代码涉及用户界面,并在XAML中表示。
981 0