<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>