WPF QuickStart系列之样式和模板(Style and Template)
在WPF桌面程序中,当我们想构建一个统一的UI表现时(在不同操作系统下,显示效果一致),此时我们就需要使用到WPF中的样式和模板技术。简单来说,如果我们需要简单的给一个Button设置宽,高,Margin等,可以使用Style来指定这一系列的属性。可以把Style理解为一个属性的集合。如果需要完全改变控件的样子,就需要使用到Template技术,相当于给控件换一层皮,不过Button还是Button,它原有的行为(Click事件)还存在。而且我们仅需要在XAML中遍可以完成对样式和模板的定义和重写。非常简洁方便。
首先通过一个例子了解Style。
<Window.Resources>
<Style x:Key="numericStyle" TargetType="{x:Type Button}">
<Setter Property="FontSize" Value="20" />
<Setter Property="Margin" Value="4" />
<Setter Property="Padding" Value="6" />
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="Blue"/>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Button" x:Key="operatorStyle"
BasedOn="{StaticResource numericStyle}">
<Setter Property="FontWeight" Value="ExtraBold" />
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="Red" />
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Background="Cyan" IsReadOnly="True" Grid.ColumnSpan="4" FontSize="20"/>
<Button Content="7" Style="{StaticResource numericStyle}" Grid.Row="1"/>
<Button Content="8" Style="{StaticResource numericStyle}" Grid.Row="1" Grid.Column="1"/>
<Button Content="9" Style="{StaticResource numericStyle}" Grid.Row="1" Grid.Column="2"/>
<Button Content="4" Style="{StaticResource numericStyle}" Grid.Row="2"/>
<Button Content="5" Style="{StaticResource numericStyle}" Grid.Row="2" Grid.Column="1"/>
<Button Content="6" Style="{StaticResource numericStyle}" Grid.Row="2" Grid.Column="2"/>
<Button Content="1" Style="{StaticResource numericStyle}" Grid.Row="3"/>
<Button Content="2" Style="{StaticResource numericStyle}" Grid.Row="3" Grid.Column="1"/>
<Button Content="3" Style="{StaticResource numericStyle}" Grid.Row="3" Grid.Column="2"/>
<Button Content="0" Style="{StaticResource numericStyle}" Grid.Row="4"/>
<Button Content="=" Style="{StaticResource operatorStyle}" Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="2">
<Button.Effect>
<DropShadowEffect Color="Green"/>
</Button.Effect>
</Button>
<Button Content="+" Style="{StaticResource operatorStyle}" Grid.Row="4" Grid.Column="3"/>
<Button Content="-" Style="{StaticResource operatorStyle}" Grid.Row="3" Grid.Column="3"/>
<Button Content="X" Style="{StaticResource operatorStyle}" Grid.Row="2" Grid.Column="3"/>
<Button Content="/" Style="{StaticResource operatorStyle}" Grid.Row="1" Grid.Column="3"/>
</Grid>
运行效果:
通过上面的示例可以看到,
1. Style中包含了很多Setter,每个Setter都会对应着不同属性的设置。正如博客开头讲到的一样。Style是一组属性的集合;
2. 在Style中可以设置TargetType,指示这个Style是给哪一个控件使用的;
3. Style可以继承,例如操作按钮的Style继承了数字按钮的Style,使用BaseOn,然后引用到Style的资源即可;
4. Style的优先级,=按钮,在Style中设置了Button的DropShadowEffect为红色,然后在Button内部我们设置DropShadowEffect为蓝色,最后显示的效果可以看出来,=按钮最终颜色为蓝色。可以理解为后来者居上。
Style中不仅可以包含一系列的Setter,还可以包含Trigger。WPF中有三种Trigger,Property Trigger,Event Trigger,Data Trigger。下面我们介绍Property Trigger,沿用上面的示例,在鼠标点击按钮时,设置Transform效果。
<Style TargetType="Button" x:Key="operatorStyle"
BasedOn="{StaticResource numericStyle}">
<Setter Property="FontWeight" Value="ExtraBold" />
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="Red" />
</Setter.Value>
</Setter> <Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="RenderTransform">
<Setter.Value>
<TranslateTransform X="4" Y="4" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
运行效果如下:
Trigger表示当满足某个/某些条件时触发。上面的例子中,当IsPressed为True时,触发了Transform的改变,当IsPressed为False时,自动恢复到初始状态,不需要额外的代码来恢复初始状态。
不仅可以在Style中使用Trigger,还可以在DataTemplate,ControlTemplate中使用。
Property Trigger针对的是依赖属性,那普通属性改变时,如何触发UI的改变呢?所以下面介绍另一种Trigger,Data Trigger。请看示例:
XAML:
<ListBox HorizontalAlignment="Center" ItemsSource="{Binding .}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Border Margin="2" BorderBrush="Blue" BorderThickness="1" Padding="2" x:Name="_border">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Name}" FontSize="20" FontWeight="Bold" />
<TextBlock Grid.Row="1" Text="{Binding AuthorName}" FontSize="16" Foreground="Blue" />
<TextBlock Opacity=".5" FontWeight="Bold" FontStyle="Italic" Foreground="Red" TextAlignment="Right" Grid.RowSpan="2" VerticalAlignment="Center" Visibility="Hidden"
x:Name="_free" Text="Free!" Margin="4" FontSize="25"/>
</Grid>
</Border>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsFree}" Value="True">
<Setter Property="Background" TargetName="_border" Value="Yellow" />
<Setter Property="Visibility" Value="Visible" TargetName="_free" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
C#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent(); DataContext = new List<Book>
{
new Book { Name = "Windows Internals",
AuthorName = "Mark Russinovich", IsFree = false },
new Book { Name = "AJAX Introduction",
AuthorName = "Bhanwar Gupta", IsFree = true },
new Book { Name = "Essential COM",
AuthorName = "Don Box", IsFree = false },
new Book { Name = "Blueprint for a Successful Presentation",
AuthorName = "Biswajit Tripathy", IsFree = true }
};
}
} public class Book
{
public string Name { get; set; } public bool IsFree { get; set; } public string AuthorName { get; set; } }
运行效果:
DataTrigger根据Binding查找特定属性,当满足条件时触发。
下面简单介绍下Event Trigger,请看示例代码:
<Grid>
<Grid.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="0" To="1" Duration="0:0:5"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<TextBlock Text="Event Trigger Demo" FontSize="24"/>
</Grid>
运行效果,Grid的透明度从0到1。
注意:Event Trigger只可以用于路由事件。
上面我们介绍了三种Trigger,但是它们都是使用与满足某一个条件然后触发。如果要满足一些条件才触发,我们可以使用MultiTrigger,请看示例:
<Window.Resources>
<Style x:Key="HoverButtonStyle" TargetType="{x:Type Button}">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsDefault" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Background" Value="Cyan" />
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect />
</Setter.Value>
</Setter>
</MultiTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel Orientation="Vertical">
<Button Content="Move mouse over me" FontSize="20"
HorizontalAlignment="Center" Margin="20" Padding="6"
x:Name="theButton" Style="{StaticResource HoverButtonStyle}"/>
<CheckBox Content="Default button" Margin="10"
IsChecked="{Binding IsDefault, ElementName=theButton,
Mode=TwoWay}" FontSize="15"/>
</StackPanel>
运行效果:
注意:只有两种MultiTrigger,除了上面这种,还有MultiDataTrigger。用于当多个数据属性满足某一条件时触发。
下面通过一个示例来介绍ControlTemplate的使用,
例如有两个"原生态"的的RadioButton,
<StackPanel Orientation="Horizontal">
<RadioButton Content="作业练习" IsChecked="True" Margin="10,5,10,0"/>
<RadioButton Content="考试测验" Margin="0,5,10,0"/>
</StackPanel>
在Win 10 和Win 7中的显示效果如下:
同样的控件在Win10与Win7下显示效果不一致,下面我们对RadioButton进行"整容",
<Window.Resources>
<Style x:Key="RadioButtonStyle01" TargetType="RadioButton">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Foreground" Value="#565656"/>
<Setter Property="Background" Value="#EDEEEF"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="45"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Border x:Name="MainBorder" BorderThickness="1" BorderBrush="#8B99BC" CornerRadius="1" Background="#F0F2F2">
<Grid>
<Image x:Name="imgChecked" Visibility="Collapsed" Source="/ControlTemplatingDemo;component/Resources/Images/Completed_02.png" Width="20" Height="20" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,-8,-10,0"/>
<ContentPresenter RecognizesAccessKey="True" Content="{TemplateBinding ContentControl.Content}"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" Margin="5" HorizontalAlignment="Center"
VerticalAlignment="Center" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</Grid>
</Border> <ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="MainBorder" Property="Background" Value="#239FFF"/>
<Setter TargetName="imgChecked" Property="Visibility" Value="Visible"/>
<Setter Property="Foreground" Value="White"/>
<Setter TargetName="MainBorder" Property="BorderBrush" Value="#239FFF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel Orientation="Horizontal">
<RadioButton Content="作业练习" Style="{StaticResource RadioButtonStyle01}" IsChecked="True" Margin="10,5,10,0"/>
<RadioButton Content="考试测验" Style="{StaticResource RadioButtonStyle01}" Margin="0,5,10,0"/>
</StackPanel>
经过ControlTemplate样式重写后的RadioButton:
现在RadioButton在不同操作系统下外貌一致了。
为了在不同OS下获得相同的显示效果,我们需要对WPF的控件进行样式的重写。对控件样式的重写,可以理解为对它的表现进行重组。我们可以通过Blend来查看控件的内部构造,然后根据项目需求对控件进行重写。
感谢您的阅读。代码点击这里下载。
WPF QuickStart系列之样式和模板(Style and Template)的更多相关文章
- WPF QuickStart系列
接触WPF有一段时间了,现在做的项目也是WPF相关的.所以决定写一个WPF QuickStart系列的文章.也是自己对WPF学习的总结,如果对你有帮助,就非常棒了.因为不善言辞,所以尽量以WPF示例和 ...
- WPF QuickStart系列之数据绑定(Data Binding)
这篇博客将展示WPF DataBinding的内容. 首先看一下WPF Data Binding的概览, Binding Source可以是任意的CLR对象,或者XML文件等,Binding Targ ...
- WPF QuickStart系列之附加属性(Attached Property)
这一篇博客是关于如何使用附加属性和创建自定义附加属性的. 1. 附加属性使用, WPF中对附加属性使用最多的莫过于对控件布局时设置控件的位置,例如在Canvas中有一个Rectangle, Ellip ...
- WPF QuickStart系列之线程模型(Thread Model)
这篇博客将介绍WPF中的线程模型. 首先我们先来看一个例子,用来计算一定范围内的素数个数. XAML: <Grid> <Grid.RowDefinitions> <Row ...
- [WPF系列]-数据邦定之DataTemplate 对 ItemsControl 进行样式和模板处理
引言 即使 ItemsControl 不是 DataTemplate 所用于的唯一控件类型,将 ItemsControl 绑定到集合仍然很常见. 在 DataTemplate 中有哪些内容一节中, ...
- WPF笔记(1.9 样式和控件模板)——Hello,WPF!
原文:WPF笔记(1.9 样式和控件模板)--Hello,WPF! 资源的另一个用途是样式设置: <Window > <Window.Resources> <St ...
- 【WPF】样式与模板:鼠标移入/悬浮时按钮的背景色不改变
情况:鼠标移到按钮上,默认情况是按钮背景色会改变的,网上也能搜到很多如何自定义改变的背景色. 需求:现在需求反过来,想要鼠标移到按钮上,保持按钮的背景色不改变. 一种思路:在样式文件中,使用Multi ...
- wpf中的样式与模板
1.WPF样式类似于Web应用程序中的CSS,在WPF中可以为控件定义统一的样式(Style).样式属于资源的一种,例如为Button定义统一的背景颜色和字体: <Window.Resource ...
- WPF后台设置xaml控件的样式System.Windows.Style
WPF后台设置xaml控件的样式System.Windows.Style 摘-自 :感谢 作者: IT小兵 http://3w.suchso.com/projecteac-tual/wpf-zhi ...
随机推荐
- VQ结合SVM分类方法
今天整理资料时,发现了在学校时做的这个实验,当时整个过程过重偏向依赖分类器方面,而又很难对分类器性能进行一定程度的改良,所以最后没有选用这个方案,估计以后也不会接触这类机器学习的东西了,希望它对刚入门 ...
- discuz论坛移植修改数据库配置
从其他地方拷贝的discuz源码,可能需要修改数据库配置 分别打开discuz目录下面以下三个文件 discuzRoot/uc_server/data/config.inc.phpdiscuzRoot ...
- 【leetcode】Word Break II
Word Break II Given a string s and a dictionary of words dict, add spaces in s to construct a senten ...
- POJ 1068
http://poj.org/problem?id=1068 这道题是一道模拟的题目 题目大意呢,p代表前面的'('的个数,而w代表这个括号所包括的括号的个数: 给你p,要你求w: 解题思路: 首先, ...
- java.lang.UnsatisfiedLinkError: Couldn't load hyphenate_av from loader dalvik.system.PathClassLoader
android studio引入第三方库时报如下异常. 06-15 16:50:24.477 9497-9497/easemobim.test.com.easemobim E/AndroidRunti ...
- linux下用cronolog分割apache日志
linux下用cronolog分割apache日志,大神莫拍砖,菜鸟留一记录,小白请默默转载.连linux登陆和vi编辑都不会的,请默默关闭此页面.入正题 说明:淡绿色底的为linux命令,其他的为备 ...
- ItemsSource绑定后ScrollViewer不复位
ItemsSource绑定后ScrollViewer不复位 ItemsSource绑定后ScrollViewer不复位,有的时候我们需要这一效果,但大多数情况下我们是想让它复位的. 在WPF中也有这个 ...
- Effective C++ -----条款45:运用成员函数模板接受所有兼容类型
请使用member function templates(成员函数模板)生成”可接受所有兼容类型“的函数. 如果你声明member templates 用于“泛化copy构造”或“泛化assignme ...
- 两种js数组去重的方法
方法一: 新建一个数组,遍历原数组,在新数组内用IndexOf查找原数组内的每一项,如果没有找到,则添加到其中 代码如下: function arrayNew(arrs ){ var newArray ...
- 汉企PHP开班
明天PHP正式开班,没什么大目标 ,在四个半月的时间吧基础知识掌握牢固,自信的面对企业.