WPF进阶-全方位了解ContentPresenter

B站影视 内地电影 2025-11-13 21:39 1

摘要:Content(基本内容):用于显示控件或数据ContentStringFormat(数据格式化):用于格式化数据,控件并不能生效ContentTemplate(数据模板):基于Content来设置对应显示的数据模板,数据类型即为 DataTemplateCo

基本概念

ContentPresenter是一个轻量级的内容显示控件,主要作用是显示单个对象内容

有哪些常用属性

Content(基本内容):用于显示控件或数据

ContentStringFormat(数据格式化):用于格式化数据,控件并不能生效

ContentTemplate(数据模板):基于Content来设置对应显示的数据模板,数据类型即为 DataTemplate

ContentTemplateSelector(模板选择器):基于Content来选择性选择多组其他自定义的数据模板

常规用法

简单的控件呈现

ContentPresenter> ContentPresenter.Content> Button Content="我是控件" /> ContentPresenter>

简单的数据呈现

12345

基于Content内容通过数据模板显示对应的效果

ContentPresenter Content="张三"> ContentPresenter.ContentTemplate> DataTemplate> Button Content="{Binding}" FontSize="20" Foreground="Green" VerticalAlignment="Center" HorizontalAlignment="Center" /> DataTemplate> ContentPresenter>

基于Content内容结合业务代码完成想要的模板显示

创建模板选择器

internal class TestContentTemplateSelector: DataTemplateSelector{ public DataTemplate Test1Template { get; set; } public DataTemplate Test2Template { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container) { if (item is string str) { if (str== "张三") { return Test1Template; } else if (str== "李四") { return Test2Template; } } return base.SelectTemplate(item, container); }
}

XAML实例化模板选择器并且使用

ContentPresenter Content="张三"> ContentPresenter.ContentTemplateSelector> local:TestContentTemplateSelector> local:TestContentTemplateSelector.Test1Template> DataTemplate> TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="20" Foreground="Red" Text="这是张三的模板" /> DataTemplate> local:TestContentTemplateSelector.Test2Template> DataTemplate> TextBlock FontSize="20" Foreground="Blue" Text="这是李四的模板" /> DataTemplate> ContentPresenter>

列表控件(ItemsControl)子项模板反推

在我们使用集合控件时,基于MVVM模式,我们用的最多的是ItemTemplate,我们发现它的类型是 DataTemplate,也就是说我们的每条数据对象都是可以用过 ItemTemplate进行渲染想要的控件,这时我们联想到集合控件渲染的基础控件可能是 ContentPresenter。

ItemsControl源码剖析

通过反编译ItemsControl我们查到 GetContainerForItemOverride方法返回的是 ContentPresenter,由此得出结论我们的猜想是对的!

当我们查看到 GetContainerForItemOverride这里,紧接着有一个 PrepareContainerForItemOverride方法,在这里 PrepareContainerForItemOverride 这个方法用于模板动态设置子项 ContentPresenter的Content、 ItemTemplate、 ItemTemplateSelector等相关属性。

在这里,我们看到集合列表基于 ContentPresenter完成了子项数据的模板渲染功能,由此可见 ContentPresenter不是一个简单的控件,而是承载着数据显示和效果渲染的重要控件。

注意:这里 ContentPresenter的DataContext并未将其进行使用, DataContext与Content的作用其实截然不同,当我们为 ContentPresenter 设置了 DataContext再进行设置ContentTemplate,其实是不生效的, ContentTemplate只针对于Content内容起作用,ContentTemplateSelector也是如此,切勿混淆使用。

进阶用法

由上述我们可以基于ItemsControl创建一个自定义依赖属性,比如HeaderTemplate,为我们自定义的子控件进行设置头部数据模板,通过重写 GetContainerForItemOverride和 PrepareContainerForItemOverride进行子项头部数据模板显示效果。

public class MyItemsControl : ItemsControl { public DataTemplate HeaderTemplate { get { return (DataTemplate)GetValue(HeaderTemplateProperty); } set { SetValue(HeaderTemplateProperty, value); } } // Using a DependencyProperty as the backing store for HeaderTemplate. This enables animation, styling, binding, etc... public static readonly DependencyProperty HeaderTemplateProperty = DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(MyItemsControl), new PropertyMetadata(null)); protected override DependencyObject GetContainerForItemOverride { return new HeaderedContentControl; } protected override bool IsItemItsOwnContainerOverride(object item) { return item is HeaderedContentControl; } protected override void PrepareContainerForItemOverride(DependencyObject element, object item) { if (element is HeaderedContentControl header) { header.HeaderTemplate = this.HeaderTemplate; } } }

自定义HeaderTemplate依赖属性数据渲染缺陷

当我们在XAML上面绑定了HeaderTemplate属性时,我们的VM动态设置Template,我们的前端是不生效的,我们应该为其新增一个回调OnHeaderTemplateChanged方法,用于刷新HeaderedContentControl的HeaderTemplate效果。

优化HeaderTemplate渲染效果

修改 PropertyMetadata,为其参数新增 OnHeaderTemplateChanged。

public DataTemplate HeaderTemplate { get { return (DataTemplate)GetValue(HeaderTemplateProperty); } set { SetValue(HeaderTemplateProperty, value); } } // Using a DependencyProperty as the backing store for HeaderTemplate. This enables animation, styling, binding, etc... public static readonly DependencyProperty HeaderTemplateProperty = DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(MyItemsControl), new PropertyMetadata(null, OnHeaderTemplateChanged)); private static void OnHeaderTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as MyItemsControl).OnHeaderTemplateChanged; } void OnHeaderTemplateChanged {

渲染难点

当我们为其新增了 OnHeaderTemplateChanged,但是发现我们不能很快的为其子项刷新效果,因为我们尚未找到刷新子项模板的有效方法,如果过我们想重新创建子控件进行替换其模板是及其困难和麻烦的。

巧用OnItemTemplateChanged方法

当我们在设置 ItemTemplate的时候发现ItemsControl会重新渲染HeaderTemplate,当我们查阅 OnItemTemplateChanged方法源代码时,发现此方法并未过多的设置一些效果,而是重新刷新容器,由此我们可以进行执行 OnItemTemplateChanged方法

由此我们可以进行通过 OnHeaderTemplateChanged 执行 OnItemTemplateChanged方法重新渲染效果达成我们绑定模板不能动态更新效果的问题,灵活引用自带 方法进行渲染,无多余性能消耗 。

void OnHeaderTemplateChanged { OnItemTemplateChanged(null,null); }

XAML示例代码

local:MyItemsControl ItemsSource="{Binding Items}"> local:MyItemsControl.HeaderTemplate> DataTemplate> TextBlock Text="{Binding UserName}" /> DataTemplate> local:MyItemsControl.ItemTemplate> DataTemplate> TextBlock Text="{Binding Content}" /> DataTemplate> local:MyItemsControl>

总结

ContentPresenter 就是 ItemsControl 中每个项目的可视化容器,当您修改 HeaderTemplate时,需要更新每个 ContentPresenter的 ContentTemplate属性,这就是为什么我们之前代码中要刷新容器的原因。

ContentPresenter 是解决WPF自定义控件问题的关键。

来源:opendotnet

相关推荐