如何在WPF DataGrid中为每个数据源元素应用转换器?
在WPF DataGrid中为每个元素应用转换器的可行方案
你尝试的通过DataGridRow.DataContext绑定自身DataContext加转换器的方式存在两个核心问题:
- 会触发循环绑定:你试图用当前DataContext的值覆盖它自己,WPF会阻止这种循环,导致转换器无法正常执行。
- 匿名类型的访问限制:你返回的匿名类型是
internal级别,XAML无法访问其Name和Size属性,绑定会直接失效。
以下是三种可行的解决方案,按推荐度排序:
方案1:用公开具名类替代匿名类型(最推荐)
创建一个公开的统一数据类,让XAML可以正常访问属性,同时避免循环绑定问题。
步骤1:添加统一数据类
// 公开类,XAML可正常访问其属性 public class BvElementViewModel { public string Name { get; set; } public string Size { get; set; } }
步骤2:修改转换器返回该类实例
public class BvElementConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { string nameValue = null; string sizeValue = null; if (value is BvFile file) { nameValue = file.Name; sizeValue = file.Size.ToString(); } else if (value is BvFolder folder) { nameValue = folder.Name; sizeValue = "this is folder"; } return new BvElementViewModel { Name = nameValue, Size = sizeValue }; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return DependencyProperty.UnsetValue; } }
步骤3:正确配置XAML
首先在Window资源中声明转换器:
<Window.Resources> <local:BvElementConverter x:Key="ElementConverter"/> </Window.Resources>
然后修改ItemContainerStyle,绑定行的Item属性而非DataContext来避免循环:
<DataGrid x:Name="dGridTest" AutoGenerateColumns="False"> <DataGrid.ItemContainerStyle> <Style TargetType="{x:Type DataGridRow}"> <Setter Property="DataContext" Value="{Binding RelativeSource={RelativeSource Self}, Path=Item, Converter={StaticResource ElementConverter}}"/> </Style> </DataGrid.ItemContainerStyle> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding Name}" Header="Наименование"/> <DataGridTextColumn Binding="{Binding Size}" Header="Размер"/> </DataGrid.Columns> </DataGrid>
方案2:在列绑定中直接使用转换器
如果不想修改行的DataContext,可以在每个列的绑定中单独应用转换器,通过参数指定要提取的属性:
修改转换器支持参数
public class BvElementConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var propName = parameter as string; if (value is BvFile file) { return propName switch { "Name" => file.Name, "Size" => file.Size.ToString(), _ => null }; } else if (value is BvFolder folder) { return propName switch { "Name" => folder.Name, "Size" => "this is folder", _ => null }; } return null; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return DependencyProperty.UnsetValue; } }
配置列绑定
<DataGrid x:Name="dGridTest" AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTextColumn Header="Наименование" Binding="{Binding Converter={StaticResource ElementConverter}, ConverterParameter=Name}"/> <DataGridTextColumn Header="Размер" Binding="{Binding Converter={StaticResource ElementConverter}, ConverterParameter=Size}"/> </DataGrid.Columns> </DataGrid>
方案3:后台提前转换整个集合
直接在后台将原始集合转换为统一类型的集合,再赋值给ItemsSource,无需XAML中设置转换器:
public MainWindow() { InitializeComponent(); List<object> dataSource = new List<object>() { new BvFile { Name = "file1", Size = 200 }, new BvFolder { Name = "folder"} }; // 提前转换集合 var converter = new BvElementConverter(); var convertedList = dataSource .Select(item => converter.Convert(item, null, null, CultureInfo.CurrentCulture) as BvElementViewModel) .ToList(); dGridTest.ItemsSource = convertedList; }
(需配合方案1中的BvElementViewModel类使用)
内容的提问来源于stack exchange,提问作者Vladimir B
相关产品推荐
相关产品推荐

