WPF-ResourceDictionary资源类型
1.style(样式)
作用
“对控件的属性进行统一设置,实现样式复用。”
本质
Style 本质是一个 属性设置器集合(Setter Collection),它不会改变控件的内部结构,只是批量修改属性值。
核心组成
| 组成部分 | 说明 |
|---|---|
| TargetType | 目标控件类型 |
| Setter | 设置属性值 |
| Trigger | 条件触发(鼠标悬浮、焦点等) |
| BasedOn | 继承另一个 Style |
基础示例
<Style x:Key="PrimaryButton" TargetType="Button">
<Setter Property="FontSize" Value="16"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Background" Value="#2196F3"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Padding" Value="15,8"/>
<Setter Property="Cursor" Value="Hand"/>
</Style>使用:
<Button Content="确定" Style="{StaticResource PrimaryButton}"/>带Trigger的示例(坑)
<Style x:Key="HoverButton" TargetType="Button">
<Setter Property="Background" Value="LightGray"/>
<Setter Property="FontSize" Value="14"/>
<Style.Triggers>
<!-- 鼠标悬浮时改变背景色 -->
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Orange"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
<!-- 按下时改变背景色 -->
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="DarkOrange"/>
</Trigger>
<!-- 禁用时变灰 -->
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5"/>
</Trigger>
</Style.Triggers>
</Style>这样写是不行的,实际只有IsEnabled生效而 IsMouseOver和IsPresseed的Background均不生效。
问题不在于 Trigger本身,而在 Button的默认模板。
需要自己去重写 ControlTemplate:
<Style x:Key="HoverButton" TargetType="Button">
<Setter Property="Background" Value="LightGray"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="BorderBrush" Value="Gray"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="{TemplateBinding Padding}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="Orange"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Background" Value="DarkOrange"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>对于这类视觉变化一般都写到 ControlTemplate.Triggers
隐式样式(省略x:Key)
<!-- 没有 x:Key,自动应用到所有 Button -->
<Style TargetType="Button">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Margin" Value="5"/>
</Style>“省略
x:Key= 隐式样式,作用域内所有该类型控件自动应用”
样式继承
<!-- 基础样式 -->
<Style x:Key="BaseButton" TargetType="Button">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="Margin" Value="5"/>
</Style>
<!-- 继承基础样式,添加更多属性 -->
<Style x:Key="DangerButton" TargetType="Button" BasedOn="{StaticResource BaseButton}">
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>- Style只能设置属性,不能改变内部组成
- 每个控件只能有一个Style,但可以通过 BaseOn 继承
- 隐式样式只影响当前作用域(Window/App/ResourceDictionary)
2.ControlTemplate(控件模板)
作用
“彻底重写控件的视觉结构*,决定控件"长什么样"、“由哪些元素组成”。”
本质
ControlTemplate 本质是一颗 Visual Tree(视觉树),它定义了控件的内部组成元素和布局方式。
与style的根本区别
| 对比项 | Style | ControlTemplate |
|---|---|---|
| 改什么 | 属性值 | 控件内部结构 |
| 比喻 | 给房子刷漆、换窗帘 | 把房子拆了重建 |
| 是否改变布局 | 否 | 是 |
| 是否添加新元素 | 否 | 是 |
基础示例:重写 Button
<ControlTemplate x:Key="RoundButtonTemplate" TargetType="Button">
<!-- 控件结构:一个圆角边框 + 内容 -->
<Border x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="20"
Padding="{TemplateBinding Padding}">
<!-- ContentPresenter = 显示 Button.Content -->
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
</ControlTemplate>使用:
<Button Content="圆角按钮"
Template="{StaticResource RoundButtonTemplate}"
Background="CornflowerBlue"
Foreground="White"
Padding="20,10"/>关键元素
| 元素 | 说明 |
|---|---|
| ContentPresenter | 显示 Content 属性的内容(用于ContentControl 类控件) |
| ItemsPressenter | 显示子项集合() |
| TemplateBinding | 绑定到模板化控件的属性 |
| x:Name | 给模板中的元素命名,供 Trigger 引用 |
带Trigger的完整实例
<ControlTemplate x:Key="FancyButton" TargetType="Button">
<Border x:Name="border"
Background="#2196F3"
CornerRadius="8"
Padding="15,8">
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<!-- 模板内的触发器 -->
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="#1976D2"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Background" Value="#0D47A1"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="border" Property="Background" Value="#BDBDBD"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>Style + ControlTemplate 结合使用(最常见的写法)
<Style x:Key="ModernButton" TargetType="Button">
<!-- Style 设置属性 -->
<Setter Property="FontSize" Value="14"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Background" Value="#4CAF50"/>
<!-- Style 中嵌入 ControlTemplate -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="border"
Background="{TemplateBinding Background}"
CornerRadius="6"
Padding="20,10">
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border"
Property="Background" Value="#388E3C"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>使用:
<Button Content="现代按钮" Style="{StaticResource ModernButton}"/>重写 TextBox 模板示例
<Style x:Key="ModernTextBox" TargetType="TextBox">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="10,8"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border x:Name="border"
Background="White"
BorderBrush="#BDBDBD"
BorderThickness="0,0,0,2"
Padding="{TemplateBinding Padding}">
<!-- ScrollViewer 是 TextBox 必须的,用于显示文字 -->
<ScrollViewer x:Name="PART_ContentHost"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter TargetName="border"
Property="BorderBrush" Value="#2196F3"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>“
PART_ContentHost是 TextBox 内部约定的名称,必须这样写,否则文字无法显示。”
3.DataTemplate(数据模板)
作用
“定义数据对象如何被呈现为 UI 元素。”
本质
DataTemplate 是一个 数据 → 视觉 的映射规则。
与 ControlTemplate 的区别
| 对比项 | ControlTemplate | DataTemplate |
|---|---|---|
| 针对什么 | 控件的外观 | 数据的展示方式 |
| 使用场景 | 重写 Button、TextBox 等 | ListBox、ComboBox、ItemsControl 中展示数据 |
| 绑定方式 | TemplateBinding | Binding |
| 比喻 | 改变容器的形状 | 改变容器里内容的展示方式 |
数据模型
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public string Avatar { get; set; }
public double Score { get; set; }
}基础示例
<DataTemplate x:Key="StudentTemplate">
<Border BorderBrush="LightGray" BorderThickness="1"
CornerRadius="8" Padding="10" Margin="5"
Background="#FAFAFA">
<StackPanel Orientation="Horizontal">
<!-- 头像 -->
<Ellipse Width="50" Height="50">
<Ellipse.Fill>
<ImageBrush ImageSource="{Binding Avatar}"/>
</Ellipse.Fill>
</Ellipse>
<!-- 信息 -->
<StackPanel Margin="10,0,0,0" VerticalAlignment="Center">
<TextBlock Text="{Binding Name}"
FontSize="16" FontWeight="Bold"/>
<TextBlock Text="{Binding Age, StringFormat='年龄: {0}岁'}"
Foreground="Gray"/>
<TextBlock Text="{Binding Score, StringFormat='成绩: {0}分'}"
Foreground="CornflowerBlue"/>
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>使用:
<ListBox ItemsSource="{Binding Students}" ItemTemplate="{StaticResource StudentTemplate}"/>隐式 DataTemplate(自动匹配类型)
<!-- 没有 x:Key,指定 DataType -->
<!-- 所有 Student 类型的数据自动使用这个模板 -->
<DataTemplate DataType="{x:Type local:Student}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" FontWeight="Bold" Margin="0,0,10,0"/>
<TextBlock Text="{Binding Score, StringFormat='{}{0}分'}" Foreground="Blue"/>
</StackPanel>
</DataTemplate>在 ContentControl 中使用
<!-- 比如展示选中的学生详情 -->
<ContentControl Content="{Binding SelectedStudent}" ContentTemplate="{StaticResource StudentDetailTemplate}"/>4.ItemsPanelTemplate(项面板模板)
作用
“定义 ItemsControl(ListBox、ListView 等)的子项用什么布局容器排列。*”
本质
默认情况下,ListBox 的子项是垂直排列的(用 VirtualizingStackPanel)。ItemsPanelTemplate 可以把它改成水平排列、网格排列等。
默认行为
ListBox 默认 → VirtualizingStackPanel(垂直排列)示例1:水平排列
<ItemsPanelTemplate x:Key="HorizontalPanel">
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>使用:
<ListBox ItemsSource="{Binding Items}" ItemsPanel="{StaticResource HorizontalPanel}"/>示例2:WrapPanel(自动换行)
<ItemsPanelTemplate x:Key="WrapLayout">
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate><!-- 标签墙效果 -->
<ItemsControl ItemsSource="{Binding Tags}"
ItemsPanel="{StaticResource WrapLayout}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="#E3F2FD" CornerRadius="12"
Padding="10,5" Margin="3">
<TextBlock Text="{Binding}" Foreground="#1565C0"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>示例3:UniformGrid(均匀网格)
<ItemsPanelTemplate x:Key="GridLayout">
<UniformGrid Columns="3"/>
</ItemsPanelTemplate><!-- 图片网格展示 -->
<ListBox ItemsSource="{Binding Photos}" ItemsPanel="{StaticResource GridLayout}"/>示例4:Canvas(自由定位)
<ItemsPanelTemplate x:Key="CanvasLayout">
<Canvas/>
</ItemsPanelTemplate>常用面板对比
| 面板 | 效果 | 适用场景 |
|---|---|---|
| StackPanel 垂直 | 从上到下排列 | 默认列表 |
| StackPanel 水平 | 从左到右排列 | 水平标签 |
| WraPanel | 自动换行 | 标签墙、照片墙 |
| UniformGrid | 均匀网格 | 棋盘、宫格 |
| Canvas | 绝对定位 | 自由拖拽 |
5.画刷资源
作用
“定义颜色填充方案,供多个控件复用。”
本质
画刷(Brush)是WPF中所有颜色/填充的基础类型,background、Foreground、BorderBrush 等属性都是 Brush 类型。
SolidColorBrush(纯色画刷)
<SolodColorBrush x:key="PrimaryBrush" Color="#2196F3"/>
<SolidColorBrush x:Key="DangerBrush" Color="#F44336"/>
<SolidColorBrush x:Key="SuccessBrush" Color="#4CAF50"/>
<SolidColorBrush x:Key="TextBrush" Color="#212121"/>
<SolidColorBrush x:Key="SubTextBrush" Color="#757575"/>使用:
<Button Background="{StaticResource PrimaryBrush}" Content="主要按钮"/>
<TextBlock Foreground="{StaticResource SubTextBrush}" Text="次要文字"/>LinearGradientBrush(线性渐变画刷)
线性渐变:颜色按照一条直线从一个点过渡到另一个点。渐变沿着一条直线进行,可以通过设置渐变的角度来控制渐变的方向。
<LinearGradientBrush x:Key="HeaderGradient" StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="#1A237E" Offset="0"/>
<GradientStop Color="#4A148C" Offset="0.5"/>
<GradientStop Color="#880E4F" Offset="1"/>
</LinearGradientBrush><Border Background="{StaticResource HeaderGradient}" Height="200">
<TextBlock Text="渐变背景" Foreground="White"
FontSize="24" HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>RadialGradientBrush(径向渐变画刷)
径向渐变:颜色从一个中心点开始,向外辐射到周围的区域。渐变的过渡是从中心点向外扩展的,形成一个环形的渐变效果。
<RadialGradientBrush x:Key="SpotlightBrush">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="Transparent" Offset="1"/>
</RadialGradientBrush>ImageBrush(图像画刷)
<ImageBrush x:Key="BackgroundImage"
ImageSource="/Assets/bg.jpg"
Stretch="UniformToFill"/><Border Background="{StaticResource BackgroundImage}" Height="300"/>画刷类型总结
| 类型 | 效果 | 场景 |
|---|---|---|
| SolidColorBrush | 纯色 | 最常用 |
| LinearGradientBrush | 线性渐变 | 标题栏、按钮 |
| RadialGradientBrush | 径向渐变 | 聚光灯、光晕 |
| ImageBrush | 图片填充 | 背景图 |
| VisualBrush | 用 Visual 元素填充 | 倒影、预览 |
| DrawingBrush | 用 Drawing 填充 | 纹理、图案 |
6.Color(颜色)
作用
“定义颜色值,供画刷或其它需要颜色的地方引用。”
本质
Color 是一个结构体值,不是画刷。它需要被画刷包裹后才能赋给 Background 等属性。
与SolidColorBrush的区别
| 类型 | 本质 | 能否直接赋给Background |
|---|---|---|
| Color | 颜色值(结构体) | 不能 |
| SolidColorBrush | 画刷(对象) | 可以 |
示例
<!-- 定义颜色 -->
<Color x:Key="PrimaryColor">#2196F3</Color>
<Color x:Key="DangerColor">#F44336</Color>
<Color x:Key="TextColor">#212121</Color>
<!-- 颜色用于画刷 -->
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource PrimaryColor}"/>
<SolidColorBrush x:Key="DangerBrush" Color="{StaticResource DangerColor}"/>“先定义Color,再用Color创建Brush,这样修改颜色只需这一处。”
颜色用于渐变
<Color x:Key="GradientStart">#1A237E</Color>
<Color x:Key="GradientEnd">#E91E63</Color>
<LinearGradientBrush x:Key="MyGradient" StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="{StaticResource GradientStart}" Offset="0"/>
<GradientStop Color="{StaticResource GradientEnd}" Offset="1"/>
</LinearGradientBrush>颜色用于动画
<Storyboard x:Key="ColorChange">
<ColorAnimation
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="{StaticResource DangerColor}"
Duration="0:0:0.3"/>
</Storyboard>“动画中必须用 Color,不能用 Brush。”
7.Thickness(边距/间距)
作用
“定义Margin、Padding、BorderThickness等需要上下左右值的属性。”
本质
Thickness 是一个结构体,包含 Left、Top、Right、Button四个值。
示例
<!-- 统一间距 -->
<Thickness x:Key="DefaultMargin">10</Thickness>
<!-- 等价于 10,10,10,10 -->
<!-- 水平/垂直不同 -->
<Thickness x:Key="CardPadding">15,10</Thickness>
<!-- 等价于 左右15,上下10 -->
<!-- 四个方向分别指定 -->
<Thickness x:Key="HeaderMargin">0,0,0,20</Thickness>
<!-- 左0,上0,右0,下20 -->
<!-- 边框粗细 -->
<Thickness x:Key="InputBorder">0,0,0,2</Thickness>
<!-- 只有底部有边框 -->使用:
<Button Margin="{StaticResource DefaultMargin}" Content="按钮"/>
<Border Padding="{StaticResource CardPadding}">
<TextBlock Text="卡片内容"/>
</Border>
<TextBox BorderThickness="{StaticResource InputBorder}"/>其它常见简单值类型
<!-- 字体大小 -->
<sys:Double x:Key="TitleFontSize">24</sys:Double>
<sys:Double x:Key="BodyFontSize">14</sys:Double>
<!-- 圆角半径 -->
<CornerRadius x:Key="CardRadius">8</CornerRadius>
<CornerRadius x:Key="ButtonRadius">20</CornerRadius>
<!-- 字体粗细 -->
<FontWeight x:Key="TitleWeight">Bold</FontWeight>
<!-- 字符串 -->
<sys:String x:Key="AppTitle">我的应用程序</sys:String>“需要引入命名空间:
xmlns:sys="clr-namespace:System;assembly=mscorlib"”
8.StoryBoard(动画故事板)
作用
“定义动画序列,控制属性随时间变化。”
本质
StoryBoard 是一个 动画时间线容器,内部包含一个或多个 Animation 对象,每个Animation 负责改变一个属性。
常用动画类型
| 动画类型 | 改变什么 | 示例 |
|---|---|---|
| DoubleAnimation | double值 | Opacity、Width、Height |
| ColorAnimation | 颜色 | Background.Color |
| ThicknessAnimation | Thickness值 | Margin、Padding |
| ObjectAnimationUsingKeyFrames | 任意对象 | Visibility |
示例1:淡入动画
<StoryBoard x:Key="FadenIn">
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
From="0"
To="1"
Duration="0:0:0.5"/>
</StoryBoard>使用:
<Grid>
<Button
Width="100"
Height="50"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="Click Me">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource FadenIn}" />
</EventTrigger>
</Button.Triggers>
</Button>
</Grid>示例2:淡出动画
<Storyboard x:Key="FadeOut">
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
From="1" To="0"
Duration="0:0:0.3"/>
</Storyboard>示例3:颜色变化
<Storyboard x:Key="ColorPulse">
<ColorAnimation
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
To="Red"
Duration="0:0:0.3"
AutoReverse="True"/>
</Storyboard>示例4:缩放动画(需要RenderTransform)
<Storyboard x:Key="ScaleUp">
<DoubleAnimation
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)"
To="1.1" Duration="0:0:0.2"/>
<DoubleAnimation
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
To="1.1" Duration="0:0:0.2"/>
</Storyboard>使用:
<Button
x:Name="MyButton"
Width="200"
Height="50"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="Click Me">
<Button.RenderTransform>
<ScaleTransform x:Name="ScaleTransform" />
</Button.RenderTransform>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource ScaleUp}" />
</EventTrigger>
</Button.Triggers>
</Button>在Style中使用动画
<Style x:Key="AnimatedButton" TargetType="Button">
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="1" ScaleY="1"/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="border" Background="#2196F3"
CornerRadius="8" Padding="15,8">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)"
To="1.05" Duration="0:0:0.15"/>
<DoubleAnimation
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
To="1.05" Duration="0:0:0.15"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)"
To="1.0" Duration="0:0:0.15"/>
<DoubleAnimation
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
To="1.0" Duration="0:0:0.15"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style><Button Style="{StaticResource AnimatedButton}" Content="Hover Me" HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="50"/>9.Converter(转换器)
作用
“在数据绑定时,将源数据转换为目标属性需要的格式。”
本质
Converter 本身是一个 C# 类,实现 IValueConverter 接口。在 ResourceDictionary 中注册资源,供 XAML 绑定使用。
定义转换器(C#代码)
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool b)
{
return b ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Visibility v)
{
return v == Visibility.Visible;
}
return false;
}
}在ResourceDictionary中注册
<ResourceDictionary xmlns:local="clr-namespace:MyApp.Converters">
<local:BoolToVisibilityConverter x:Key="BoolToVisibility"/>
</ResourceDictionary>使用
<TextBlock Text="加载中..."
Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibility}}"/>更多常用转换器示例
反转 Bool
public class InverseBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return value is bool b ? !b : value;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return value is bool b ? !b : value;
}
}数值 → 百分比文字
public class PercentConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value is double d)
return $"{d:P0}"; // 例如 0.85 → "85%"
return "0%";
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}空字符串 → 显示占位符
public class NullToPlaceholderConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
string s = value as string;
return string.IsNullOrEmpty(s) ? (parameter ?? "暂无数据") : s;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}多值转换器(IMultiValueConverter)
// 姓 + 名 → 全名
public class FullNameConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, CultureInfo culture)
{
string firstName = values[0] as string ?? "";
string lastName = values[1] as string ?? "";
return $"{lastName}{firstName}";
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}<local:FullNameConverter x:Key="FullNameConverter"/><TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource FullNameConverter}">
<Binding Path="FirstName"/>
<Binding Path="LastName"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>10.Geometry(几何图形)
作用
“定义矢量图形路径,用于图标、形状绘制。”
本质
Geometry 是一个数学描述的形状轮廓,它不会自己渲染,需要配合 Path 控件使用。
与 Shape 的区别
| 类型 | 说明 |
|---|---|
| Geometry | 纯数据(形状描述),轻量 |
| Path | UI控件,内部使用 Geomotry 来绘制 |
示例:定义图标路径
<!-- 关闭图标 -->
<Geometry x:Key="CloseIcon">
M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12Z
</Geometry>
<!-- 搜索图标 -->
<Geometry x:Key="SearchIcon">
M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z
</Geometry>
<!-- 菜单图标 -->
<Geometry x:Key="MenuIcon">
M3,6H21V8H3V6M3,11H21V13H3V11M3,16H21V18H3V16Z
</Geometry>
<!-- 箭头图标 -->
<Geometry x:Key="ArrowRightIcon">
M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z
</Geometry>使用
<!-- 方式1:Path 控件 -->
<Path Data="{StaticResource CloseIcon}"
Fill="Red" Width="24" Height="24"
Stretch="Uniform"/>
<!-- 方式2:Button 中使用图标 -->
<Button Width="40" Height="40" Background="Transparent" BorderThickness="0">
<Path Data="{StaticResource SearchIcon}"
Fill="#757575" Width="20" Height="20"
Stretch="Uniform"/>
</Button>封装为图标按钮 Style
<Style x:Key="IconButton" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Width" Value="36"/>
<Setter Property="Height" Value="36"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="border" Background="{TemplateBinding Background}"
CornerRadius="18">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border"
Property="Background" Value="#1A000000"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style><Button Style="{StaticResource IconButton}">
<Path Data="{StaticResource MenuIcon}"
Fill="#333" Width="18" Height="18"
Stretch="Uniform"/>
</Button>Geometry 来源
通常不手写路径数据,而是从以下来源获取:
| 来源 | 说明 |
|---|---|
| Material Design Icons | Google 图标库,大量现成路径 |
| SVG 转换 | 将 SVG 文件转换为 Geometry |
| Blend 设计工具 | 直接绘制后导出 |
| Font Awesome | 字体图标转矢量 |
11. 其他常见资源类型补充
FontFamily(字体)
<FontFamily x:Key="TitleFont">Microsoft YaHei UI</FontFamily>
<FontFamily x:Key="CodeFont">Consolas</FontFamily><TextBlock FontFamily="{StaticResource TitleFont}" Text="标题"/>BitmapImage(位图)
<BitmapImage x:Key="Logo" UriSource="/Assets/logo.png"/>
<BitmapImage x:Key="DefaultAvatar" UriSource="/Assets/avatar.png"/><Image Source="{StaticResource Logo}" Width="100"/>GridLength / ColumnDefinition(不常放资源里但可以)
一般不放 ResourceDictionary,但理论上可以。
ContextMenu
<ContextMenu x:Key="EditContextMenu">
<MenuItem Header="剪切" Command="ApplicationCommands.Cut"/>
<MenuItem Header="复制" Command="ApplicationCommands.Copy"/>
<MenuItem Header="粘贴" Command="ApplicationCommands.Paste"/>
<Separator/>
<MenuItem Header="全选" Command="ApplicationCommands.SelectAll"/>
</ContextMenu><TextBox ContextMenu="{StaticResource EditContextMenu}"/>DrawingImage(矢量图标作为 Image 源)
<DrawingImage x:Key="CheckIcon">
<DrawingImage.Drawing>
<GeometryDrawing Brush="Green">
<GeometryDrawing.Geometry>
<PathGeometry Figures="M9,20.42L2.79,14.21L5.62,11.38L9,14.77L18.88,4.88L21.71,7.71L9,20.42Z"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingImage.Drawing>
</DrawingImage><Image Source="{StaticResource CheckIcon}" Width="24" Height="24"/>12. ResourceDictionary 本身的用法
在 Window 中定义局部资源
<Window.Resources>
<SolidColorBrush x:Key="MyBrush" Color="Red"/>
<Style x:Key="MyStyle" TargetType="Button">
<Setter Property="Background" Value="{StaticResource MyBrush}"/>
</Style>
</Window.Resources>独立的资源文件
文件:Themes/ButtonStyles.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="PrimaryBrush" Color="#2196F3"/>
<Style x:Key="PrimaryButton" TargetType="Button">
<Setter Property="Background" Value="{StaticResource PrimaryBrush}"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
</Style>
</ResourceDictionary>在 App.xaml 中合并
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Colors.xaml"/>
<ResourceDictionary Source="Themes/Brushes.xaml"/>
<ResourceDictionary Source="Themes/ButtonStyles.xaml"/>
<ResourceDictionary Source="Themes/TextBoxStyles.xaml"/>
<ResourceDictionary Source="Themes/Converters.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>推荐的文件组织结构
MyApp/
├── Themes/
│ ├── Colors.xaml ← Color 定义
│ ├── Brushes.xaml ← Brush 定义(引用 Colors)
│ ├── Sizes.xaml ← Thickness、Double、CornerRadius
│ ├── Fonts.xaml ← FontFamily
│ ├── Icons.xaml ← Geometry 图标
│ ├── Converters.xaml ← 转换器注册
│ ├── ButtonStyles.xaml ← Button 的 Style + Template
│ ├── TextBoxStyles.xaml ← TextBox 的 Style + Template
│ ├── ListBoxStyles.xaml ← ListBox 的 Style + Template
│ └── DataTemplates.xaml ← DataTemplate
├── Converters/
│ ├── BoolToVisibilityConverter.cs
│ └── PercentConverter.cs
├── ViewModels/
├── Views/
└── App.xaml ← 合并所有资源字典资源查找顺序
控件自身 Resources
↓
父容器 Resources
↓
Window Resources
↓
App.xaml Resources
↓
Theme Resources(Generic.xaml)就近原则:先从自身找,找不到往上找。
13. 总结对比表
| 资源类型 | 本质 | 作用 | 关键用途 |
|---|---|---|---|
| Style | 属性设置器集合 | 批量设置控件属性 | 统一外观、触发器 |
| ControlTemplate | 视觉树 | 重写控件内部结构 | 自定义控件外观 |
| DataTemplate | 数据→UI 映射 | 定义数据如何展示 | ListBox、ContentControl |
| ItemsPanelTemplate | 布局容器定义 | 改变子项排列方式 | 横向列表、网格 |
| SolidColorBrush | 画刷对象 | 颜色填充 | Background、Foreground |
| LinearGradientBrush | 渐变画刷 | 渐变效果 | 标题栏、装饰 |
| Color | 颜色值(结构体) | 定义颜色 | 供 Brush 和动画使用 |
| Thickness | 四边值(结构体) | Margin/Padding/Border | 间距统一管理 |
| Storyboard | 动画时间线容器 | 属性动画 | 过渡效果、交互反馈 |
| Converter | C# 类实例 | 数据绑定转换 | Bool→Visibility 等 |
| Geometry | 矢量路径数据 | 图标/形状 | 矢量图标 |
| BitmapImage | 位图引用 | 图片资源 | Logo、头像 |
| FontFamily | 字体引用 | 字体定义 | 全局字体 |
| CornerRadius | 圆角值 | Border 圆角 | 卡片、按钮 |
| sys:Double | 数值 | 字体大小等 | 统一尺寸 |
| sys:String | 字符串 | 文本内容 | 国际化、常量 |
一句话记忆
| 类型 | 一句话 |
|---|---|
| Style | 给控件穿衣服 |
| ControlTemplate | 给控件换骨骼 |
| DataTemplate | 给数据化妆 |
| ItemsPanelTemplate | 给列表换队形 |
| Brush | 颜料 |
| Color | 颜料的配方 |
| Storyboard | 动画剧本 |
| Converter | 翻译官 |
| Geometry | 图标的蓝图 |
“ResourceDictionary 就是一个大仓库,里面放着各种“可复用的零件”。Style 和 ControlTemplate只是其中最常见的两种,实际上任何可序列化的对象都可以作为资源存放。”