自定义WPF DataGridTemplateColumn数据绑定失败问题求助
自定义DataGridTemplateColumn绑定失效问题修复
问题根源
你的代码存在三个核心问题:
- 构造函数过早访问依赖属性:XAML解析时构造函数先执行,此时
Text和MaxLength属性还未从XAML绑定中获取有效值(仍为默认null),导致绑定到TextBlock/TextBox的是无效空绑定。 - 错误设置列的DataContext:DataGrid的列本身不继承行数据的DataContext,强行将列的DataContext绑定到DataGrid,反而破坏了单元格元素对行数据的绑定继承逻辑。
- 模板创建时机错误:在构造函数中一次性创建模板,无法响应后续依赖属性的变更(比如XAML中设置的绑定值)。
修复方案(推荐重写生成方法)
DataGridColumn原生提供GenerateElement和GenerateEditingElement方法,专门用于生成显示/编辑状态的单元格元素,这是自定义列的标准实现方式,能完美规避构造函数的时机问题:
修复后的WindowedColumn代码
public class WindowedColumn : DataGridTemplateColumn { public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(Binding), typeof(WindowedColumn), new PropertyMetadata(null)); public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.Register(nameof(MaxLength), typeof(Binding), typeof(WindowedColumn), new PropertyMetadata(null)); public Binding Text { get => (Binding)GetValue(TextProperty); set => SetValue(TextProperty, value); } public Binding MaxLength { get => (Binding)GetValue(MaxLengthProperty); set => SetValue(MaxLengthProperty, value); } // 重写显示状态元素生成方法 protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem) { var textBlock = new TextBlock(); if (Text != null) { BindingOperations.SetBinding(textBlock, TextBlock.TextProperty, Text); } return textBlock; } // 重写编辑状态元素生成方法 protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem) { var stackPanel = new StackPanel { Orientation = Orientation.Horizontal }; var textBox = new TextBox { MinWidth = 50, MaxWidth = 200 }; if (Text != null) { BindingOperations.SetBinding(textBox, TextBox.TextProperty, Text); } if (MaxLength != null) { BindingOperations.SetBinding(textBox, TextBox.MaxLengthProperty, MaxLength); } var button = new Button { Content = ":" }; stackPanel.Children.Add(textBox); stackPanel.Children.Add(button); return stackPanel; } }
备选方案:通过DataTemplate绑定列的依赖属性
如果你更倾向于使用DataTemplate实现,可以通过RelativeSource让模板内元素绑定到列的依赖属性,同时移除构造函数中的错误代码:
public class WindowedColumn : DataGridTemplateColumn { public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(Binding), typeof(WindowedColumn), new PropertyMetadata(null, OnPropertyChanged)); public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.Register(nameof(MaxLength), typeof(Binding), typeof(WindowedColumn), new PropertyMetadata(null, OnPropertyChanged)); public Binding Text { get => (Binding)GetValue(TextProperty); set => SetValue(TextProperty, value); } public Binding MaxLength { get => (Binding)GetValue(MaxLengthProperty); set => SetValue(MaxLengthProperty, value); } private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var column = (WindowedColumn)d; column.UpdateTemplates(); } private void UpdateTemplates() { // 创建显示模板 var standardTemplate = new DataTemplate(); var textBlockFactory = new FrameworkElementFactory(typeof(TextBlock)); textBlockFactory.SetBinding(TextBlock.TextProperty, new Binding(nameof(Text)) { RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(WindowedColumn), 1) }); standardTemplate.VisualTree = textBlockFactory; CellTemplate = standardTemplate; // 创建编辑模板 var editingTemplate = new DataTemplate(); var stackPanelFactory = new FrameworkElementFactory(typeof(StackPanel)); stackPanelFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal); var textBoxFactory = new FrameworkElementFactory(typeof(TextBox)); textBoxFactory.SetValue(TextBox.MinWidthProperty, 50.0); textBoxFactory.SetValue(TextBox.MaxWidthProperty, 200.0); textBoxFactory.SetBinding(TextBox.TextProperty, new Binding(nameof(Text)) { RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(WindowedColumn), 1) }); if (MaxLength != null) { textBoxFactory.SetBinding(TextBox.MaxLengthProperty, new Binding(nameof(MaxLength)) { RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(WindowedColumn), 1) }); } var buttonFactory = new FrameworkElementFactory(typeof(Button)); buttonFactory.SetValue(ContentControl.ContentProperty, ":"); stackPanelFactory.AppendChild(textBoxFactory); stackPanelFactory.AppendChild(buttonFactory); editingTemplate.VisualTree = stackPanelFactory; CellEditingTemplate = editingTemplate; } }
验证
XAML使用方式保持不变即可:
<Columns:WindowedColumn Header="My Column" Text="{Binding OrderNumber}" MaxLength="{Binding MyMaxLengthProperty}"/>
此时单元格会正确继承行数据的DataContext,绑定OrderNumber和MyMaxLengthProperty并正常显示数据。
内容的提问来源于stack exchange,提问作者hubert kwiecień
相关产品推荐
相关产品推荐

