目录

WPF-Binidng在ControlTemplate 内会失效的问题

{Binding}在ControlTemplate里经常失效,并不是Binding写错了,而是ControlTemplate里的元素根本没有DataContext.

一、一个失效的例子:

<ControlTemplate TargetType="Button">
   <TextBlock Text="{Binding Title}"/>
</ControlTemplate>

em……,这里可能会想:“Button在页面上”,Button的DataContext是ViewModel,那ControlTemplate 内的TextBlock应该也能绑定到ViewModel的Title吧?

这是不对的

分析下失效的原因或者说这里具体发生了什么:

首先要清楚 ControlTemplate内的元素不在逻辑树,不继承外部的DataContext,DataContext默认是null。

可这样说:

在ControlTemplate 等价于 TextBlock.DataContext == null

此时{Binding}什么也绑定不到,所以看起来就好似“失效”了

二、那么在ControlTemplate内该怎么写Binding呢?

规则1:模板里绑定控件自身属性–使用 TemplateBinding

<Border Background="{TemplateBinding Background}"/>

规则2:用 RelativeSource TemplatedParent

<TextBlock
    Text="{Binding Content,
           RelativeSource={RelativeSource TemplatedParent}}"/>

等价于:“绑定到应用了这个模板的控件”

一个常见的错误写法:

<TextBlock Text="{Binding Content}"/>

因为:DataContext是null,Binding无法解析

三、{Binding}在ControlTemplate内什么时候能用?

情况:ControlTemplate内部某个元素自己“被显式设置了DataContext”

例如:

<ControlTemplate TargetType="Button">
   <Grid DataContext="{TemplateBinding Content}"
      <TextBlock Text="{Binding}"/>
   </Grid>
</ControlTemplate>

此时:Grid.DataContext = Button.Contet,DataContext不为null,{Binding} 自然就有了意义。

四、官方模板写法

<ContentPresenter
   Content="{TemplateBinding Content}"
   ContentTemplate="{TemplateBinding ContentTemplate}"/>

为什么不直接写 Content="{Binding}"?

因为 ContentPresenter必须绑定的是 控件属性 ,不是DataContext.

五、对于上述的所有,WPF为什么要这样设计?

目的:ControlTemplate是“控件外观”,不是“业务视图”

如果ControlTemplate能随意访问ViewModel,那么一个Button的外观和业务就耦合了,模板将不可复用,控件库会直接废掉,

所以 WPF 强制:模板只能认识“控件自己”,不能认识“外部数据”

六、使用武侠进行总结:

ControlTemplate:“统一发的门派制服”

它只能知道:衣服尺寸(控件属性),颜色(控件属性),样式(控件属性)

它不知道:穿衣服的人是谁(ViewModel),这个人的身份、名字、背景

所以 {Binding} 就等于 “我想用当前人的内力” 但你 根本不知道这个人是谁 –> 失败,正确的方式是“用这件衣服本身的属性”

最后:在 ControlTemplate 内 Binding 时,ControlTemplate 不认 DataContext,只认自己 TemplatedParent,要么TemplateBinding,要么 RelativeSource.