WPF中DataGrid列头无法实现本地化绑定的问题求助
我的ComboBox可以完美切换按钮内容,代码如下:
<ComboBox Margin="2 0 0 0" x:Name="languageComboBox" SelectedItem="{Binding SelectedLanguage,Mode=TwoWay}"> <ComboBoxItem Content="English" Tag="en-US"/> <ComboBoxItem Content="Japanese" Tag="jp-JP" /> </ComboBox>
但我无法切换DataGrid的列头内容,试了多种写法都失败了——比如把Id绑定到Label的Content属性能正常工作,但绑定到DataGrid列头就不行。
我试过的两种写法:
第一种:
</DataGrid> <DataGrid AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTextColumn HeaderStringFormat="{Binding Id}" Binding="{Binding Id}"/> <DataGridTextColumn HeaderStringFormat="{Binding Name}" Binding="{Binding Name}"/> <DataGridTextColumn HeaderStringFormat="{Binding Address}" Binding="{Binding Address}"/> </DataGrid.Columns> </DataGrid>
第二种:
</DataGrid> <DataGrid AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTextColumn Header="{Binding Id}" Binding="{Binding Id}"/> <DataGridTextColumn Header="{Binding Name}" Binding="{Binding Name}"/> <DataGridTextColumn Header="{Binding Address}" Binding="{Binding Address}"/> </DataGrid.Columns> </DataGrid>
运行时会抛出如下错误:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement
or FrameworkContentElement for target element.
BindingExpression:Path=Id; DataItem=null; target element is
'DataGridTextColumn' (HashCode=654914); target property is
'HeaderStringFormat' (type 'String')
问题根源
WPF里DataGridColumn不属于可视化树的一部分,没法直接继承DataGrid的DataContext,所以绑定找不到数据源,这就是报错的原因。
解决方法
方法1:用绑定代理(Freezable)共享DataContext
先创建一个继承自Freezable的类,用来保存DataContext:
public class BindingProxy : Freezable { protected override Freezable CreateInstanceCore() { return new BindingProxy(); } public object Data { get { return GetValue(DataProperty); } set { SetValue(DataProperty, value); } } public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null)); }
在XAML的资源里声明这个代理:
<Window.Resources> <local:BindingProxy x:Key="ViewModelProxy" Data="{Binding}" /> </Window.Resources>
然后在列头绑定中引用这个代理:
<DataGrid AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTextColumn Header="{Binding Data.Id, Source={StaticResource ViewModelProxy}}" Binding="{Binding Id}"/> <DataGridTextColumn Header="{Binding Data.Name, Source={StaticResource ViewModelProxy}}" Binding="{Binding Name}"/> <DataGridTextColumn Header="{Binding Data.Address, Source={StaticResource ViewModelProxy}}" Binding="{Binding Address}"/> </DataGrid.Columns> </DataGrid>
方法2:直接指定DataContext来源
如果ViewModel挂在Window/UserControl的DataContext上,可以通过ElementName或RelativeSource指定:
<DataGrid AutoGenerateColumns="False" x:Name="MyDataGrid"> <DataGrid.Columns> <DataGridTextColumn Header="{Binding DataContext.Id, ElementName=MyDataGrid}" Binding="{Binding Id}"/> <DataGridTextColumn Header="{Binding DataContext.Name, ElementName=MyDataGrid}" Binding="{Binding Name}"/> <DataGridTextColumn Header="{Binding DataContext.Address, ElementName=MyDataGrid}" Binding="{Binding Address}"/> </DataGrid.Columns> </DataGrid>
或者用RelativeSource:
<DataGridTextColumn Header="{Binding DataContext.Id, RelativeSource={RelativeSource AncestorType=Window}}" Binding="{Binding Id}"/>
方法3:用DataTemplate定义列头
把列头内容放到DataTemplate里,这样就能正常继承DataContext:
<DataGrid AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding Id}"> <DataGridTextColumn.HeaderTemplate> <DataTemplate> <TextBlock Text="{Binding DataContext.Id, RelativeSource={RelativeSource AncestorType=DataGrid}}"/> </DataTemplate> </DataGridTextColumn.HeaderTemplate> </DataGridTextColumn> <DataGridTextColumn Binding="{Binding Name}"> <DataGridTextColumn.HeaderTemplate> <DataTemplate> <TextBlock Text="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=DataGrid}}"/> </DataTemplate> </DataGridTextColumn.HeaderTemplate> </DataGridTextColumn> <DataGridTextColumn Binding="{Binding Address}"> <DataGridTextColumn.HeaderTemplate> <DataTemplate> <TextBlock Text="{Binding DataContext.Address, RelativeSource={RelativeSource AncestorType=DataGrid}}"/> </DataTemplate> </DataGridTextColumn.HeaderTemplate> </DataGridTextColumn> </DataGrid.Columns> </DataGrid>
内容的提问来源于stack exchange,提问作者haides

