Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >[WPF]如何使用代码创建DataTemplate(或者ControlTemplate)

[WPF]如何使用代码创建DataTemplate(或者ControlTemplate)

原创
作者头像
dino.c
发布于 2019-01-17 01:06:25
发布于 2019-01-17 01:06:25
2.2K0
举报
文章被收录于专栏:dino.c的专栏dino.c的专栏

1. 前言

上一篇文章([UWP]如何使用代码创建DataTemplate(或者ControlTemplate))介绍了在UWP上的情况,这篇文章再稍微介绍在WPF上如何实现。

2. 使用FrameworkElementFactory

FrameworkElementFactory用于以编程的方式创建模板,虽然文档中说不推荐,但WPF中常常使用这个类,例如DisplayMemberTemplateSelector

代码语言:txt
AI代码解释
复制
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构建模板,只不过需要将

代码语言:txt
AI代码解释
复制
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

改为

代码语言:txt
AI代码解释
复制
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,例如这样:

代码语言:txt
AI代码解释
复制
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如下:

代码语言:txt
AI代码解释
复制
<TextBlock Text="" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />

可以看到这段XAML并没有反映<CODE>text.SetBinding(TextBlock.TextProperty, binding);</CODE>这段设置的绑定。具体原因可见XamlWriter.Save 的序列化限制

值得庆幸的是WPF有足够长的历史,在这段历史里经过了无数人上上下下的折腾,上面提到的问题在10年前已经有人给出了解决方案:XamlWriter and Bindings Serialization

首先,MarkupExtension及其派生类(如Binding)需要有一个TypeConverter以便可以序列化:

代码语言:txt
AI代码解释
复制
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:

代码语言:txt
AI代码解释
复制
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>();

然后就可以愉快地使用了:

代码语言:txt
AI代码解释
复制
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了:

代码语言:txt
AI代码解释
复制
<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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
[UWP]如何使用代码创建DataTemplate(或者ControlTemplate)
在UWP中DataTemplate是一个十分重要的功能,并且几乎无处不在,例如DataGrid中的DataGridTemplateColumn:
dino.c
2019/01/16
2.1K0
WPF-数据绑定
这是数据绑定中的特殊情况,Binding源本身就数据且不需要Path来指明,string、int等基本数据。
MaybeHC
2024/04/23
1990
WPF-数据绑定
WPF依赖属性(wpf 依赖属性)
依赖属性就是一种自己可以没有值,并且可以通过绑定从其他数据源获取值。依赖属性可支持WPF中的样式设置、数据绑定、继承、动画及默认值。
全栈程序员站长
2022/07/28
2.4K0
WPF依赖属性(wpf 依赖属性)
WPF 的 ElementName 在 ContextMenu 中无法绑定成功?试试使用 x:Reference!
发布于 2018-10-13 21:38 更新于 2018-10-14 04:25
walterlv
2020/02/10
3.6K0
WPF 应用启动过程同时启动多个 UI 线程且访问 ContentPresenter 可能让多个 UI 线程互等
在应用启动过程里,除了主 UI 线程之外,如果还多启动了新的 UI 线程,且此新的 UI 线程碰到 ContentPresenter 类型,那么将可能存在让新的 UI 线程和主 UI 线程互等。这是多线程安全问题,不是很好复现,即使采用 demo 的代码,也需要几千次运行才能在某些配置比较差的机器上遇到新的 UI 线程和主 UI 线程互等,应用启动失败。本文来告诉大家复现的步骤,以及原因,和解决方法
林德熙
2022/08/12
7040
WPF 应用启动过程同时启动多个 UI 线程且访问 ContentPresenter 可能让多个 UI 线程互等
XP上XamlReader读取XAML失败的问题
项目中DataGrid需要合并列,开始还以为XP不支持WPF动态模版创建,后来发现是XP上不支持绑定数据的格式化(加¥),把StringFormat={}{0:C}, ConverterCulture=zh-CN去掉,就可以正常绑定数据了
kiki.
2022/09/29
7690
[WPF]本地化入门
WPF的本地化是个很常见的功能,我做过的WPF程序大部分都实现了本地化(不管最终有没有用到)。通常本地化有以下几点需求:
dino.c
2019/01/18
2.8K0
[WPF]本地化入门
《深入浅出WPF》——模板学习
图形用户界面(GUI,Graphic User Interface)应用较之控制台界面(CUI,Command User Interface)应用程序最大的好处就是界面友好、数据显示直观。CUI程序中数据只能以文本的形式线性显示,GUI程序则允许数据以文本、列表、图形等多种形式立体显示。 用户体验在GUI程序设计中起着举足轻重的作用——用户界面设计成什么样子看上去才够漂亮?控件如何安排才简单易用并且少犯错误?(控件并不是越复杂越好)这些都是设计师需要考虑的问题。WPF系统不但支持传统Windows Forms(简称WinForm)编程的用户界面和用户体验设计,更支持使用专门的设计工具Microsoft Expression Blend进行专业设计,同时还推出了以模板为核心的新一代设计理念(这是2010年左右的书,在那时是新理念,放现在较传统.NET开发也还行,不属于落后的技术)。 本章我们就一同来领略WPF强大的模板功能的风采。
全栈程序员站长
2022/09/09
5.3K0
《深入浅出WPF》——模板学习
win10 uwp 在 ItemsPanelTemplate 里面通过样式绑定 Orientation 显示方向
在 UWP 是不支持在 Setter 里面的 Value 进行绑定,如果想要在 ItemsPanelTemplate 里面绑定显示方向,那么需要通过附加属性的方法绑定。如果在后台代码定义了 Orientation 属性想要在 xaml 绑定到 ListView 的样式,可以尝试多创建一个帮助属性,用于在里面绑定
林德熙
2019/03/19
9120
win10 uwp 在 ItemsPanelTemplate 里面通过样式绑定 Orientation 显示方向
WPF 制作 Windows 屏保
[1]GitHub: https://github.com/yanjinhuagood/ScreenSaver
独立观察员
2022/12/06
1K0
WPF 制作 Windows 屏保
win10 uwp 资源字典 资源的key所有的元素都可以定义资源合并资源字典主题资源共享的资源用户控件资源定义
本文主要翻译ResourceDictionary and XAML resource references - UWP app developer ,里面的代码我重新写了一下,有一些不相同。
林德熙
2018/09/18
1.1K0
《深入浅出WPF》学习笔记之深入浅出话Binding
  如果把Binding比作数据的桥梁,那么它的两端分别是Binding的源(Source)和目标(Target)。一般源是逻辑层对象,目标是UI层控件对象.
zls365
2021/02/26
6.1K0
WPF 中如何绑定附加属性?XAML 中记得加括号,C# 中记得不能用字符串
在 XAML 中写绑定是 WPF 学习的必修课,进阶一点的,是用 C# 代码来写绑定。然而一旦绑定的属性是附加属性,好多小伙伴就会开始遇到坑了。
walterlv
2020/04/02
3K0
WPF下可编辑Header的Tab控件实现
介绍 有这样一个需求,当用户双击Tab控件Header区域时, 希望可以直接编辑。对于WPF控件,提供一个ControlTemplate在加上一些Trigger就可以实现。效果如下: 代码 首先,我们需要给Tab Header设计一个ControlTemplate。类似一个TextBlock,双击进入编辑状态。 所以Xaml如下: <Setter Property="Template"> <Setter.Value> <ControlT
葡萄城控件
2018/01/10
1.2K0
[WPF自定义控件库]了解如何自定义ItemsControl
对WPF来说ContentControl和ItemsControl是最重要的两个控件。
dino.c
2019/05/21
2.8K0
[WPF自定义控件库]了解如何自定义ItemsControl
WPF--模板选择
  典型的,把模板关联到一块特定的数据上,不过通常希望动态的确定使用哪个模板---既可以基于一个属性值,也可以是一个全局状态。当真正需要大规模替换模板时,也可以使用DataTemplateSelector。            DataTemplateSelector提供了一个单一的方法----SelectTemplate,以允许通过执行任何逻辑来决定使用哪个模板。可以在被包含的元素中查找模板,并返回一些硬编码的模板,甚至动态的为每个条目创建模板。 首先,创建一个继承自DataTemplate
hbbliyong
2018/03/05
1.3K0
WPF--模板选择
WPF 数据绑定实例一
原理:监听事件机制,界面改变有TextChanged之类的事件,所以改变界面可以同步修改到对象
zls365
2021/02/26
9830
【愚公系列】2023年10月 WPF控件专题 ListView控件详解
WPF控件是Windows Presentation Foundation(WPF)中的基本用户界面元素。它们是可视化对象,可以用来创建各种用户界面。WPF控件可以分为两类:原生控件和自定义控件。
愚公搬代码
2023/10/11
1.1K0
WPF实现简单的数据绑定
首先创建一个作为数据源来使用,这里创建的类需要实现System.ComponentModel名称空间中的INotifyPropertyChanged接口。当为Binding设置了数据源之后,Binding就会自动侦听来自这个接口的PropertyChanged事件。
MaybeHC
2024/04/23
1950
WPF实现简单的数据绑定
WPF-Bingding的数据校验
在进行数据校验时,我们需要用到ValidationRule类,ValidationRule类是一个抽象类,在使用的时候我们需要创建它的派生类并实现它的Validate方法,Validate方法的返回值是ValidationResult类型对象,如果校验通过就把ValidationResult对象的IsValid属性设置为true,反之设为false并为ErrorContent属性设置一个消息内容。 下面以一个程序为例:
MaybeHC
2024/04/23
1080
WPF-Bingding的数据校验
推荐阅读
相关推荐
[UWP]如何使用代码创建DataTemplate(或者ControlTemplate)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档