1. ContentPresenter 是什么
ContentPresenter 是 WPF 中专门用来“显示内容”的元素。
它通常不单独作为业务控件使用,而是放在 ControlTemplate、DataTemplate、自定义控件模板中,用来把某个 Content 渲染出来。
常见位置:
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}">
<ContentPresenter />
</Border>
</ControlTemplate>
这里的 ContentPresenter 负责显示 Button.Content。
2. ContentPresenter 的核心功能
2.1 显示任意内容
ContentPresenter.Content 可以是字符串、数字、对象、UI 元素等。
<ContentPresenter Content="Hello WPF" />
<ContentPresenter>
<ContentPresenter.Content>
<StackPanel>
<TextBlock Text="Title" />
<Button Content="OK" />
</StackPanel>
</ContentPresenter.Content>
</ContentPresenter>
2.2 使用 ContentTemplate
可以通过 ContentTemplate 指定内容显示方式。
<ContentPresenter Content="{Binding User}">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<TextBlock Text=" – " />
<TextBlock Text="{Binding Age}" />
</StackPanel>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
如果 Content 是一个对象,ContentPresenter 会用 DataTemplate 把它显示出来。
2.3 使用 ContentTemplateSelector
当不同数据需要使用不同模板时,可以用 ContentTemplateSelector。
<ContentPresenter Content="{Binding CurrentItem}"
ContentTemplateSelector="{StaticResource MyTemplateSelector}" />
适合场景:
- 不同类型数据使用不同 UI
- 根据状态切换模板
- 消息列表、动态表单、配置面板等
2.4 使用 ContentStringFormat
用于格式化内容。
<ContentPresenter Content="{Binding Price}"
ContentStringFormat="Price: {0:C}" />
2.5 在 ControlTemplate 中显示控件内容
这是最常见用法。
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Button.Content 最终就是由 ContentPresenter 显示的。
3. ContentPresenter 常用属性
| Content | 要显示的内容 |
| ContentTemplate | 内容模板 |
| ContentTemplateSelector | 内容模板选择器 |
| ContentStringFormat | 内容字符串格式 |
| ContentSource | 在模板中指定内容来源 |
| RecognizesAccessKey | 是否识别访问键,例如 _File |
4. ContentSource 的作用
ContentSource 常用于 ControlTemplate 内部,用来告诉 ContentPresenter 应该显示模板宿主控件的哪个内容属性。
例如:
<ContentPresenter ContentSource="Content" />
它大致等价于:
<ContentPresenter Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"
ContentStringFormat="{TemplateBinding ContentStringFormat}" />
对于 ContentControl,默认就是显示 Content。
如果是类似 HeaderedContentControl,可以用:
<ContentPresenter ContentSource="Header" />
这会绑定到:
Header
HeaderTemplate
HeaderTemplateSelector
HeaderStringFormat
5. ContentPresenter 的内容显示规则
当 ContentPresenter 显示内容时,大致规则如下:
5.1 如果内容是 UIElement
直接显示该元素。
<ContentPresenter>
<ContentPresenter.Content>
<Button Content="Click Me" />
</ContentPresenter.Content>
</ContentPresenter>
5.2 如果设置了 ContentTemplate
使用 ContentTemplate 渲染。
<ContentPresenter Content="{Binding Person}"
ContentTemplate="{StaticResource PersonTemplate}" />
5.3 如果设置了 ContentTemplateSelector
由选择器决定使用哪个模板。
<ContentPresenter Content="{Binding Message}"
ContentTemplateSelector="{StaticResource MessageTemplateSelector}" />
5.4 如果没有模板
通常调用内容的 ToString() 显示。
<ContentPresenter Content="{Binding SomeObject}" />
如果 SomeObject.ToString() 返回 "abc",界面上就显示 abc。
6. ContentControl 是什么
ContentControl 是一个控件基类,用于承载单个内容。
常见继承自 ContentControl 的控件有:
ContentControl
├─ ButtonBase
│ ├─ Button
│ ├─ ToggleButton
│ └─ RepeatButton
├─ Label
├─ CheckBox
├─ RadioButton
├─ ToolTip
├─ GroupBox
├─ UserControl
└─ Window
它本身有 Content 属性:
<ContentControl Content="Hello" />
也可以承载复杂 UI:
<ContentControl>
<StackPanel>
<TextBlock Text="User Name" />
<TextBox />
</StackPanel>
</ContentControl>
7. ContentControl 常用功能
7.1 承载单个内容
<ContentControl Content="{Binding CurrentView}" />
常用于 MVVM 页面切换。
7.2 使用 ContentTemplate
<ContentControl Content="{Binding CurrentUser}">
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
7.3 使用 ContentTemplateSelector
<ContentControl Content="{Binding CurrentPage}"
ContentTemplateSelector="{StaticResource PageTemplateSelector}" />
7.4 作为自定义控件基类
如果一个控件只需要一个内容区域,可以继承 ContentControl。
public class CardControl : ContentControl
{
}
模板中通常放一个 ContentPresenter:
<Style TargetType="{x:Type local:CardControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CardControl}">
<Border Padding="12"
Background="White"
BorderBrush="Gray"
BorderThickness="1">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
8. ContentPresenter 和 ContentControl 的区别
核心区别
| 定位 | 控件 | 内容展示器 |
| 是否继承 Control | 是 | 否 |
| 是否有控件模板 Template | 有 | 没有 ControlTemplate |
| 是否用于交互 | 可以 | 通常不用于交互 |
| 是否可作为控件基类 | 可以 | 通常不作为控件基类 |
| 主要用途 | 承载一个内容 | 在模板中显示内容 |
| 常见使用位置 | 页面、控件、布局中 | ControlTemplate / DataTemplate 内部 |
| 是否拥有完整控件行为 | 是 | 否 |
简单理解
ContentControl
是一个“容器控件”。
<ContentControl Content="Hello" />
它本身是控件,负责承载内容、应用样式、使用控件模板。
ContentPresenter
是一个“内容显示占位符”。
<ContentPresenter Content="{TemplateBinding Content}" />
它主要负责把内容显示出来。
关系可以这样理解
很多 ContentControl 的默认模板内部都会包含一个 ContentPresenter。
ContentControl
└─ ControlTemplate
└─ ContentPresenter
└─ 显示 ContentControl.Content
也就是说:
- ContentControl 拥有 Content
- ContentPresenter 显示 Content
9. 示例:两者配合使用
自定义按钮模板
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="LightBlue"
Padding="8"
CornerRadius="4">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
使用:
<Button Content="Save" />
这里:
- Button 是 ContentControl
- Button.Content 是 "Save"
- ContentPresenter 负责显示 "Save"
10. 什么时候用 ContentControl
适合直接放在页面或控件中,用来显示某个内容。
场景 1:动态页面切换
<ContentControl Content="{Binding CurrentViewModel}" />
配合 DataTemplate:
<DataTemplate DataType="{x:Type vm:HomeViewModel}">
<views:HomeView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SettingViewModel}">
<views:SettingView />
</DataTemplate>
场景 2:封装一个可承载内容的控件
<local:CardControl>
<TextBlock Text="Card Content" />
</local:CardControl>
场景 3:显示单个对象
<ContentControl Content="{Binding SelectedItem}"
ContentTemplate="{StaticResource DetailTemplate}" />
11. 什么时候用 ContentPresenter
适合在模板内部使用。
场景 1:自定义控件模板
<ControlTemplate TargetType="{x:Type local:CardControl}">
<Border>
<ContentPresenter />
</Border>
</ControlTemplate>
场景 2:显示模板宿主的内容
<ContentPresenter Content="{TemplateBinding Content}" />
场景 3:显示 Header
<ContentPresenter ContentSource="Header" />
12. 常见误区
误区 1:用 ContentPresenter 替代 ContentControl
如果是在页面中动态显示内容,通常用:
<ContentControl Content="{Binding CurrentView}" />
而不是:
<ContentPresenter Content="{Binding CurrentView}" />
虽然有时也能显示,但语义上 ContentControl 更合适。
误区 2:模板中忘记放 ContentPresenter
如果自定义 Button 模板时没有放 ContentPresenter:
<ControlTemplate TargetType="Button">
<Border Background="Red" />
</ControlTemplate>
那么:
<Button Content="OK" />
OK 不会显示。
应写:
<ControlTemplate TargetType="Button">
<Border Background="Red">
<ContentPresenter />
</Border>
</ControlTemplate>
误区 3:没有绑定内容对齐属性
自定义模板中如果写死:
<ContentPresenter />
可能导致 HorizontalContentAlignment、VerticalContentAlignment 不生效。
更完整写法:
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
13. 总结
ContentControl
用于“拥有并承载内容”。
ContentControl = 一个可以放内容的控件
适合:
- 页面动态区域
- 自定义内容控件
- 承载单个对象或 UI
- MVVM 页面切换
ContentPresenter
用于“把内容显示出来”。
ContentPresenter = 模板里的内容显示位置
适合:
- ControlTemplate
- DataTemplate
- 自定义控件模板
- 显示 Content、Header 等内容属性
最重要一句话
ContentControl 是控件,拥有 Content;ContentPresenter 是模板里的显示器,负责把 Content 呈现出来。
了解更多
ContentPresenter 类
ContentControl 类 | Microsoft Learn
System.Windows.Controls 命名空间 | Microsoft Learn
控件库 – WPF .NET Framework | Microsoft Learn
WPF 介绍 | Microsoft Learn
使用 Visual Studio 创建新应用教程 – WPF .NET | Microsoft Learn
https://github.com/HeBianGu
HeBianGu的个人空间-HeBianGu个人主页-哔哩哔哩视频
GitHub – HeBianGu/WPF-Control: WPF轻量控件和皮肤库
GitHub – HeBianGu/WPF-ControlBase: Wpf封装的自定义控件资源库
