WPF中如何为自定义DataGridTemplateColumn绑定行级DataContext属性?
错误原因分析
无法找到目标元素对应的FrameworkElement或FrameworkContentElement。BindingExpression:Path=AvailablePointTypes; DataItem=null; target element is 'MultiEditComboBoxColumn' ...
这个错误的核心原因是:DataGrid的列对象不属于可视化树,它的DataContext继承自DataGrid本身(即MainViewModel),而非每行的PointViewModel。你试图在列上绑定行级的AvailablePointTypes属性(属于每行的PointViewModel),WPF会在MainViewModel中查找该属性,找不到就会导致DataItem为null,绑定失效。
正确解决方案
因为你的MultiEditComboBoxColumn继承自DataGridTemplateColumn,正确的做法是在列生成的单元格模板(CellTemplate/CellEditingTemplate)中的ComboBox上绑定行级属性,而非直接在列的依赖属性上绑定。
步骤1:修改自定义列的实现
调整MultiEditComboBoxColumn的单元格元素生成逻辑,让内部ComboBox直接绑定当前行的AvailablePointTypes:
public class MultiEditComboBoxColumn : DataGridTemplateColumn { // 移除原来的ItemsSource依赖属性(如果不需要列级全局数据源) // 保留你的自定义事件和后端逻辑 // 生成编辑状态的ComboBox protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem) { var comboBox = new ComboBox(); // 绑定到当前行DataContext(PointViewModel)的AvailablePointTypes comboBox.SetBinding(ComboBox.ItemsSourceProperty, new Binding(nameof(PointViewModel.AvailablePointTypes))); // 绑定选中项到PointViewModel的对应属性(比如SelectedPointType) comboBox.SetBinding(ComboBox.SelectedItemProperty, new Binding(nameof(PointViewModel.SelectedPointType))); // 添加你的自定义事件处理(比如SelectionChanged) comboBox.SelectionChanged += OnComboBoxSelectionChanged; return comboBox; } // 生成只读状态的显示元素(比如TextBlock) protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem) { var textBlock = new TextBlock(); // 绑定选中项的显示文本(比如SelectedPointType的Name属性) textBlock.SetBinding(TextBlock.TextProperty, new Binding($"{nameof(PointViewModel.SelectedPointType)}.Name")); return textBlock; } // 你的自定义事件逻辑 private void OnComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e) { // 这里写你的后端处理代码 } }
步骤2:简化XAML中的列配置
现在不需要在列上绑定ItemsSource,直接使用自定义列即可:
<DataGrid ItemsSource="{Binding Points}"> <DataGrid.Columns> <!-- 其他列 --> <local:MultiEditComboBoxColumn Header="Point Type"/> </DataGrid.Columns> </DataGrid>
补充说明
如果你的业务场景需要在列级别配置通用数据源(而非每行不同),可以保留列的ItemsSource依赖属性,但此时需要确保绑定的是DataGrid的DataContext(MainViewModel)中的全局集合。但根据你的需求,每行的AvailablePointTypes不同,所以直接在模板中绑定行级DataContext是最直接有效的方案。
内容的提问来源于stack exchange,提问作者5ORBEX

