<Window.Resources> <Style x:Key="BigFontButtonStyle"> <Setter Property="Control.FontFamily" Value="Times New Roman"></Setter> <Setter Property="Control.FontSize" Value="20"></Setter> <Setter Property="Control.FontWeight" Value="Bold"></Setter> </Style> </Window.Resources>
上面的標記建立了一個獨立的資源:一個System.Windows.Style對象,與全部資源同樣,樣式對象有一個鍵名,從而當須要時 能夠從集合中提取它,根據約定,用於樣式的鍵名一般以Style結尾。樣式經過元素的Style屬性(該屬性是在FrameworkElement基類中 定義的)插入到元素中,以下所示爲按鈕使用了以上定義的樣式:canvas
<Button Style="{StaticResource BigFontButtonStyle}">A Button For My Test</Button>
也可使用代碼設置樣式,就是使用FindResources()方法從最近的資源集合中提取樣式:app
btnStyleTest1.Style = (Style)btnStyleTest1.FindResource("BigFontButtonStyle");
樣式設置元素的初始外觀,可是能夠隨意覆蓋它們設置的這些特性。例如,若是應用了BigFontButtonStyle樣式,而且明確地設置了FontSize屬性,則在按鈕中的FindSize設置就會覆蓋樣式。
Setters集合是Style類中最重要的屬性,但並非惟一的屬性,在Style類中共有5個重要屬性: ide
設置樣式屬性
每一個Style對象包裝了一個Setter對象的集合,每一個Setter對象設置元素的單個屬性。惟一的限制是設置器只能改變依賴項屬性而不能修改其它屬性。
在某些狀況下,不能使用簡單的特性字符串設置屬性值,此時可使用嵌套的屬性元素代替特性,以下是一個示例:
工具
<Window.Resources> <Style x:Key="HappyFaceTileStyle"> <Setter Property="Control.Background"> <Setter.Value> <ImageBrush ImageSource="images/happyface.jpg" ViewportUnits="Absolute" Viewport="0,0,32,32" TileMode="Tile"></ImageBrush> </Setter.Value> </Setter> </Style> </Window.Resources>
若是但願在多個樣式中(或在同同樣式的多個設置器中)重用相同的圖像畫刷,能夠將其定義爲資源,而後再在樣式中使用資源:post
<Window.Resources> <ImageBrush x:Key="happyFace" ImageSource="images/happyface.jpg" ViewportUnits="Absolute" Viewport="0,0,32,32" TileMode="Tile"/> <Style x:Key="HappyFaceTileStyle"> <Setter Property="Control.Background" Value="{StaticResource happyFace}"></Setter> </Style> </Window.Resources>
爲了標識但願設置的屬性,須要設置類和屬性的名稱,然而,類名沒必要是定義屬性的類名,也能夠是繼承了屬性的派生類,以下示例,它使用Button類的引用代替了Control類的引用,此時,若是爲Label元素也引用了此樣式,哪麼對於Label此樣式就不起做用:字體
<Style x:Key="BigFontButtonStyle"> <Setter Property="Button.FontFamily" Value="Times New Roman"></Setter> <Setter Property="Button.FontSize" Value="20"></Setter> <Setter Property="Button.FontWeight" Value="Bold"></Setter> </Style>
以下樣式,Button.FontSize屬性和TextBlock.FontSize屬性是在它們各自的基類中分別聲明,但它們都引用同 一個依賴項屬性(也就是說,TextBlock.FontSize和Control.FontSize引用都指向同一個 DependencyProperty對象)。因此,當使用這個樣式時,WPF設置FontSize屬性兩次,最後引用的設置具備優先權,而且被同時引用 到Button和TextBlock對象。動畫
<Style x:Key="BigFontButtonStyle"> <Setter Property="Button.FontSize" Value="20"></Setter> <Setter Property="TextBlock.FontSize" Value="33"></Setter> </Style>
若是定義的樣式中全部的屬性都準備用於相同的元素類型,能夠設置Style對象的TargetType屬性,指定準備應用屬性的類來簡化樣式聲明,例如,若是隻建立只應用於按鈕的樣式,可按以下方式建立樣式:this
<Style x:Key="BigFontButtonStyle" TargetType="Button"> <Setter Property="FontFamily" Value="Times New Roman"></Setter> <Setter Property="FontSize" Value="20"></Setter> <Setter Property="FontWeight" Value="Bold"></Setter> </Style>
關聯事件處理程序
先看下面的示例,在樣式設置中使用EventSetter對象爲MouseEnter和MouseLeave事件關聯事件處理程序:url
<Style x:Key="MouseOverHighlightStyle"> <EventSetter Event="TextBlock.MouseEnter" Handler="element_MouseEnter"></EventSetter> <EventSetter Event="TextBlock.MouseLeave" Handler="element_MouseLeave"></EventSetter> <Setter Property="TextBlock.Padding" Value="5"></Setter> </Style>
下面是事件處理程序:spa
private void element_MouseEnter(object sender, MouseEventArgs e) { ((TextBlock)sender).Background = new SolidColorBrush(Colors.LightGoldenrodYellow); } private void element_MouseLeave(object sender, MouseEventArgs e) { ((TextBlock)sender).Background = null; }
MouseEnter和MouseLeave事件使用直接事件路由,這意味着它們不在元素樹中冒泡或隧道。若是但願爲大量元素應用以上的效 果,須要爲每一個元素添加MouseEnter和MouseLeave事件處理程序,基於樣式的事件處理程序簡化了這一任務,如今只須要應用單個樣式,該樣 式包含了屬性設置器和事件設置器:
<TextBlock Grid.Row="3" Style="{StaticResource MouseOverHighlightStyle}">Hover over me</TextBlock>
在WPF中,事件設置器是一種不多使用的技術,而更多的使用的是事件觸發器。當處理冒泡路由策略時,事件設置器不是一個好的選擇,對因而種狀況,在高層次的元素上處理但願處理的事件一般更容易。
多層樣式
儘管能夠在許多不一樣的層次定義任意數量的樣式,可是每一個元素一次只能使用一個樣式對象,乍一看,這好像是一種限制,但因爲屬性值繼承 和樣式繼承特性,所以,實際上這種限制並不存在。若是但願爲一組相同的控件使用相同的字體,而又不想爲每一個控件應用相同的樣式,對於這種狀況,能夠將它們 放置到一個面板,並設置面板的樣式,只要設置的屬性具備屬性值繼承特性,這些值就會被傳遞到子元素。使用這種模型的屬性包括IsEnabled、 IsVisible、Foreground以及全部字體屬性。
對於樣式繼承特性,能夠經過爲樣式設置 BasedOn 特性在另外一個樣式的基礎上建立樣式,例如如下代碼,第二個樣式繼承了第一個樣式的屬性設置:
<Style x:Key="BigFontButtonStyle"> <Setter Property="Control.FontFamily" Value="Times New Roman"></Setter> <Setter Property="Control.FontSize" Value="20"></Setter> <Setter Property="Control.FontWeight" Value="Bold"></Setter> </Style> <Style x:Key="EmphasizedBigFontButtonStyle" BasedOn="{StaticResource BigFontButtonStyle}"> <Setter Property="Control.Background" Value="White"></Setter> <Setter Property="Control.Foreground" Value="DarkBlue"></Setter> </Style>
除非有特殊緣由要求一個樣式繼承自另外一個樣式(例如,第二個樣式是第一個樣式的特例,而且只改變了繼承來有大量設置中的幾個特徵),不然不要使用樣式繼承,由於這種繼承會使應用程序更加脆弱。
經過類型自動應用樣式
能夠經過設置樣式的TargetType屬性爲特定類型的元素自動應用樣式,並徹底忽略鍵名,此時,WPF其實是隱式地使用類型標記擴展設置鍵名,以下代碼所示:
x:Key="{x:Type Button}"
以下示例,在一個窗口中爲樣式設置TargetType特性爲Button,此樣式就會被應用到這個窗口中的全部按鈕上:
<Style TargetType="Button"> <Setter Property="FontFamily" Value="Times New Roman"></Setter> <Setter Property="FontSize" Value="20"></Setter> <Setter Property="FontWeight" Value="Bold"></Setter> </Style>
能夠爲一個元素的樣式特性設置null值來刪除樣式:
<Button Style="{x:Null}">Test Button</Button>
儘管自動樣式很方便,可是它會使設計變得複雜,下面是幾條緣由。爲了不出現此問題,最好果斷的使用自動樣式爲整個用戶界面提供一個單一且一致的外觀,而後再爲特例設置明確的樣式。
<Style x:Key="BigFontButtonStyle"> <Style.Setters> <Setter Property="Control.FontFamily" Value="Times New Roman"></Setter> <Setter Property="Control.FontSize" Value="20"></Setter> <Setter Property="Control.FontWeight" Value="Bold"></Setter> </Style.Setters> <Style.Triggers> <Trigger Property="Control.IsFocused" Value="True"> <Setter Property="Control.Foreground" Value="DarkRed"></Setter> </Trigger> </Style.Triggers> </Style>
觸發器的優勢是不須要爲翻轉它們而編寫任何邏輯,只要中止應用觸發器,元素就會恢復到它的正常外觀。能夠建立一次應用相同元素的多個觸發器,若是這些觸發器設置不一樣的屬性,這種狀況就不會出現混亂,但若是多個觸發器修改相同的屬性,那麼最後的觸發器有效。
若是但願建立只有當幾個條件都爲真時才激活的觸發器,可使用 MultiTrigger。它提供了一個 Conditions 集合,能夠經過該屬性定義一系列屬性和值的組合,在下例中,只有當按鈕具備焦點且鼠標懸停在該按鈕上時,纔會應用樣式:
<Style x:Key="BigFontButtonStyle"> <Style.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="Control.IsFocused" Value="True"></Condition> <Condition Property="Control.IsMouseOver" Value="True"></Condition> </MultiTrigger.Conditions> <MultiTrigger.Setters> <Setter Property="Control.Foreground" Value="DarkRed"></Setter> </MultiTrigger.Setters> </MultiTrigger> </Style.Triggers> </Style>
<Style x:Key="EventTriggerTest"> <Style.Triggers> <EventTrigger RoutedEvent="Mouse.MouseEnter"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="FontSize" To="22"/> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> <EventTrigger RoutedEvent="Mouse.MouseLeave"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:1" Storyboard.TargetProperty="FontSize"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Style.Triggers> </Style>
1 public class DragInCanvasBehavior : Behavior<UIElement> 2 { 3 private Canvas canvas; 4 5 //如下的OnAttached()和OnDetaching()方法是用於監視MouseLeftButtonDown、MouseMove、MouseLeftButtonUp事件的代碼。 6 protected override void OnAttached() 7 { 8 base.OnAttached(); 9 10 // Hook up event handlers. 11 this.AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown; 12 this.AssociatedObject.MouseMove += AssociatedObject_MouseMove; 13 this.AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp; 14 } 15 16 protected override void OnDetaching() 17 { 18 base.OnDetaching(); 19 20 // Detach event handlers. 21 this.AssociatedObject.MouseLeftButtonDown -= AssociatedObject_MouseLeftButtonDown; 22 this.AssociatedObject.MouseMove -= AssociatedObject_MouseMove; 23 this.AssociatedObject.MouseLeftButtonUp -= AssociatedObject_MouseLeftButtonUp; 24 } 25 26 // Keep track of when the element is being dragged. 27 private bool isDragging = false; 28 29 // When the element is clicked, record the exact position 30 // where the click is made. 31 private Point mouseOffset; 32 33 //當用戶單擊鼠標左鍵時,DragInCanvasBehavior開始拖動操做,記錄元素左上角與鼠標指針之間的偏移,並捕獲鼠標。 34 private void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 35 { 36 // Find the canvas. 37 if (canvas == null) canvas = VisualTreeHelper.GetParent(this.AssociatedObject) as Canvas; 38 39 // Dragging mode begins. 40 isDragging = true; 41 42 // Get the position of the click relative to the element 43 // (so the top-left corner of the element is (0,0). 44 mouseOffset = e.GetPosition(AssociatedObject); 45 46 // Capture the mouse. This way you'll keep receiveing 47 // the MouseMove event even if the user jerks the mouse 48 // off the element. 49 AssociatedObject.CaptureMouse(); 50 } 51 52 //當元素處於拖動模式且移動鼠標時,從新定位元素。 53 private void AssociatedObject_MouseMove(object sender, MouseEventArgs e) 54 { 55 if (isDragging) 56 { 57 // Get the position of the element relative to the Canvas. 58 Point point = e.GetPosition(canvas); 59 60 // Move the element. 61 AssociatedObject.SetValue(Canvas.TopProperty, point.Y - mouseOffset.Y); 62 AssociatedObject.SetValue(Canvas.LeftProperty, point.X - mouseOffset.X); 63 } 64 } 65 66 //當釋放鼠標時,結束拖動。 67 private void AssociatedObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) 68 { 69 if (isDragging) 70 { 71 AssociatedObject.ReleaseMouseCapture(); 72 isDragging = false; 73 } 74 } 75 }
<Window x:Class="WpfApplication1.DragInCanvasTest" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:custom="clr-namespace:CustomBehaviousLibrary;assembly=CustomBehaviousLibrary" Title="DragInCanvasTest" Height="300" Width="300"> <Grid> <Canvas> <Rectangle Canvas.Left="36" Canvas.Top="49" Height="46" Name="rectangle1" Stroke="Black" Width="76" Fill="#FFC00000"> <i:Interaction.Behaviors> <custom:DragInCanvasBehavior></custom:DragInCanvasBehavior> </i:Interaction.Behaviors> </Rectangle> <Ellipse Canvas.Left="82" Canvas.Top="126" Height="69" Name="ellipse1" Stroke="Black" Width="39" Fill="#FF2323D1"> <i:Interaction.Behaviors> <custom:DragInCanvasBehavior></custom:DragInCanvasBehavior> </i:Interaction.Behaviors> </Ellipse> <Ellipse Canvas.Left="164" Canvas.Top="103" Height="34" Name="ellipse2" Stroke="Black" Width="41" Fill="#FF1CAA1C" /> </Canvas> </Grid> </Window>