WPF中如何在DataGridTextColumn的样式模板中复用绑定配置?
问题背景
我需要修改DataGridTextColumn(后续会扩展到DataGridComboBoxColumn和DataGridTemplateColumn)的CellStyle、ElementStyle及EditingElementStyle模板。现有列定义如下:
<DataGridTextColumn CellStyle="{StaticResource DataGridTextColumnCellStyle}" ElementStyle="{StaticResource DataGridTextColumnElementStyle}" EditingElementStyle="{StaticResource MyPropertyDataGridTextColumnEditingElementStyle}" Header="My Property"> <DataGridTextColumn.Binding> <Binding Mode="TwoWay" NotifyOnValidationError="True" Path="MyProperty" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True" ValidatesOnNotifyDataErrors="True"> <Binding.ValidationRules> <vr:MyPropertyValidationRule ValidationStep="RawProposedValue" /> </Binding.ValidationRules> </Binding> </DataGridTextColumn.Binding> </DataGridTextColumn>
目前我对CellStyle和ElementStyle的区别不太清晰,但核心困扰是EditingElementStyle的绑定复用问题:因为不知道如何复用DataGridTextColumn上已定义的绑定,只能给样式键名加上MyProperty前缀,导致每个列都要写重复的样式代码。
当前样式定义如下:
<Style x:Key="MyPropertyDataGridTextColumnEditingElementStyle" TargetType="{x:Type TextBox}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <!-- CustomTextBox是包含自定义校验错误样式的UserControl,我还有类似的ComboBox、DatePicker等自定义控件 --> <controls:CustomTextBox Color="{TemplateBinding Foreground}"> <!-- 这里重复写了绑定,导致每个列都要复制这段代码,非常冗余 --> <Binding Mode="TwoWay" NotifyOnValidationError="True" Path="MyProperty" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True" ValidatesOnNotifyDataErrors="True"> <Binding.ValidationRules> <vr:MyPropertyValidationRule ValidationStep="RawProposedValue" /> </Binding.ValidationRules> </Binding> </controls:CustomTextBox> </ControlTemplate> </Setter.Value> </Setter> </Style>
我尝试用RelativeSource将CustomTextBox的Text属性绑定到DataGridColumn,但触发了**“双向绑定需要Path或XPath”**的错误,希望找到可行的解决方法。
先理清CellStyle和ElementStyle的区别
- ElementStyle:仅作用于单元格非编辑状态下的内部显示元素(比如
DataGridTextColumn的非编辑态是TextBlock),控制该元素的外观。 - CellStyle:作用于整个
DataGridCell容器,控制单元格本身的样式(如背景、边框、对齐方式等),其作用范围包含ElementStyle和EditingElementStyle。
核心解决方案:复用列绑定的正确方式
DataGridTextColumn进入编辑状态时,会自动将列上定义的绑定应用到编辑元素(即TextBox)的Text属性上。因此,自定义模板时只需将CustomTextBox的Text属性绑定到模板父级TextBox的Text属性,就能自动复用列上的绑定规则,无需重复编写。
修改后的通用EditingElementStyle:
<Style x:Key="GenericDataGridTextColumnEditingElementStyle" TargetType="{x:Type TextBox}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TextBox}"> <!-- 通过TemplateBinding继承父级TextBox的Text属性,自动复用列上的绑定和校验规则 --> <controls:CustomTextBox Text="{TemplateBinding Text}" Color="{TemplateBinding Foreground}" /> </ControlTemplate> </Setter.Value> </Setter> </Style>
列定义中直接使用这个通用样式即可,无需为每个属性单独创建样式:
<DataGridTextColumn CellStyle="{StaticResource DataGridTextColumnCellStyle}" ElementStyle="{StaticResource DataGridTextColumnElementStyle}" EditingElementStyle="{StaticResource GenericDataGridTextColumnEditingElementStyle}" Header="My Property"> <!-- 保留原有绑定定义,无需修改 --> <DataGridTextColumn.Binding> <Binding Mode="TwoWay" NotifyOnValidationError="True" Path="MyProperty" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True" ValidatesOnNotifyDataErrors="True"> <Binding.ValidationRules> <vr:MyPropertyValidationRule ValidationStep="RawProposedValue" /> </Binding.ValidationRules> </Binding> </DataGridTextColumn.Binding> </DataGridTextColumn>
错误原因分析
之前尝试直接绑定到DataGridColumn是错误的,因为DataGridColumn不属于可视化树,RelativeSource无法定位到它。正确的思路是利用模板绑定继承父级编辑元素的属性——父级元素的对应属性已经被DataGrid自动绑定到列定义的路径,只需传递给自定义控件即可。
扩展到其他列类型(如DataGridComboBoxColumn)
对于DataGridComboBoxColumn,复用逻辑完全一致:编辑元素是ComboBox,只需将自定义CustomComboBox的SelectedItem/SelectedValue等属性绑定到模板父级ComboBox的对应属性:
<Style x:Key="GenericDataGridComboBoxColumnEditingElementStyle" TargetType="{x:Type ComboBox}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ComboBox}"> <controls:CustomComboBox SelectedItem="{TemplateBinding SelectedItem}" ItemsSource="{TemplateBinding ItemsSource}" Foreground="{TemplateBinding Foreground}" /> </ControlTemplate> </Setter.Value> </Setter> </Style>
内容的提问来源于stack exchange,提问作者Ludovic Wagner

