WPF DataGrid表头按钮绑定OpenWindowCommand报错排查
问题描述
在WPF项目中,在DataGridTemplateColumn.HeaderTemplate内的按钮上绑定OpenWindowCommand,运行时出现绑定路径错误,提示在String对象上找不到该属性,但ViewModel类中已定义public ICommand OpenWindowCommand属性。
相关代码
XAML代码:
<DataGridTemplateColumn.HeaderTemplate> <DataTemplate> <Grid > <Grid.RowDefinitions> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid Grid.Column="1" > <Button Command="{Binding OpenWindowCommand}"> <Image Source="{StaticResource IconImage}" Width="20" Height="20" /> </Button> </Grid> ...
ViewModel代码:
public ICommand OpenWindowCommand { get { ... } }
报错信息
System.Windows.Data Error: 40 : BindingExpression path error: 'OpenWindowCommand' property not found on 'object' ''String' (HashCode=1217782553)'. BindingExpression:Path=OpenWindowCommand; DataItem='String' (HashCode=1217782553); target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')
System.Windows.Data Information: 20 : BindingExpression cannot retrieve value due to missing information. BindingExpression:Path=OpenWindowCommand; DataItem='String' (HashCode=1217782553); target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')
System.Windows.Data Information: 21 : BindingExpression cannot retrieve value from null data item. This could happen when binding is detached or when binding to a Nullable type that has no value. BindingExpression:Path=OpenWindowCommand; DataItem='String' (HashCode=1217782553); target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=OpenWindowCommand; DataItem='String' (HashCode=1217782553); target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')
报错原因
DataGridTemplateColumn.HeaderTemplate的默认DataContext并不是页面/窗口的ViewModel,而是该列的Header内容(通常是字符串类型的列标题文本)。所以按钮的Command绑定会尝试在String对象上查找OpenWindowCommand属性,自然找不到,从而抛出绑定错误。
解决方法
方法1:使用RelativeSource绑定到父级DataGrid的DataContext
修改按钮的Command绑定,通过RelativeSource找到父级的DataGrid,再访问其DataContext中的命令:
<Button Command="{Binding DataContext.OpenWindowCommand, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"> <Image Source="{StaticResource IconImage}" Width="20" Height="20" /> </Button>
方法2:使用ElementName绑定
给DataGrid设置一个名称(比如x:Name="MainDataGrid"),然后通过ElementName直接绑定到它的DataContext:
<DataGrid x:Name="MainDataGrid" ...> <DataGridTemplateColumn.HeaderTemplate> <DataTemplate> <!-- 其他代码 --> <Button Command="{Binding DataContext.OpenWindowCommand, ElementName=MainDataGrid}"> <Image Source="{StaticResource IconImage}" Width="20" Height="20" /> </Button> </DataTemplate> </DataGridTemplateColumn.HeaderTemplate> </DataGrid>
方法3:显式设置HeaderTemplate根元素的DataContext
在HeaderTemplate的根容器(比如Grid)上直接设置DataContext为父级DataGrid的DataContext,这样内部所有控件的绑定都会默认使用这个上下文:
<DataGridTemplateColumn.HeaderTemplate> <DataTemplate> <Grid DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"> <Grid.RowDefinitions> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid Grid.Column="1" > <Button Command="{Binding OpenWindowCommand}"> <Image Source="{StaticResource IconImage}" Width="20" Height="20" /> </Button> </Grid> ...
内容的提问来源于stack exchange,提问作者AniAxe

