数据触发器
到目前为止,您只看到在特定对象的上下文中运行的触发器。 触发器通过更改同一对象的另一个属性或通过调用影响该对象的Action来响应对象属性的更改。 EventTrigger同样响应一个对象触发的事件,以在同一个对象上调用Action。
DataTrigger是不同的。 与其他TriggerBase派生类似,DataTrigger附加到可视元素或在样式中定义。 但是,DataTrigger可以通过数据绑定检测另一个对象的属性更改,并更改其附加到的对象中的属性,或者(通过使用从TriggerBase继承的EnterActions和ExitActions集合)调用该对象上的TriggerAction。
DataTrigger定义以下三个属性。
- Binding 类型是 BindingBase。
- Object类型的Value。
- IList类型的setter。 这是DataTrigger的content属性。
从应用程序的角度来看,DataTrigger与Trigger非常相似,只是Trigger名为Property的属性被Binding属性替换。 Trigger和DataTrigger都需要设置TargetType属性。
DataTrigger的Binding属性引用的另一个对象是什么?它可以是MVVM场景中的ViewModel或页面上的其他元素的一部分。
您可能还记得第19章“集合视图”中的SchoolOfFineArt库。该库中的Student类定义了一个名为Sex of type string的属性,该属性设置为“Male”或“Female”。下面介绍的GenderColors程序将该属性与DataTrigger结合使用,为学生设置蓝色或粉红色(无论这种颜色方案看起来多么过时。)
ListView显示高中的所有学生,ViewCell格式化每个学生以显示学生的照片,全名和当前的成绩点平均值:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:school="clr-namespace:SchoolOfFineArt;assembly=SchoolOfFineArt"
x:Class="GenderColors.GenderColorsPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness"
iOS="0, 20, 0, 0" />
</ContentPage.Padding>
<ContentPage.BindingContext>
<school:SchoolViewModel />
</ContentPage.BindingContext>
<StackLayout BindingContext="{Binding StudentBody}">
<Label Text="{Binding School}"
FontSize="Large"
FontAttributes="Bold"
HorizontalTextAlignment="Center" />
<ListView ItemsSource="{Binding Students}"
VerticalOptions="FillAndExpand">
<ListView.RowHeight>
<OnPlatform x:TypeArguments="x:Int32"
iOS="70"
Android="70"
WinPhone="100" />
</ListView.RowHeight>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="0, 5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0"
Source="{Binding PhotoFilename}"
VerticalOptions="Center" />
<StackLayout Grid.Column="1"
VerticalOptions="Center">
<Label Text="{Binding FullName}"
FontSize="22"
TextColor="Pink">
<Label.Triggers>
<DataTrigger TargetType="Label"
Binding="{Binding Sex}"
Value="Male">
<Setter Property="TextColor" Value="#8080FF" />
</DataTrigger>
</Label.Triggers>
</Label>
<Label Text="{Binding GradePointAverage,
StringFormat='G.P.A. = {0:F2}'}"
FontSize="16" />
</StackLayout>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
该程序使用ViewCell而不是ImageCell,因此它可以访问Label,它可以附加DataTrigger。 触发器不能直接附加到Cell或Cell派生,因为没有为这些类定义触发器集合。
Label显示Student对象的FullName属性,TextColor设置为Pink。 但DataTrigger会检查Student对象的Sex属性是否等于“Male”,如果是,则使用Setter将TextColor设置为浅蓝色。 这是从单元格的其余部分隔离的Label:
<Label Text="{Binding FullName}"
FontSize="Large"
TextColor="Pink">
<Label.Triggers>
<DataTrigger TargetType="Label"
Binding="{Binding Sex}"
Value="Male">
<Setter Property="TextColor" Value="#8080FF" />
</DataTrigger>
</Label.Triggers>
</Label>
DataTrigger的BindingContext与它所附加的Label的BindingContext相同。 BindingContext是一个特定的Student对象,因此DataTrigger上的Binding只需要指定Sex属性。
这是在运行结果:
直接从Student对象的Sex属性到Label(或ImageCell)的TextColor属性的数据绑定可以完全相似,但它需要一个绑定转换器。 DataTrigger无需任何其他代码即可完成工作。
但是,DataTrigger本身无法模仿第19章中的ColorCodedStudents程序。如果该学生的成绩点平均值危险地低于2.0标准,则该程序将显示红色的学生。小于数字的比较需要一些代码。这也是一个行为的工作,一旦你在本章后面了解了行为,你应该能够自己编写这样的代码。
DataTrigger也可以引用页面上的另一个元素来监视该元素的属性。
例如,图形环境中的一个经典任务是,如果没有在文本输入字段中输入任何内容,则禁用按钮。也许文本输入字段是文件名,按钮执行一些代码来加载或保存该文件。如果文件名为空,则启用该按钮没有任何意义。
您可以使用DataTrigger完全在XAML中完成这项工作。这是ButtonEnabler项目中的标记:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ButtonEnabler.ButtonEnablerPage"
Padding="20, 50">
<StackLayout Spacing="20">
<Entry x:Name="entry"
Text=""
Keyboard="Url"
Placeholder="enter filename" />
<Button Text="Save"
FontSize="Large"
HorizontalOptions="Center">
<Button.Triggers>
<DataTrigger TargetType="Button"
Binding="{Binding Source={x:Reference entry},
Path=Text.Length}"
Value="0">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Button.Triggers>
</Button>
</StackLayout>
</ContentPage>
Button上的DataTrigger使用引用Entry元素的Source设置其Binding属性。 Path设置为Text.Length。 Entry元素的Text属性是string类型,Length是string的属性,因此this binding指的是Entry元素中输入的字符数。 DataTrigger的Value属性设置为零,因此当Entry中输入零个字符时,将调用Setter属性,该属性将Button的IsEnabled属性设置为False。
根据Entry元素中的输入,此处显示的iPhone和Windows 10 Mobile屏幕上的按钮被禁用,但在Android屏幕上启用:
虽然这代表了对用户界面的微小增强,但如果您没有DataTrigger,则需要在Entry的TextChanged处理程序中的代码中实现此增强功能,或者您需要为绑定编写绑定转换器 Button的IsEnabled属性和Entry的Text.Length属性之间。
ButtonEnabler中的XAML文件包含一个您可能没有注意到的关键属性设置:
<Entry __ Text="" __ />
首次创建Entry时,Text属性不是空字符串而是null,这意味着DataTrigger中的数据绑定正在尝试引用空字符串对象的Length属性,并且它将失败。 由于绑定失败,因此在程序首次启动时将启用Button。 只有在用户键入字符和退格后,它才会被禁用。
将Text属性初始化为空字符串没有其他效果,但允许DataTrigger在程序启动时工作。