學習目標
- Trigger - 屬性觸發
- EeventTrigger - 事件觸發
當初在 第七章 介紹 XAML 時,XAML 似乎就僅是個建構使用者介面的標籤語法而已,
但到了二十三章,Xamairn 想讓 XAML 做更多 後端能做 的事情,
Triggers 和 Behaviors 就這樣誕生了
Triggers 可以在屬性條件成立時,同時更改其他的屬性值。
Behaviors 則是更開放性的用法,在 Visual Element 內加入更多客製化的功能。
不論是 Triggers 或是 Behavior,都可以寫在 Style 內,讓程式 重複使用。
Triggers
Visual Element 定義了 Triggers 屬性 可做設定,
其類型為 IList<TriggerBase>,意思就是 同一個 Visual Element可以放入多個 ,甚至多種 Trigger
可放入的 Trigger 種類如下:
- Trigger - 當屬性符合條件時要觸發
- EventTrigger - 當事件觸發時要觸發
- DataTrigger - 當綁定的屬性值符合條件時要觸發
- MultiTrigger - Trigger 有多組條件要做判斷時
本小節會先介紹 Trigger 和 EventTrigger。
Trigger
講到現在,相信大家對 Trigger 還是霧薩薩的...
先下個簡單的定義:
Trigger 就是一組設定好,當條件成立時,會去執行某事情 的 XAML 標籤。來看範例:
要做的事很簡單,當我們點擊 Entry 時,Entry 會變大...
更精準地說,
當 IsFocused 為 True 時,將 Scale 的值改為 1.5
當 IsFocused 變回 False 時,Scale 改回原本預設的值 1。
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="EntryPop.EntryPopPage" Padding="20, 50, 120, 0"> <StackLayout Spacing="20"> <Entry Placeholder="enter name" AnchorX="0"> <!--設定 Triggers--> <Entry.Triggers> <!--設定條件,當 IsFocused 為 Value 時成立--> <Trigger TargetType="Entry" Property="IsFocused" Value="True"> <!--要執行的事情--> <Setter Property="Scale" Value="1.5" /> </Trigger> </Entry.Triggers> </Entry> <Entry Placeholder="enter address" AnchorX="0"> <Entry.Triggers> <Trigger TargetType="Entry" Property="IsFocused" Value="True"> <Setter Property="Scale" Value="1.5" /> </Trigger> </Entry.Triggers> </Entry> <Entry Placeholder="enter city and state" AnchorX="0"> <Entry.Triggers> <Trigger TargetType="Entry" Property="IsFocused" Value="True"> <Setter Property="Scale" Value="1.5" /> </Trigger> </Entry.Triggers> </Entry> </StackLayout> </ContentPage>
執行結果:
Style 也定義了 Triggers 屬性,代表的是你可以把 Trigger 寫在 Style 內,讓其他物件共享此 Trigger。
讓我們改造一下上面程式碼,同時加入隱含 Style,簡化程式碼:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="StyledTriggers.StyledTriggersPage" Padding="20, 50, 120, 0"> <ContentPage.Resources> <ResourceDictionary> <Style TargetType="Entry"> <Setter Property="AnchorX" Value="0" /> <!--寫在 Style 內的 Trigger--> <Style.Triggers> <Trigger TargetType="Entry" Property="IsFocused" Value="True"> <Setter Property="Scale" Value="1.5" /> </Trigger> </Style.Triggers> </Style> </ResourceDictionary> </ContentPage.Resources> <StackLayout Spacing="20"> <Entry Placeholder="enter name" /> <Entry Placeholder="enter address" /> <Entry Placeholder="enter city and state" /> </StackLayout> </ContentPage>
如果今天,你不想讓 Entry 只是直接變大,而是慢慢的脹大,需要 Trigger 配合 Animation 的寫法…
請繼續往下看...
EventTrigger
即使 Trigger 可以完全在 XAML 上執行,但有時也要寫一點 .cs 的…
例如我們想做的事情:在 Trigger 內搭配 Animation 使物件 慢慢脹大 或 慢慢縮小,
但, XAML 並不支援寫 Animations,
所以,如果要用 Trigger 去觸發 Animation,勢必要寫在 .cs。
最常見的方法就是使用 EventTrigger,定義了兩個屬性:
- Event of type string
- Actions of type IList<TriggerAction>
EventTrigger 的定義就是:
當觸發 指定事件 時,會將所有在 Actions 內的 TriggerAction 加入執行
// TriggerAction 可以寫在 .cs
舉例來說,
你可以把 Focused 設定為 EventTrigger 指定的事件,
而當 Focused 事件 被觸發時,EventTrigger 底下的 TriggerAction 會被加入並執行。
而你的工作就是要 提供要執行的 TriggerAction 事件,覆寫 Invoke 方法。
要提供的 TriggerAction 簡單來說可以長這樣:
public class ScaleAction : TriggerAction< VisualElement> { protected override void Invoke(VisualElement visual) { visual.ScaleTo(1.5); } }
當然,你不會想把這支程式碼寫死。
上面簡單的 TriggerAction 在 Focused 事件內運作得很好,但同時你也會需要 Unfocused 事件,將大小恢復的 TriggerAction
來改造下 ScaleAtion,讓其更有彈性:
namespace Xamarin.FormsBook.Toolkit { public class ScaleAction : TriggerAction< VisualElement> { public ScaleAction() { // 預設值. Anchor = new Point (0.5, 0.5); Scale = 1; Length = 250; Easing = Easing.Linear; } public Point Anchor { set; get; } public double Scale { set; get; } public int Length { set; get; } //Converter 附錄在本節最後. [TypeConverter(typeof(EasingConverter))] public Easing Easing { set; get; } protected override void Invoke(VisualElement visual) { visual.AnchorX = Anchor.X; visual.AnchorY = Anchor.Y; visual.ScaleTo(Scale, (uint)Length, Easing); } } }
注意到中間有一行 TypeConvert 搭配 Easing,Easing 是指物件消失時的動畫類型,
因為在 XAML 我們只能給予字串,.cs 是看不懂字串的,要自行轉換成 Easing 類別,轉換程式附錄在本節最後。
來看一下 XAML 怎用:
<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="EntrySwell.EntrySwellPage" Padding="20, 50, 120, 0"> <ContentPage.Resources> <ResourceDictionary> <Style TargetType="Entry"> <Style.Triggers> <!--設定 EventTrigger,指定觸發事件為 Focused--> <EventTrigger Event="Focused"> <toolkit:ScaleAction Anchor="0, 0.5" Scale="1.5" Easing="SpringOut" /> </EventTrigger> <EventTrigger Event="Unfocused"> <toolkit:ScaleAction Anchor="0, 0.5" Scale="1" /> </EventTrigger> </Style.Triggers> </Style> </ResourceDictionary> </ContentPage.Resources> <StackLayout Spacing="20"> <Entry Placeholder="enter name" /> <Entry Placeholder="enter address" /> <Entry Placeholder="enter city and state" /> </StackLayout> </ContentPage>
如果想要 屬性改變時 去執行 Actions ( 非指定事件觸發) 怎辦~?
- EnterActions of type IList<TriggerAction>
- ExitActions of type IList<TriggerAction>
來看範例:
當屬性 IsFocused 為 True 時,去執行 Actions
<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="EnterExitSwell.EnterExitSwellPage" Padding="20, 50, 120, 0"> <ContentPage.Resources> <ResourceDictionary> <Style TargetType="Entry"> <Style.Triggers> <!--設定 Trigger--> <Trigger TargetType="Entry" Property="IsFocused" Value="True"> <!--設定 EnterActions--> <Trigger.EnterActions> <toolkit:ScaleAction Anchor="0, 0.5" Scale="1.5" Easing="SpringOut" /> </Trigger.EnterActions> <!--設定 ExitActions--> <Trigger.ExitActions> <toolkit:ScaleAction Anchor="0, 0.5" Scale="1" /> </Trigger.ExitActions> </Trigger> </Style.Triggers> </Style> </ResourceDictionary> </ContentPage.Resources> <StackLayout Spacing="20"> <Entry Placeholder="enter name" /> <Entry Placeholder="enter address" /> <Entry Placeholder="enter city and state" /> </StackLayout> </ContentPage>
附錄,TypeConverter(typeof(EasingConverter))
namespace Xamarin.FormsBook.Toolkit { public class EasingConverter : TypeConverter { public override bool CanConvertFrom(Type sourceType) { if (sourceType == null) throw new ArgumentNullException("EasingConverter.CanConvertFrom: sourceType"); return (sourceType == typeof(string)); } public override object ConvertFrom(CultureInfo culture, object value) { if (value == null || !(value is string)) return null; string name = ((string)value).Trim(); if (name.StartsWith("Easing")) { name = name.Substring(7); } FieldInfo field = typeof(Easing).GetRuntimeField(name); if (field != null && field.IsStatic) { return (Easing)field.GetValue(null); } throw new InvalidOperationException( String.Format("Cannot convert \"{0}\" into Xamarin.Forms.Easing", value)); } } }
你好,有個 Xamarin 開發 App 的需求,是否方便與你聯繫? Paza / hipaza@gmail.com
回覆刪除您好,可以來信我剛註冊好的信箱... loganedge.tw@gmail.com
刪除或是直接加我 LineID:Magic0800