WPF觸發器分類(一起學習WPF)
首先說明一點就是觸發器可以完成的事情完全可以通過轉換器或者直接編寫代碼進行實現,但是我更喜歡使用觸發器,願意很簡單,實現更加直觀,更符合MVVM,界面樣式的變化不需要代碼的過多接入。
WPF的觸發器使用起來其實比較簡單,但是卻可以發揮很大的作用,使用得當可以使得22代碼簡潔,思路清晰。
使用觸發器,可以自動完成簡單的樣式改變。
觸發器可以分為以下五類:
1、Trigger簡單觸發器
最簡單,也是最基礎的觸發器,可以檢測依賴屬性的變化,然后根據setter中設置的樣式進行改變。
下邊給出一個例子,我們創建一個Border,然后使用簡單觸發器,使當鼠標進入到Border時,顯示一個紅色的邊框。
下邊給出兩個寫法。
(1)第一種
1 <Border Width="100" Height="30" BorderBrush="Blue" BorderThickness="3"> 2 <Border.Style> 3 <Style TargetType="Border"> 4 <Style.Setters> 5 <Setter Property="Background" Value="Green"/> 6 </Style.Setters> 7 <Style.Triggers> 8 <Trigger Property="IsMouseOver" Value="True"> 9 <Setter Property="BorderBrush" Value="Red"/> 10 <Setter Property="BorderThickness" Value="3"/> 11 </Trigger> 12 </Style.Triggers> 13 </Style> 14 </Border.Style> 15 </Border>
(2)第二種
<Border Width="100" Height="30"> <Border.Style> <Style TargetType="Border"> <Style.Setters> <Setter Property="Background" Value="Green"/> <Setter Property="BorderBrush" Value="Blue"/> <Setter Property="BorderThickness" Value="3"/> </Style.Setters> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="BorderBrush" Value="Red"/> <Setter Property="BorderThickness" Value="3"/> </Trigger> </Style.Triggers> </Style> </Border.Style> </Border>
對比兩種寫法,並經過嘗試,發現第一種並沒有起作用,運行后邊框會一直顯示最開始設置的值藍色,而第二種寫法會在鼠標移入時達到我們想要的效果,為什么會這樣呢,這里我們引用WPF編程寶典中的一句話進行說明:
”本質上,觸發器是眾多覆蓋從依賴屬性返回的值的屬性提供者之一。但原始的值(不管是本地設置還是通過樣式設置)仍會保留。只要觸發器被禁用,那么之前的屬性值就會再次可用“。
這段話好像並不能解釋前邊拋出發問題,哈哈哈,不過這句話依舊很重要。
那么真正的原因是什么呢,我們在上邊的代碼中加入兩個本地屬性。
<Border Width="100" Height="30" BorderBrush="Orange" BorderThickness="3"> <Border.Style> <Style TargetType="Border"> <Style.Setters> <Setter Property="Background" Value="Green"/> <Setter Property="BorderBrush" Value="Blue"/> <Setter Property="BorderThickness" Value="3"/> </Style.Setters> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="BorderBrush" Value="Red"/> <Setter Property="BorderThickness" Value="3"/> </Trigger> </Style.Triggers> </Style> </Border.Style> </Border>
運行起來會顯示什么顏色呢,答案是會顯示橙色。因為WPF允許在多個地方設置依賴屬性的值,那么自然會涉及優先級的問題,也就是說本地屬性的優先級要高於setter設置的值,而默認Style Setter的優先級又高於默認Style Trigger中設置的值,這種優先級別在所有的觸發器中均適用。具體的優先級見下圖,更為詳細的關於依賴屬性的說明會在專門的文章中進行說明。
最后需要說明一點的是,當有多個觸發器設置同一個屬性值的時候,觸發器在標記中的排列順序決定了最終顯示的效果。比如我們在上邊的例子中再增加一個IsVisible的觸發器,設置同樣的Setter,顏色為紫色,那么當鼠標移入Border后顯示的邊框顯示的顏色將由Trigger在Triggers中的先后順序決定。
2、MultiTrigger多條件觸發器
多條件觸發器其實就是普通觸發器進行簡單的組合,然后進行&&預算,當所有的條件滿足后出發Setter中設置的屬性,用一個簡單的CheckBox進行說明。只有當控件處於可見狀態並且選中時,復選框的邊框呈現紅色,這里只是舉例,實際開發中必然是不會這樣設置條件的。
<CheckBox> <CheckBox.Style> <Style TargetType="CheckBox"> <Style.Setters> <Setter Property="BorderBrush" Value="Blue"/> <Setter Property="BorderThickness" Value="3"/> </Style.Setters> <Style.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="IsVisible" Value="True"/> <Condition Property="IsChecked" Value="False"/> </MultiTrigger.Conditions> <MultiTrigger.Setters> <Setter Property="BorderBrush" Value="Red"/> <Setter Property="BorderThickness" Value="3"/> </MultiTrigger.Setters> </MultiTrigger> </Style.Triggers> </Style> </CheckBox.Style> </CheckBox>
3、DataTrigger數據觸發器
數據觸發器同樣是和觸發器一樣,當達到設置的某個條件時,進行樣式的更改,或者動畫的觸發。以及其他的一些操作,雖然目前並不知道還有哪些可以觸發,哈哈哈。
使用方法一通百通,和普通觸發器使用方法基本一樣,先上例子。
<TextBox Margin="16 0 0 0" Width="80"> <TextBox.Style> <Style TargetType="TextBox"> <Style.Setters> <Setter Property="Foreground" Value="Green"/> </Style.Setters> <Style.Triggers> <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},Path=Text}" Value="5"> <Setter Property="Foreground" Value="OrangeRed"/> </DataTrigger> </Style.Triggers> </Style> </TextBox.Style> </TextBox>
這個例子是在文本框中輸入的值為“5”時將字體顏色設置為橙紅色,其余時候為綠色。這里我們注意到數據觸發器和前邊的觸發器出現了一個不同的地方,那就是綁定,(綁定我們在這里進行說明)出現了綁定就意味着這里的數據觸發可以是多個條件,比如當我們綁定自身的某個屬性值等於指定值的時候便可以實現部分觸發器的功能,比如將例子中的Text屬性替換為IsVisible,並將值設置為True,便能實現和前邊一樣的效果,當然這樣做是不恰當的。除了綁定自身屬性,還可以綁定其他元素的屬性,使用ElementName進行綁定,或者我們干脆直接綁定后台的屬性,並設置值,都可以實現數據觸發器的效果。總結一些就是,依賴屬性可以綁定的東西,均可以用作數據觸發器的條件,前提是設置恰當的Value值。
4、MultiDataTrigger多數據觸發器
這個觸發器就不做過多的說明了,根據多條件觸發器和數據觸發器的學習,想必多條件數據觸發器是非常好理解的,就是將幾個是否相等的判斷進行了且運算,如果全部成立,則觸發效果,反之則無動於衷。
5、EventTrigger事件觸發器
事件觸發器和前邊幾個觸發器相比是一個比較特殊的觸發器,因為這個觸發器的使用目的不是為了直接改變某個依賴屬性的值,而是“緩慢”的改變,具有一個動畫效果,用於觸發故事板中的一段動畫(動畫我們放到這里學習)。更為官方的說法是事件觸發器要求用戶提供一系列修改控件的動作,這些動作通常被用於動畫。
<Border Margin="16 0 0 0" Width="30" Background="OrangeRed"> <Border.Style> <Style TargetType="Border"> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Trigger.EnterActions> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:0.5" Storyboard.TargetProperty="Width" From="30" To="100"/> </Storyboard> </BeginStoryboard> </Trigger.EnterActions> <Trigger.ExitActions> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:0.5" Storyboard.TargetProperty="Width" From="100" To="30"/> </Storyboard> </BeginStoryboard> </Trigger.ExitActions> </Trigger> </Style.Triggers> </Style> </Border.Style> </Border>
這個事件觸發器中,當鼠標移入后改變Border的長度,移除后恢復原來的長度。這里為什么會有兩段動畫呢,不是觸發器不改變依賴屬性原來的值,失效后自動恢復的嘛。這就是與屬性觸發器的不同了,如果希望元素返回原來的狀態,需要反轉事件觸發器,這是因為默認的動畫行為是一旦動畫完成就繼續保持激活狀態,從而會繼續保持動畫結束時最后的屬性值。
更為有意思的是,當依賴屬性等於某個值的時候,也可以執行動畫。也就是執行動畫並不是事件觸發器的專屬,而是每種觸發器都可以,為了使用這項技術,技巧是不為觸發器提供任何的Setter對象,而是設置EnterActions和ExitActions屬性,這兩個屬性都有一個動作集合,例如啟動動畫的BeginStorybord。當屬性達到指定的值時,執行EnterActions,屬性離開指定值時,執行EnterActions。
跟為詳細的動畫觸發內容將會在后續的動畫中進行記錄學習。
說明:文中代碼使用NET5進行編寫。