WPF中为DataGrid多列共享带绑定的DataTemplate的实现问询
WPF DataGrid复用DataTemplate并在列级别指定绑定属性的解决方案
核心问题分析
原生WPF DataGrid的DataGridTemplateColumn不属于可视化树,导致共享DataTemplate中的控件无法直接通过RelativeSource找到列对象——这也是你之前出现Binding.RelativeSource cannot be set while using Binding.StaticSource错误的根本原因,列不在可视化树里,RelativeSource找不到目标元素。
可行解决方案:绑定代理+附加属性+转换器
通过以下步骤实现复用DataTemplate并在列级别指定绑定路径:
1. 实现绑定代理类
用来中转行数据的DataContext,解决可视化树隔离问题:
public class BindingProxy : Freezable { protected override Freezable CreateInstanceCore() { return new BindingProxy(); } public object Data { get => GetValue(DataProperty); set => SetValue(DataProperty, value); } public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy)); }
2. 定义自定义附加属性
用于在DataGridTemplateColumn上标记要绑定的属性路径:
public static class DataGridColumnHelpers { public static string GetColumnBindingPath(DependencyObject obj) { return (string)obj.GetValue(ColumnBindingPathProperty); } public static void SetColumnBindingPath(DependencyObject obj, string value) { obj.SetValue(ColumnBindingPathProperty, value); } public static readonly DependencyProperty ColumnBindingPathProperty = DependencyProperty.RegisterAttached("ColumnBindingPath", typeof(string), typeof(DataGridColumnHelpers)); }
3. 实现路径转绑定的转换器
把附加属性指定的路径字符串,转换成针对当前行数据的实际绑定:
public class PathToBindingConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is object rowData && parameter is string propertyPath) { return new Binding(propertyPath) { Source = rowData, Mode = BindingMode.TwoWay }.ProvideValue(null); } return null; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } }
4. XAML中配置使用
先在资源中定义绑定代理和共享DataTemplate:
<Grid.Resources> <local:BindingProxy x:Key="RowDataProxy" Data="{Binding}" /> <DataTemplate x:Key="datePickerTemplate"> <StackPanel> <DatePicker SelectedDate="{Binding Data, Source={StaticResource RowDataProxy}, Converter={local:PathToBindingConverter}, ConverterParameter={Binding Path=(local:DataGridColumnHelpers.ColumnBindingPath), RelativeSource={RelativeSource AncestorType=DataGridCell}}}" /> </StackPanel> </DataTemplate> </Grid.Resources>
然后在DataGrid列中指定附加属性和复用模板:
<DataGrid Name="CarDataGrid" AutoGenerateColumns="false"> <DataGrid.Columns> <DataGridTemplateColumn Header="生产日期" local:DataGridColumnHelpers.ColumnBindingPath="ManufacturedDate" CellTemplate="{StaticResource datePickerTemplate}"/> <DataGridTemplateColumn Header="过期日期" local:DataGridColumnHelpers.ColumnBindingPath="ExpireDate" CellTemplate="{StaticResource datePickerTemplate}"/> </DataGrid.Columns> </DataGrid>
方案说明
- 绑定代理
RowDataProxy负责将当前行的DataContext传递给模板中的控件,绕开可视化树隔离限制。 - 附加属性
ColumnBindingPath在列级别标记要绑定的属性名,实现同一模板对应不同属性的需求。 - 转换器
PathToBindingConverter将路径字符串转换成针对当前行数据的双向绑定,确保数据交互正常。
内容的提问来源于stack exchange,提问作者Anton Curmanschii
相关产品推荐
相关产品推荐

