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

简介: MultiTrigger中的组合条件Trigger和DataTrigger都有效地监视属性以确定它是否等于特定值。 这称为触发器的条件,如果条件为真,则调用Setter对象的集合。作为程序员,您可能会开始怀疑是否可以在触发器中具有多个条件。

MultiTrigger中的组合条件
Trigger和DataTrigger都有效地监视属性以确定它是否等于特定值。 这称为触发器的条件,如果条件为真,则调用Setter对象的集合。
作为程序员,您可能会开始怀疑是否可以在触发器中具有多个条件。 但是一旦你开始谈论多个条件,你需要确定是否要将条件与逻辑OR运算或AND运算结合起来 - 如果任何条件为真,是否调用触发器,或者是否需要所有条件是真的。
如果您希望在多个条件都为真时调用触发器 - 逻辑AND情况 - 这是从TriggerBase派生的四个类中的最后一个。 MultiTrigger定义了两个集合属性:

  • IList类型的Conditions
  • IList类型的setter

Condition是一个抽象类,有两个实现类:

  • PropertyCondition,具有属性和值属性,如Trigger
  • BindingCondition,它具有像DataTrigger这样的Binding和Value属性

您可以在MultiTrigger的相同Conditions集合中混合多个PropertyCondition和BindingCondition对象。 当所有条件都为真时,将应用Setters集合中的所有Setter对象。
让我们看一个简单的例子:在AndConditions程序中,四个Switch元素与蓝色BoxView共享页面。 当所有Switch元素都打开时,BoxView变为红色:
2019_04_04_151719
XAML文件显示了如何完成此操作。 BoxView的Triggers集合包含MultiTrigger。 TargetType属性是必需的。 Conditions集合包含四个BindingCondition对象,每个对象引用四个Switch元素之一的IsToggled属性并检查True值。 如果所有条件都为真,则MultiTrigger将BoxView的Color属性设置为Red:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="AndConditions.AndConditionsPage">
    <StackLayout>
        <Grid VerticalOptions="CenterAndExpand">
            <Switch x:Name="switch1" Grid.Column="0"
                    HorizontalOptions="Center" />
 
            <Switch x:Name="switch2" Grid.Column="1"
                    HorizontalOptions="Center" />
 
            <Switch x:Name="switch3" Grid.Column="2"
                    HorizontalOptions="Center" />
 
            <Switch x:Name="switch4" Grid.Column="3"
                    HorizontalOptions="Center" />
        </Grid>
        <BoxView WidthRequest="100"
                 HeightRequest="100"
                 VerticalOptions="CenterAndExpand"
                 HorizontalOptions="Center"
                 Color="Blue">
            <BoxView.Triggers>
                <MultiTrigger TargetType="BoxView">
                    <MultiTrigger.Conditions>
                        <BindingCondition Binding="{Binding Source={x:Reference switch1},
                                                            Path=IsToggled}"
                                          Value="True" />
                        <BindingCondition Binding="{Binding Source={x:Reference switch2},
                                                            Path=IsToggled}"
                                          Value="True" />
                        <BindingCondition Binding="{Binding Source={x:Reference switch3},
                                                            Path=IsToggled}"
                                          Value="True" />
                        <BindingCondition Binding="{Binding Source={x:Reference switch4},
                                                            Path=IsToggled}"
                                          Value="True" />
                    </MultiTrigger.Conditions>
                    <Setter Property="Color" Value="Red" />
                </MultiTrigger>
            </BoxView.Triggers>
        </BoxView>
    </StackLayout>
</ContentPage>

那就是AND组合。 OR组合怎么样?
因为Triggers集合可以容纳多个DataTrigger对象,所以您可能认为这可以工作:

<BoxView WidthRequest="100"
         HeightRequest="100"
         VerticalOptions="CenterAndExpand"
         HorizontalOptions="Center"
         Color="Blue">
    <BoxView.Triggers>
        <DataTrigger TargetType="BoxView"
                     Binding="{Binding Source={x:Reference switch1}, Path=IsToggled}"
                     Value="True">
            <Setter Property="Color" Value="Red" />
        </DataTrigger>
        <DataTrigger TargetType="BoxView"
                     Binding="{Binding Source={x:Reference switch2}, Path=IsToggled}"
                     Value="True">
            <Setter Property="Color" Value="Red" />
        </DataTrigger>
        <DataTrigger TargetType="BoxView"
                     Binding="{Binding Source={x:Reference switch3}, Path=IsToggled}"
                     Value="True">
            <Setter Property="Color" Value="Red" />
        </DataTrigger>
 
        <DataTrigger TargetType="BoxView"
                     Binding="{Binding Source={x:Reference switch4}, Path=IsToggled}"
                     Value="True">
            <Setter Property="Color" Value="Red" />
        </DataTrigger>
    </BoxView.Triggers>
</BoxView>

如果你尝试它,你可能会发现它似乎确实起作用。 但是当你进一步尝试打开和关闭各种开关元件时,你会发现它确实不起作用。
它是否应该起作用或不起作用是值得商榷的。 四个DataTrigger对象都以相同的Color属性为目标,如果每个DataTrigger独立工作以确定是否应该应用该Setter,那么这实际上不应该作为逻辑OR。
但是,请记住维多利亚时代的数学家奥古斯都德摩根的逻辑定律,其中说明了(使用C#语法进行AND,OR,逻辑否定和等价):

𝐴 | 𝐵 == ! (! 𝐴 & !𝐵)
𝐴 & 𝐵 == ! (!𝐴 | !𝐵)

这意味着您可以使用MultiTrigger执行逻辑OR,如OrConditions程序演示:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="OrConditions.OrConditionsPage">
    <StackLayout>
        <Grid VerticalOptions="CenterAndExpand">
            <Switch x:Name="switch1" Grid.Column="0"
                    HorizontalOptions="Center" />
            <Switch x:Name="switch2" Grid.Column="1"
                    HorizontalOptions="Center" />
            <Switch x:Name="switch3" Grid.Column="2"
                    HorizontalOptions="Center" />
            <Switch x:Name="switch4" Grid.Column="3"
                    HorizontalOptions="Center" />
        </Grid>
        <BoxView WidthRequest="100"
                 HeightRequest="100"
                 VerticalOptions="CenterAndExpand"
                 HorizontalOptions="Center"
                 Color="Red">
            <BoxView.Triggers>
                <MultiTrigger TargetType="BoxView">
                    <MultiTrigger.Conditions>
                        <BindingCondition Binding="{Binding Source={x:Reference switch1},
                                                            Path=IsToggled}"
                                          Value="False" />
                        <BindingCondition Binding="{Binding Source={x:Reference switch2},
                                                            Path=IsToggled}"
                                          Value="False" />
                        <BindingCondition Binding="{Binding Source={x:Reference switch3},
                                                            Path=IsToggled}"
                                          Value="False" />
 
                        <BindingCondition Binding="{Binding Source={x:Reference switch4},
                                                            Path=IsToggled}"
                                          Value="False" />
                    </MultiTrigger.Conditions>
                    <Setter Property="Color" Value="Blue" />
                </MultiTrigger>
            </BoxView.Triggers>
        </BoxView>
    </StackLayout>
</ContentPage>

除了所有逻辑都被翻转之外,它与AndConditions相同。 所有BindingCondition对象都会检查IsToggled属性的False值,如果满足所有条件,则通常为红色的BoxView将显示为蓝色:
2019_04_04_152944
这是你可以想到这两个程序的另一种方式:在AndConditions中,除非切换所有Switch元素,否则BoxView始终是蓝色的。 在OrConditions中,除非所有Switch元素都关闭,否则BoxView始终为红色。
假设您有一个涉及两个Entry字段和一个Button的场景。 如果“输入”字段包含某些文本,则需要启用“按钮”。
翻转逻辑:如果两个Entry字段都不包含文本,您确实要禁用Button。 这很容易:

<StackLayout>
    <Entry x:Name="entry1"
           Text="" />
    <Entry x:Name="entry2"
           Text="" />
    <Button Text="Send">
        <Button.Triggers>
            <MultiTrigger TargetType="Button">
                <MultiTrigger.Conditions>
                    <BindingCondition Binding="{Binding Source={x:Reference entry1},
                                                        Path=Text.Length}"
                                     Value="0" />
                    <BindingCondition Binding="{Binding Source={x:Reference entry2},
                                                        Path=Text.Length}"
                                     Value="0" />
                </MultiTrigger.Conditions>
                <Setter Property="IsEnabled" Value="False" />
            </MultiTrigger>
        </Button.Triggers>
    </Button>
</StackLayout>

请注意,两个Entry字段将Text属性初始化为空字符串,以便该属性不等于null。 如果两个Text属性的长度都为零,则满足两个BindingConditions,并将Button的IsEnabled属性设置为False。
但是,只有当两个Entry视图都有一些文本时,才能调整它以启用Button。 如果您尝试翻转逻辑,则必须更改BindingCondition对象,以便它们检查长度不等于零的Text属性,并且这不是一个选项。
为了帮助实现逻辑,可以使用一些中间不可见的Switch元素:

<StackLayout>
    <Entry x:Name="entry1"
           Text="" />
    <Switch x:Name="switch1"
            IsVisible="False">
        <Switch.Triggers>
            <DataTrigger TargetType="Switch"
                         Binding="{Binding Source={x:Reference entry1},
                                           Path=Text.Length}"
                         Value="0">
                <Setter Property="IsToggled" Value="True" />
            </DataTrigger>
        </Switch.Triggers>
    </Switch>
 
    <Entry x:Name="entry2"
           Text="" />
 
    <Switch x:Name="switch2"
            IsVisible="False">
        <Switch.Triggers>
            <DataTrigger TargetType="Switch"
                         Binding="{Binding Source={x:Reference entry2},
                                          Path=Text.Length}"
                         Value="0">
                <Setter Property="IsToggled" Value="True" />
            </DataTrigger>
        </Switch.Triggers>
    </Switch>
    <Button Text="Send"
            IsEnabled="False">
        <Button.Triggers>
            <MultiTrigger TargetType="Button">
                <MultiTrigger.Conditions>
                    <BindingCondition Binding="{Binding Source={x:Reference switch1},
                                                        Path=IsToggled}"
                                      Value="False" />
                    <BindingCondition Binding="{Binding Source={x:Reference switch2},
                                                        Path=IsToggled}"
                                      Value="False" />
                </MultiTrigger.Conditions>
 
                <Setter Property="IsEnabled" Value="True" />
            </MultiTrigger>
        </Button.Triggers>
    </Button>
</StackLayout>

每个Entry现在都有一个伴随Switch,如果Entry的Text属性的长度为零,则使用DataTrigger将其IsToggled属性设置为True。 然后可以在MultiTrigger中使用两个Switch元素。 如果两个Switch元素的IsToggled属性都设置为True,则两个Entry字段都包含一些文本,Button的IsEnabled属性可以设置为True。
如果您想要实际组合AND和OR操作,您需要参与更深层次的逻辑。
例如,假设您有一个具有两个Entry视图和一个Button的场景,并且仅当两个Entry视图中的任何一个包含一些文本时才应启用Button,但如果两个Entry视图都包含一些文本则不应该启用Button:
2019_04_04_154648
也许(如截图所示)其中一个Entry视图用于文件名,另一个用于URL,程序只需要这两个文本字符串中的一个。
你需要的是异或(XOR)操作,它是AND,OR和否定运算符的组合:

𝐴 ^ 𝐵 == (𝐴 | 𝐵) & ! (𝐴 & 𝐵)

这可以通过三个MultiTrigger对象完成,其中两个在中间不可见的Switch元素上,最后一个在Button本身上。 这是XorConditions XAML文件,其中包含描述逻辑各个部分的注释:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XorConditions.XorConditionsPage"
             Padding="50, 20">
    <StackLayout>
        <Label Text="Enter:" />
 
        <Entry x:Name="entry1"
               Text=""
               Placeholder="filename" />
        <!-- IsToggled is true if entry1 has no text -->
        <Switch x:Name="switch1"
                IsVisible="False">
            <Switch.Triggers>
                <DataTrigger TargetType="Switch"
                             Binding="{Binding Source={x:Reference entry1},
                                               Path=Text.Length}"
                             Value="0">
                    <Setter Property="IsToggled" Value="True" />
                </DataTrigger>
            </Switch.Triggers>
        </Switch>
        <Label Text="Or:" />
 
        <Entry x:Name="entry2"
               Text=""
               Placeholder="url" />
        <!-- IsToggled is true if entry2 has no text -->
        <Switch x:Name="switch2"
                IsVisible="False">
            <Switch.Triggers>
                <DataTrigger TargetType="Switch"
                             Binding="{Binding Source={x:Reference entry2},
                                               Path=Text.Length}"
                             Value="0">
                    <Setter Property="IsToggled" Value="True" />
                </DataTrigger>
            </Switch.Triggers>
        </Switch>
        <!-- IsToggled is true if either Entry has some text (OR operation) -->

        <Switch x:Name="switch3"
                IsToggled="True"
                IsVisible="False">
            <Switch.Triggers>
                <MultiTrigger TargetType="Switch">
                    <MultiTrigger.Conditions>
                        <BindingCondition Binding="{Binding Source={x:Reference switch1},
                                                            Path=IsToggled}"
                                          Value="True" />
                        <BindingCondition Binding="{Binding Source={x:Reference switch2},
                                                            Path=IsToggled}"
                                          Value="True" />
                    </MultiTrigger.Conditions>
 
                    <Setter Property="IsToggled" Value="False" />
                </MultiTrigger>
            </Switch.Triggers>
        </Switch>
 
        <!-- IsToggled is true if both Entry's have some text (AND operation) -->
        <Switch x:Name="switch4"
                IsVisible="False">
            <Switch.Triggers>
                <MultiTrigger TargetType="Switch">
                    <MultiTrigger.Conditions>
                        <BindingCondition Binding="{Binding Source={x:Reference switch1},
                                                            Path=IsToggled}"
                                          Value="False" />
                        <BindingCondition Binding="{Binding Source={x:Reference switch2},
                                                            Path=IsToggled}"
                                          Value="False" />
                    </MultiTrigger.Conditions>
                    <Setter Property="IsToggled" Value="True" />
                </MultiTrigger>
            </Switch.Triggers>
        </Switch>
        <!-- Button is enabled if either Entry has some text but not both (XOR operation) -->
        <Button Text="Load"
                IsEnabled="False"
                FontSize="Large">
            <Button.Triggers>
                <MultiTrigger TargetType="Button">
                    <MultiTrigger.Conditions>
                        <BindingCondition Binding="{Binding Source={x:Reference switch3},
                                                            Path=IsToggled}"
                                          Value="True" />
                        <BindingCondition Binding="{Binding Source={x:Reference switch4},
                                                            Path=IsToggled}"
                                          Value="False" />
                    </MultiTrigger.Conditions>
                    <Setter Property="IsEnabled" Value="True" />

                </MultiTrigger> 
            </Button.Triggers>
        </Button>
    </StackLayout>
</ContentPage>

当然,一旦XAML获得这种奢侈,如果您只是决定在代码中启用或禁用Button,则没有人会对您造成错误!

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