MAUI的触发器,提供了在运行时动态更改控件样式的方法。在Blazor或Vue中,可以通过三元表达式或绑定class来轻松实现,而MAUI则相对麻烦些,需要通过触发器来实现。触发器,其实就是控件的一个属性,只要是可视化控件,都带有一个Triggers集合属性,在这个集合属性中,可以设置多个Trigger。Trigger包括两个组成部分,一是触发条件,二是改变目标(样式类属性及其值)。对触发条件来说,我们有时候需要某个属性或数据改变时就触发,或者直接用事件触发,还或者当控件状态发生改变时触发,MAUI相应为我们提供的属性触发器、数据触发器、事件触发器和状态触发器。而需要改变的样式类属性,大多数时候是通过Setter,当然也可以通过后台代码。
一、属性触发器Trigger
<!--属性触发器:直接在控件中定义=================================================================--> <ContentPage ......> <StackLayout x:Name="stackLayout" Padding="10"> <Entry Placeholder="请输入"> <Entry.Triggers> <!--触发条件:当Entry的属性IsFocused的值为true时。--> <Trigger TargetType="Entry" Property="IsFocused" Value="True" > <!--在Trigger的Setters集合属性中设置Setter,<Trigger.Setters>可以省略--> <!--当触发条件达到时,Entry的背景颜色变为AliceBlue--> <Trigger.Setters> <Setter Property="BackgroundColor" Value="AliceBlue"/> </Trigger.Setters> </Trigger> </Entry.Triggers> </Entry> </StackLayout> </ContentPage> <!--属性触发器:通过资源字典中的样式定义触发器,效果以上例一样========================================--> <ContentPage ......> <!--在资源字典中定义样式触发器,隐式应用于Entry控件--> <ContentPage.Resources> <Style TargetType="Entry"> <Style.Triggers> <!--Trigger的定义,和在控件中定义一模一样--> <Trigger TargetType="Entry" Property="IsFocused" Value="True"> <Setter Property="BackgroundColor" Value="AliceBlue" /> </Trigger> </Style.Triggers> </Style> </ContentPage.Resources> <StackLayout x:Name="stackLayout" Padding="10"> <Entry Placeholder="请输入" /> </StackLayout> </ContentPage>
二、数据触发器DataTrigger
<ContentPage ......> <StackLayout x:Name="stackLayout" Padding="10"> <Entry x:Name="entry" Placeholder="请输入" /> <Button Text="确定"> <Button.Triggers> <!-- 数据触发器的类型为DataTrigger --> <!-- 通过Binding,绑定数据,数据源可以是当前页面的UI属性值,也可以是后台属性。也可以使用Converter --> <DataTrigger Binding="{Binding Source={x:Reference entry}, Path=Text.Length}" TargetType="Button" Value="10"> <!--设置了多个Setter,可以触发多个属性的变更--> <Setter Property="IsEnabled" Value="False" /> <Setter Property="BackgroundColor" Value="Red" /> </DataTrigger> </Button.Triggers> </Button> </StackLayout> </ContentPage>
三、属性和数据触发器的多触发条件MultiTrigger
- 触发器可以触发多个属性的变更,也可以通过<MultiTrigger>和<MultiTrigger.Conditions>设置多触发条件。
- 设置多触发条件时,必须所有条件都满足,才会触发
- 只有属性和数据触发器,可以设置多条件。数据使用BindingCondition,属性使用PropertyCondition
<ContentPage ......> <StackLayout x:Name="stackLayout" Padding="10"> <Entry x:Name="entry1" Placeholder="请输入email" Text=""/> <Entry x:Name="entry2" Placeholder="请输入phone" Text=""/> <Button Text="确定"> <Button.Triggers> <!--单触发条件使用Trigger,多触发条件使用MultiTrigger--> <MultiTrigger TargetType="Button"> <!--设置多触发条件--> <MultiTrigger.Conditions> <!--通过BindingCondition设置多个数据触发条件--> <BindingCondition Binding="{Binding Source={x:Reference entry1},Path=Text.Length}" Value="10"/> <BindingCondition Binding="{Binding Source={x:Reference entry2},Path=Text.Length}" Value="10"/> <!--也可以设置多个属性触发条件,如--> <PropertyCondition Property="Text" Value="确定"/> </MultiTrigger.Conditions> <Setter Property="IsEnabled" Value="False"/> </MultiTrigger> </Button.Triggers> </Button> </StackLayout> </ContentPage>
四、属性和数据触发器的EnterActions和ExitActions
- 通过后台代码更改目标属性,如:<Trigger.EnterActions><local:FadeTriggerAction StartsFrom="0" /></Trigger.EnterActions>
- 当满足触发条件时,将执行上例中,FadeTriggerAction对象的Invoke方法,在这个方法中,可以更改控件属性
- FadeTriggerAction是TriggerAction<T>类的派生类,T为目标控件的类型,这个类中重写Invoke方法
<!--使用属性触发器的EnterActions和ExitActions--> <ContentPage ...... xmlns:triggeraction="clr-namespace:MauiApp12.TriggerActions"> <StackLayout x:Name="stackLayout" Padding="10"> <Entry x:Name="entry" Placeholder="请输入" Text=""> <Entry.Triggers> <Trigger TargetType="Entry" Property="IsFocused" Value="True"> <!-- ①当聚焦条件满足(EnterActions)或不满足(ExitActions)时,创建立FadeTriggerAction对象,初始化StartsFrom属性 ②执行FadeTriggerAction对象的Invoke方法 ③本案例中,FadeTriggerAction对象的Invoke方法,将创建一个背景色的渐变动画 --> <Trigger.EnterActions> <triggeraction:FadeTriggerAction StartsFrom="0"/> </Trigger.EnterActions> <Trigger.ExitActions> <triggeraction:FadeTriggerAction StartsFrom="1"/> </Trigger.ExitActions> </Trigger> </Entry.Triggers> </Entry> </StackLayout> </ContentPage> <!--定义EnterActions和ExitActions--> <!--在TriggerActions文件夹中定义FadeTriggerAction类,派生自TriggerAction<T>--> public class FadeTriggerAction : TriggerAction<VisualElement> { //StartsFrom属性值,在创建FadeTriggerAction对象时初始化 public int StartsFrom { get; set; } //重写Invoke方法,参数sender为触发器的宿主控件 protected override void Invoke(VisualElement sender) { //设置sender的Animate,自定义一个背景色渐变动画 sender.Animate("FadeTriggerAction",new Animation((d) => { var val = StartsFrom == 1 ? d : 1-d; sender.BackgroundColor = Color.FromRgb(1, val, 1); }), length:1000, //动画长度1000毫秒 easing:Easing.Linear); //动画的速率-恒定速度 } }
五、事件触发器
- 事件触发器的使用,和属性/数据触发器的EnterAcitons/ExitActions用法类似,都是调用TriggerAction<T>派生类对象的Invoke方法,在后台代码中更改控件的样式属性
- 事件触发器不能使用EnterAcitons/ExitActions,也不能使用Setter
//XAML文件中定义事件触发器========================================================================================= <ContentPage ...... xmlns:triggeraction="clr-namespace:MauiApp12.TriggerActions"> <StackLayout x:Name="stackLayout" Padding="10"> <Entry x:Name="entry" Placeholder="请输入" Text=""> <Entry.Triggers> //在Triggers集合中,增加EventTrigger事件触发器 <EventTrigger Event="TextChanged"> //当TextChanged事件发生时,调用NumericValidationTriggerAction对象的Invoke方法 //NumericValidationTriggerAction是TriggerAction<T>的派生类 <triggeraction:NumericValidationTriggerAction/> </EventTrigger> </Entry.Triggers> </Entry> </StackLayout> </ContentPage> //在后台代码中,更改控件的样式属性================================================================================== //和EnterAcitons/ExitAcitons的用法一样,派生自泛型抽像类TriggerAction<T>,泛型参数为宿主类型 //重写抽像类的Invoke方法,但满足触发条件时(此例为Entry的TextChanged事件),调用Invoke方法 public class NumericValidationTriggerAction : TriggerAction<Entry> { protected override void Invoke(Entry entry) { //尝试将输入值转换为double,如果可以转,则将转化成功后的值赋值给result,并返回true,否则返回false double result; bool isValid = Double.TryParse(entry.Text,out result); //如果可以转(说明是数值),则entry的字体颜色为黑色,否则为红色 entry.TextColor = isValid ? Colors.Black : Colors.Red; } }
六、状态触发器:
- 回顾控件状态章节,当我们自定义状态时,需要在后台代码中调用VisualStateManager.GoToState方法,来定义控件状态改变的逻辑。而使用触发器,可以直接在XAML文件中,通过触发器来触发控件状态的变更。当符合某个触发条件时,控件转到某个状态,使用更加方便。
- 每个控件状态VisualState,有一个VisualState.StateTriggers集合属性,将触发器添加到这个集合中。
- 触发器的触发条件,包括:绑定数据、绑定属性、窗口大小、设备类型、设备方向等。
- 每个状态触发器,还有一个IsActiveChanged事件,当满足触发条件时,还可以执行这个事件的委托方法。
1、状态触发器:数据触发
<ContentPage ......> <ContentPage.Resources> <!--定义隐式样式,自动作用于Grid控件--> <Style TargetType="Grid"> <!--设置附加属性VisualStateManager.VisualStateGroups,和控件状态的使用基本一样--> <Setter Property="VisualStateManager.VisualStateGroups"> <VisualStateGroupList> <!--为Grid自定义了两个状态,Checked和UnChecked--> <VisualStateGroup> <VisualState x:Name="Checked"> <!--定义状态触发器,使用绑定数据的方式,IsToggled可以通过MVVM模式,绑定ViewModel的数据--> <!--同时,在触发器上定义了IsActiveChanged事件的事件处理程序OnCheckedStateIsActiveChanged--> <VisualState.StateTriggers> <StateTrigger IsActive="{Binding IsToggled}" IsActiveChanged="OnCheckedStateIsActiveChanged" /> </VisualState.StateTriggers> <!--状态的Setters设置,详见控件状态章节--> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Black" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Unchecked"> <VisualState.StateTriggers> <StateTrigger IsActive="{Binding IsToggled, Converter={StaticResource inverseBooleanConverter}}" IsActiveChanged="OnUncheckedStateIsActiveChanged" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Property="BackgroundColor" Value="White" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateGroupList> </Setter> </Style> </ContentPage.Resources>
<Grid RowDefinitions="1*,2*,Auto" ColumnDefinitions="1*,1*,Auto"> ...... </Grid> </ContentPage>
2、状态触发器:属性触发
<ContentPage ......> <ContentPage.Resources> <Style TargetType="Grid"> <Setter Property="VisualStateManager.VisualStateGroups"> <VisualStateGroupList> <VisualStateGroup> <!--自定义了两个控件状态,Checked和Unchecked--> <VisualState x:Name="Checked"> <!-- 定义触发器,使用绑定控件属性的方式 --> <!-- StateTrigger和CompareStateTrigger,都派生自StateTriggerBase,也可以使用IsActiveChanged事件 --> <!--CompareStateTrigger绑定当前页面的checkBox控件的IsChecked属性,当值为True时触发控件状态Checked--> <VisualState.StateTriggers> <CompareStateTrigger Property="{Binding Source={x:Reference checkBox}, Path=IsChecked}" Value="True" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Black" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Unchecked"> <!--CompareStateTrigger绑定当前页面的checkBox控件的IsChecked属性,当值为False时触发控件状态Checked--> <VisualState.StateTriggers> <CompareStateTrigger Property="{Binding Source={x:Reference checkBox}, Path=IsChecked}" Value="False" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Property="BackgroundColor" Value="White" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateGroupList> </Setter> </Style> </ContentPage.Resources> <Grid> <StackLayout Orientation="Horizontal"> <CheckBox x:Name="checkBox" VerticalOptions="Center" /> <Label Text="点击复选框,改变Grid的背景色" VerticalOptions="Center" /> </StackLayout> </Grid> </ContentPage>
3、状态触发器:窗口大小触发
<ContentPage ......> <ContentPage.Resources> <Style TargetType="StackLayout"> <Setter Property="VisualStateManager.VisualStateGroups"> <VisualStateGroupList> <!--定义两个状态,状态Vertical,StackLayout的方向为Vertical。状态Horizontal,StackLayout的为Horizontal--> <!--触发器为AdaptiveTrigger,触发条件为窗口大小,通过设置MinWindowWidth和MinWindowHeight--> <!--MinWindowWidth和MinWindowHeight的使用逻辑,和CSS中的媒体查询类似--> <VisualStateGroup> <VisualState x:Name="Vertical"> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="0" MinWindowHeight="0"/> </VisualState.StateTriggers> <VisualState.Setters> <Setter Property="Orientation" Value="Vertical" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Horizontal"> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="800" MinWindowHeight="800"/> </VisualState.StateTriggers> <VisualState.Setters> <Setter Property="Orientation" Value="Horizontal" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateGroupList> </Setter> </Style> </ContentPage.Resources> <StackLayout> <!--定义三个测试的盒子--> <Label Text="111" WidthRequest="100" HeightRequest="100" BackgroundColor="Red"/> <Label Text="222" WidthRequest="100" HeightRequest="100" BackgroundColor="Yellow"/> <Label Text="333" WidthRequest="100" HeightRequest="100" BackgroundColor="Green"/> </StackLayout> </ContentPage>
4、状态触发器:设备类型触发
<!--通过DeviceStateTrigger,设置设备类型触发器--> <!--目前支持的类型包括iOS、Android、WinUI、MacCatalyst、Tizen--> <VisualState.StateTriggers> <DeviceStateTrigger Device="iOS" /> </VisualState.StateTriggers>
5、状态触发器:设备方向触发
<!--通过OrientationStateTrigger,设置设备方向触发器--> <!--目前支持的方向包括Portrait(竖屏)、Landscape(横屏)--> <VisualState.StateTriggers> <OrientationStateTrigger Orientation="Portrait" /> </VisualState.StateTriggers>
标签:触发,触发器,5.3,Invoke,控件,状态,Trigger,MAUI,属性 From: https://www.cnblogs.com/functionMC/p/16988512.html