WPF中能否修改验证错误模板属性复用同一ControlTemplate
WPF中能否复用验证错误模板并修改局部属性?
在WPF中使用
Validation.ErrorTemplate为所有验证不通过的控件统一显示校验图标时,若单个特殊控件需要调整校验图标的边距位置,可完全复用全局统一的控件模板,仅针对该控件修改模板内对应元素的Margin属性,无需单独创建新的ControlTemplate。
全局模板基础实现代码如下:
<UserControl> <UserControl.Resources> <ControlTemplate x:Key="ValidationTemplate" TargetType="Control"> <DockPanel> <Grid DockPanel.Dock="Right" Height="16" Margin="10,0,0,0" VerticalAlignment="Center" Width="16"> <Image AutomationProperties.AutomationId="_validationIcon" Source="{x:Static icons:Icons.ValidationIcon}" ToolTip="{Binding Path=ErrorContent}" /> </Grid> <AdornedElementPlaceholder /> </DockPanel> </ControlTemplate> <ItemsControl Validation.ErrorTemplate="{StaticResource ValidationTemplate}" /> </UserControl.Resources> </UserControl>
实现方案
不需要重复创建ControlTemplate,有两种零冗余的实现方案可以满足需求:
方案1:自定义附加属性传参(推荐,类型安全、易维护)
- 第一步:创建验证帮助类,注册
ValidationIconMargin附加属性,默认值设为全局通用的边距10,0,0,0,标准附加属性实现代码:
public static class ValidationAssist { public static readonly DependencyProperty ValidationIconMarginProperty = DependencyProperty.RegisterAttached( "ValidationIconMargin", typeof(Thickness), typeof(ValidationAssist), new PropertyMetadata(new Thickness(10, 0, 0, 0))); public static void SetValidationIconMargin(DependencyObject element, Thickness value) => element.SetValue(ValidationIconMarginProperty, value); public static Thickness GetValidationIconMargin(DependencyObject element) => (Thickness)element.GetValue(ValidationIconMarginProperty); }
- 第二步:修改全局验证模板中承载图标的Grid的Margin属性,通过相对源绑定到被装饰控件上的附加属性值
- 第三步:普通控件无需额外配置,自动使用默认边距;需要调整位置的控件直接给附加属性赋值即可
对应修改后的模板核心代码:
<ControlTemplate x:Key="ValidationTemplate" TargetType="Control"> <DockPanel> <Grid DockPanel.Dock="Right" Height="16" Margin="{Binding AdornedElement.(local:ValidationAssist.ValidationIconMargin), RelativeSource={RelativeSource AncestorType=Adorner}}" VerticalAlignment="Center" Width="16"> <Image AutomationProperties.AutomationId="_validationIcon" Source="{x:Static icons:Icons.ValidationIcon}" ToolTip="{Binding Path=ErrorContent}" /> </Grid> <AdornedElementPlaceholder /> </DockPanel> </ControlTemplate>
特殊控件的使用方式:
<ItemsControl Validation.ErrorTemplate="{StaticResource ValidationTemplate}" local:ValidationAssist.ValidationIconMargin="5,2,0,0" />
方案2:资源覆盖(无需编写C#代码,实现最快)
- 第一步:将模板中硬编码的Margin值替换为动态资源引用
- 第二步:在全局资源中定义对应Key的Thickness默认值
- 第三步:需要调整边距的控件,在自身资源字典中重写同名Key的Thickness值即可
对应修改后的模板核心代码:
<!-- 全局资源中定义默认边距 --> <Thickness x:Key="ValidationIconDefaultMargin">10,0,0,0</Thickness> <ControlTemplate x:Key="ValidationTemplate" TargetType="Control"> <DockPanel> <Grid DockPanel.Dock="Right" Height="16" Margin="{DynamicResource ValidationIconDefaultMargin}" VerticalAlignment="Center" Width="16"> <Image AutomationProperties.AutomationId="_validationIcon" Source="{x:Static icons:Icons.ValidationIcon}" ToolTip="{Binding Path=ErrorContent}" /> </Grid> <AdornedElementPlaceholder /> </DockPanel> </ControlTemplate>
特殊控件的使用方式:
<ItemsControl Validation.ErrorTemplate="{StaticResource ValidationTemplate}"> <ItemsControl.Resources> <!-- 重写当前控件范围内的图标边距 --> <Thickness x:Key="ValidationIconDefaultMargin">5,2,0,0</Thickness> </ItemsControl.Resources> </ItemsControl>
两种方案的实现原理都是利用WPF的属性值继承、资源查找优先级规则,让同一个模板可以根据承载控件的配置读取不同的参数值,完全避免了模板重复定义的问题。
内容的提问来源于stack exchange,提问作者amarsha4
相关产品推荐
相关产品推荐

