You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义WPF DataGridTemplateColumn数据绑定失败问题求助

自定义DataGridTemplateColumn绑定失效问题修复

问题根源

你的代码存在三个核心问题:

  1. 构造函数过早访问依赖属性:XAML解析时构造函数先执行,此时Text和MaxLength属性还未从XAML绑定中获取有效值(仍为默认null),导致绑定到TextBlock/TextBox的是无效空绑定。
  2. 错误设置列的DataContext:DataGrid的列本身不继承行数据的DataContext,强行将列的DataContext绑定到DataGrid,反而破坏了单元格元素对行数据的绑定继承逻辑。
  3. 模板创建时机错误:在构造函数中一次性创建模板,无法响应后续依赖属性的变更(比如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ń

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 08:59:13