摘要: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
