在自定义TemplatedControl时,有时并不想借助资源文件来搭建控件的模板。比如一个容器型控件,它的模板可能只有一个ContentPresenter,其他的相关逻辑都是在后台代码中实现的。
根据上述场景,我们不妨回顾下WPF中是如何实现的...
在WPF中,有FrameworkElementFactory这么个东西,借助它可以在后台代码中进行VisualTree的搭建。如下代码示例便是创建了一个根元素为Border的模板:
var factory = new FrameworkElementFactory();
factory.Type = typeof(Border);
factory.Name = "PART_RootBorder";
this.Template = new ControlTemplate()
{
VisualTree = factory
};
而Avalonia中,没有这个工具,所以这个思路行不通!
通过阅读官方文档,笔者只找到了关于代码生成数据模板的示例。
var template = new FuncDataTemplate<Student>((value, namescope) =>
new TextBlock
{
[!TextBlock.TextProperty] = new Binding("FirstName"),
});
故而先尝试用FuncControlTemplate直接构建模板赋值给Template属性,结果在OnApplyTemplate方法中无法获取到元素。
究竟缺少了什么呢?
遇事不决,先翻源码。
果不其然,源码中ContentControl便是用这种方式构建模板的。与上述思路不同的是,它需要在静态构造函数中,对Template属性进行覆盖,以达到模板应用的目的。
比如创建一个只含有ContentPresenter的控件模板,在静态构造函数中的代码如下:
TemplateProperty.OverrideDefaultValue<CartesianChart>(new FuncControlTemplate((_, ns) => new ContentPresenter
{
Name = "PART_ContentPresenter"
}.RegisterInNameScope(ns)));
看上去实现起来也非常简单,本篇文章也只是对这一摸索过程的记录,不喜勿喷!