[WPF]如何使用代码创建DataTemplate(或者ControlTemplate)
1. 前言
上一篇文章([UWP]如何使用代码创建DataTemplate(或者ControlTemplate))介绍了在UWP上的情况,这篇文章再稍微介绍在WPF上如何实现。
2. 使用FrameworkElementFactory
FrameworkElementFactory用于以编程的方式创建模板,虽然文档中说不推荐,但WPF中常常使用这个类,例如DisplayMemberTemplateSelector。
FrameworkElementFactory text = new FrameworkElementFactory(typeof(TextBlock));
Binding binding = new Binding
{
Path = new PropertyPath("Name")
};
text.SetBinding(TextBlock.TextProperty, binding);
var xmlNodeContentTemplate = new DataTemplate();
xmlNodeContentTemplate.VisualTree = text;
xmlNodeContentTemplate.Seal();
ListControl.ItemTemplate = xmlNodeContentTemplate;
使用方式如上,这种方式可以方便地使用代码设置绑定或属性值,并且提供了AppendChild方法用于创建复杂的树结构。但是一旦这样做将使代码变得很复杂,建议还是不要这样做。
3. 使用XamlReader和XamlWriter
和UWP一样,WPF也支持使用XamlReader构建模板,只不过需要将
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
改为
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
和UWP不一样的是WPF还有XamlWriter这个工具。
XamlWriter提供一个静态 Save 方法,该方法可用于以受限的 XAML 序列化方式,将所提供的运行时对象序列化为 XAML 标记。如果使用这个类说不定可以用普通的方式创建一个UI元素并且最终创建它对应的DataTemplate,例如这样:
TextBlock text = new TextBlock();
Binding binding = new Binding("Name");
text.SetBinding(TextBlock.TextProperty, binding);
string xaml = string.Empty;
using (var stream = new MemoryStream())
{
XamlWriter.Save(text, stream);
using (var streamReader = new StreamReader(stream))
{
stream.Seek(0, SeekOrigin.Begin);
xaml = streamReader.ReadToEnd();
}
}
var template = (DataTemplate)XamlReader.Parse(@"
<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
" + xaml + @"
</DataTemplate>");
但现实没有这么简单,在生成xaml的那步就出错了,声称的xaml如下:
<TextBlock Text="" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />
可以看到这段XAML并没有反映text.SetBinding(TextBlock.TextProperty, binding);这段设置的绑定。具体原因可见XamlWriter.Save 的序列化限制。
值得庆幸的是WPF有足够长的历史,在这段历史里经过了无数人上上下下的折腾,上面提到的问题在10年前已经有人给出了解决方案:XamlWriter and Bindings Serialization。
首先,MarkupExtension及其派生类(如Binding)需要有一个TypeConverter以便可以序列化:
internal class BindingConvertor : ExpressionConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(MarkupExtension))
return true;
else return false;
}
public override object ConvertTo(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(MarkupExtension))
{
BindingExpression bindingExpression = value as BindingExpression;
if (bindingExpression == null)
throw new Exception();
return bindingExpression.ParentBinding;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
然后,需要由TypeDescriptor告诉大家要使用这个TypeConverter:
internal static class EditorHelper
{
public static void Register<T, TC>()
{
Attribute[] attr = new Attribute[1];
TypeConverterAttribute vConv = new TypeConverterAttribute(typeof(TC));
attr[0] = vConv;
TypeDescriptor.AddAttributes(typeof(T), attr);
}
}
EditorHelper.Register<BindingExpression, BindingConvertor>();
然后就可以愉快地使用了:
Binding binding = new Binding("Name");
TextBlock text = new TextBlock();
text.SetBinding(TextBlock.TextProperty, binding);
StringBuilder outstr = new StringBuilder();
//this code need for right XML fomating
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
OmitXmlDeclaration = true
};
var dsm = new XamlDesignerSerializationManager(XmlWriter.Create(outstr, settings))
{
//this string need for turning on expression saving mode
XamlWriterMode = XamlWriterMode.Expression
};
XamlWriter.Save(text, dsm);
var xaml = outstr.ToString();
var template = (DataTemplate)XamlReader.Parse(@"
<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
" + xaml + @"
</DataTemplate>");
这样就可以产生正确的XAML了:
<TextBlock Text="{Binding Path=Name}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />
不过我没遇到这么复杂的业务需求,所以这个方案我也没实际使用过。从原文的评论来看果然还是有些问题,如ValidationRules不能正确地序列化。总之使用要谨慎。
4. 结语
有关TypeConverter和TypeDescriptor的更多信息可见我的另一篇文章了解TypeConverter。不过回顾了这篇文章后我发觉我更需要的是简化文章的能力,所以以后尽可能还是写简短实用些。
5. 参考
FrameworkElementFactory
XamlWriter
XamlWriter and Bindings Serialization
TypeConverter
TypeDescriptor
了解TypeConverter
[WPF]如何使用代码创建DataTemplate(或者ControlTemplate)的更多相关文章
- [UWP]如何使用代码创建DataTemplate(或者ControlTemplate)
1. 前言 在UWP中DataTemplate是一个十分重要的功能,并且几乎无处不在,例如DataGrid中的DataGridTemplateColumn: <controls:DataGrid ...
- wpf 中用 C# 代码创建 PropertyPath ,以对间接目标进行 Storyboard 动画.
如图,一个 Rectangle 一个 Button ,点击按钮时要通过动画完成对 Rectangle填充色的渐变动画. Xaml: 1 <Window 2 x:Class="WpfAp ...
- WPF在代码中创建DataTemplate时候的异常
今天写段程序用到了在代码中手动创建DataTemplate, var factory = new FrameworkElementFactory(typeof(OperationColumn)); ...
- WPF Template模版之DataTemplate与ControlTemplate【一】
WPF Template模版之DataTemplate与ControlTemplate[一] 标签: Wpf模版 2015-04-19 11:52 510人阅读 评论(0) 收藏 举报 分类: -- ...
- WPF -- DataTemplate与ControlTemplate结合使用
如深入浅出WPF中的描述,DataTemplate为数据的外衣,ControlTemplate为控件的外衣.ControlTemplate控制控件的样式,DataTemplate控制数据显示的样式,D ...
- WPF Template模版之DataTemplate与ControlTemplate的关系和应用【二】
1. DataTemplate和ControlTemplate的关系 学习过DataTemplate和ControlTemplate,你应该已经体会到,控件只是数据的行为和载体,是个抽象的概念,至于它 ...
- 【转】WPF Template模版之DataTemplate与ControlTemplate的关系和应用(二)
1. DataTemplate和ControlTemplate的关系 学习过DataTemplate和ControlTemplate,你应该已经体会到,控件只是数据的行为和载体,是个抽象的概念,至于它 ...
- WPF使用后台C#代码创建Grid
笔者刚刚接触WPF,菜鸟一枚,在做一个练手程序时遇到这样一个需求,创建一个新的Grid并将其添加至一个ListView中,要求Grid及其子元素应按一定顺序给Name属性赋值,直接使用XAML创建的话 ...
- WPF DataTemplate與ControlTemplate
一. 前言 什麼是DataTemplate? 什麼是ControlTemplate? 在stackoverflow有句簡短的解釋 "A DataTemplate, therefore ...
随机推荐
- 结对编程--四则运算(Java)萧英杰 夏浚杰
结对编程--四则运算(Java)萧英杰 夏浚杰 Github项目地址 功能要求 题目:实现一个自动生成小学四则运算题目的命令行程序 使用 -n 参数控制生成题目的个数(实现) 使用 -r 参数控制题目 ...
- 使用Fraps获取3D程序的FPS
Fraps为免费软件(wiki),通过Hook OpenGL(SwapBuffer)或D3D(Present)来获取目标进程的FPS信息(32位进程注入fraps32.dll,64位注入fraps64 ...
- windows网络编程中文 笔记(二)
IPv4 地址段 IPv4地址类别 种类 网络部分 第1个数字 端点数字 A 8位 0-127 16777216 B 16位 128-191 65526 C 24位 193-223 ...
- java.lang.IllegalStateException: Cannot forward after response has been committed的一个情况解决方法
java.lang.IllegalStateException: Cannot forward after response has been committed xxx.xxx.doPost(upd ...
- 简单易懂的程序语言入门小册子(4):基于文本替换的解释器,递归,如何构造递归函数,Y组合子
递归.哦,递归. 递归在计算机科学中的重要性不言而喻. 递归就像女人,即令人烦恼,又无法抛弃. 先上个例子,这个例子里的函数double输入一个非负整数$n$,输出$2n$. \[ {double} ...
- c/c++ 标准库 pair 介绍
标准库 pair 介绍 问题:map里的元素由key和value组成,这个key和value的组合是什么类型呢??? 答案:pair类型 pair介绍: 它是模板 有2个公有成员可供访问. first ...
- linux shell 指令 诸如-d, -f, -e之类的判断表达式简介
一.文件比较运算符 1. e filename 如果 filename存在,则为真 如: [ -e /var/log/syslog ] 2. -d filename 如果 filename为目录,则为 ...
- jQuery设置radio、select、checkbox只读属性后,如何在后台得到数据
1 设置表单的readonly属性 对于radio.select.checkbox来说,readonly属性对这三个标签不起什么作用. 2 设置表单的disabled属性 以radio为例说明. 代码 ...
- GitHub-标签管理
参考博文:廖雪峰Git教程 1. 创建标签 切换到需要打标签的分支上,之后打标签 [root@mini05 zhangtest]# git branch dev * master [root@mini ...
- 解决终端SSH连接服务器一段时间不操作之后卡死的问题
卡死是因为LIUNX安全设置问题,在一段时间内没有使用数据的情况下会自动断开,解决方法就是让本地或者服务器隔一段时间发送一个请求给对方即可 在本地打开配置文件(不建议在server端设置) sudo ...