如何在UserControl中用TemplateBinding定义转换器?异常排查
场景描述
有两个视觉结构完全一致的控件,唯一差异是绑定IsToggled属性时使用的IValueConverter不同:
原LeftHeader.xaml代码片段
<Path.RenderTransform> <RotateTransform Angle="{Binding IsToggled, Converter={x:Static local:LeftHeader.GlyphAngleConverter}}"/> </Path.RenderTransform>
原RightHeader.xaml代码片段
<Path.RenderTransform> <RotateTransform Angle="{Binding IsToggled, Converter={x:Static local:RightHeader.GlyphAngleConverter}}"/> </Path.RenderTransform>
为复用控件结构,尝试创建带有GlyphAngleConverter(类型为IValueConverter的依赖属性)的GlyphControl,模板代码如下:
<UserControl x:Class="GlyphControl"> <UserControl.Template> <ControlTemplate> <!-- 其他控件结构 --> <Path.RenderTransform> <RotateTransform Angle="{Binding IsToggled, Converter={TemplateBinding GlyphAngleConverter}}"/> </Path.RenderTransform> <!-- 其他控件结构 --> </ControlTemplate> </UserControl.Template> </UserControl>
使用时通过绑定传递静态转换器实例:
新LeftHeader.xaml代码片段
<GlyphControl GlyphAngleConverter="{Binding Source={x:Static local:LeftHeader.GlyphAngleConverter}}"/>
新RightHeader.xaml代码片段
<GlyphControl GlyphAngleConverter="{Binding Source={x:Static local:RightHeader.GlyphAngleConverter}}"/>
运行时抛出异常:
Unable to cast object of type 'System.Windows.TemplateBindingExpression' to type 'System.Windows.Data.IValueConverter'.
错误原因
TemplateBinding本质是一种绑定表达式对象(TemplateBindingExpression),而RotateTransform.Angle绑定中的Converter属性要求传入的是实现IValueConverter接口的实例对象。直接将TemplateBinding作为Converter的值,会导致类型不匹配,触发强制转换失败的异常。
修复方案
方案一:改用RelativeSource绑定替代TemplateBinding
将模板中的TemplateBinding替换为完整的RelativeSource绑定,这样可以正确解析到GlyphControl实例上的GlyphAngleConverter属性值(即实际的转换器实例):
<RotateTransform Angle="{Binding IsToggled, Converter={Binding RelativeSource={RelativeSource TemplatedParent}, Path=GlyphAngleConverter}}"/>
方案二:简化实现,直接通过资源传递转换器
如果不需要定义额外的依赖属性,可以直接在使用GlyphControl时,将转换器作为实例资源传入,模板中直接引用该资源:
- 修改
GlyphControl模板:
<UserControl x:Class="GlyphControl"> <UserControl.Template> <ControlTemplate> <!-- 其他控件结构 --> <Path.RenderTransform> <RotateTransform Angle="{Binding IsToggled, Converter={StaticResource GlyphAngleConverter}}"/> </Path.RenderTransform> <!-- 其他控件结构 --> </ControlTemplate> </UserControl.Template> </UserControl>
- 使用时传入对应转换器:
<!-- LeftHeader中使用 --> <GlyphControl> <GlyphControl.Resources> <local:LeftHeader.GlyphAngleConverter x:Key="GlyphAngleConverter"/> </GlyphControl.Resources> </GlyphControl> <!-- RightHeader中使用 --> <GlyphControl> <GlyphControl.Resources> <local:RightHeader.GlyphAngleConverter x:Key="GlyphAngleConverter"/> </GlyphControl.Resources> </GlyphControl>
额外注意点
如果坚持使用方案一,请确保GlyphControl的GlyphAngleConverter依赖属性定义正确:
public class GlyphControl : UserControl { public static readonly DependencyProperty GlyphAngleConverterProperty = DependencyProperty.Register( nameof(GlyphAngleConverter), typeof(IValueConverter), typeof(GlyphControl), new PropertyMetadata(null)); public IValueConverter GlyphAngleConverter { get => (IValueConverter)GetValue(GlyphAngleConverterProperty); set => SetValue(GlyphAngleConverterProperty, value); } // 其他属性(如IsToggled)的定义 }
内容的提问来源于stack exchange,提问作者mike

