DataGridComboBoxColumn绑定DataSet的ID及模板列样式优化问题
问题背景
我有一个包含Name和ID属性的DataSet对象,ViewModel中维护了一个DataSet列表,需要作为DataGrid的ComboBox列的ItemsSource。初始绑定代码如下:
<dataGridControl:DataGridComboBoxColumn Binding="{Binding DataSetIndex}" Header="DataSet" ItemsSource="{x:Bind Model.DataSets}" DisplayMemberPath="Name" Tag="DataSet" />
当前绑定可运行,但需求是将选中项的DataSet.ID(int类型)绑定到DataSetIndex,而非整个DataSet对象,此时报错提示ItemsSource无int属性。疑问是:社区工具包的DataGridComboBoxColumn是否没有SelectedValuePath属性?是否必须使用模板列?
之后基于方案更新了模板列代码:
<dataGridControl:DataGridTemplateColumn Header="DataSet"> <dataGridControl:DataGridTemplateColumn.CellEditingTemplate> <DataTemplate x:DataType="local:DataExtractionSetupViewModel"> <ComboBox x:Name="dataSetsComboBox" Loaded="ComboBox_Loaded" DisplayMemberPath="Name" SelectedValue="{Binding DataSetIndex, Mode=TwoWay}" SelectedValuePath="ID" IsDropDownOpen="True"/> </DataTemplate> </dataGridControl:DataGridTemplateColumn.CellEditingTemplate> <dataGridControl:DataGridTemplateColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding DatasetIndex}"/> </DataTemplate> </dataGridControl:DataGridTemplateColumn.CellTemplate> </dataGridControl:DataGridTemplateColumn>
现在的问题是:如何让该自定义模板列外观与原生DataGridComboBoxColumn完全一致,且CellTemplate中显示DataSet的名称而非索引值?
解决方案
1. 关于初始疑问的明确
WPF Community Toolkit的DataGridComboBoxColumn确实没有SelectedValuePath属性,这是它和原生WPF DataGridComboBoxColumn的核心差异之一。当需要绑定选中项的单个属性值而非整个对象时,必须使用模板列来实现。
2. 实现CellTemplate显示DataSet名称
可以通过两种方式实现名称的正确显示:
方式一:ViewModel添加计算属性
为DataGrid行数据对象新增一个计算属性,根据DataSetIndex(ID值)匹配对应的DataSet名称:
public string DataSetName { get { var targetSet = Model.DataSets.FirstOrDefault(ds => ds.ID == DataSetIndex); return targetSet?.Name ?? string.Empty; } }
修改CellTemplate的绑定:
<dataGridControl:DataGridTemplateColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding DataSetName}" Margin="4,2" VerticalAlignment="Center"/> </DataTemplate> </dataGridControl:DataGridTemplateColumn.CellTemplate>
方式二:使用值转换器
创建一个ID转名称的转换器:
public class DataSetIdToNameConverter : IValueConverter { public IEnumerable<DataSet> DataSets { get; set; } public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is int id && DataSets != null) { var targetSet = DataSets.FirstOrDefault(ds => ds.ID == id); return targetSet?.Name ?? string.Empty; } return string.Empty; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
在XAML资源中声明并绑定数据源:
<Page.Resources> <local:DataSetIdToNameConverter x:Key="DataSetIdToNameConverter" DataSets="{x:Bind Model.DataSets}"/> </Page.Resources>
修改CellTemplate绑定:
<dataGridControl:DataGridTemplateColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding DataSetIndex, Converter={StaticResource DataSetIdToNameConverter}}" Margin="4,2" VerticalAlignment="Center"/> </DataTemplate> </dataGridControl:DataGridTemplateColumn.CellTemplate>
3. 让模板列外观与原生列一致
原生DataGridComboBoxColumn的CellTemplate是带下拉箭头的只读样式,而非单纯文本块。直接在CellTemplate中使用只读ComboBox即可匹配外观:
<dataGridControl:DataGridTemplateColumn.CellTemplate> <DataTemplate> <ComboBox IsReadOnly="True" IsEditable="False" ItemsSource="{x:Bind Model.DataSets, Mode=OneWay}" SelectedValue="{Binding DataSetIndex, Mode=OneWay}" SelectedValuePath="ID" DisplayMemberPath="Name" VerticalAlignment="Center" Margin="2"/> </DataTemplate> </dataGridControl:DataGridTemplateColumn.CellTemplate>
这种方式无需额外属性或转换器,直接复用数据源绑定,同时完全匹配原生列的视觉样式。
内容的提问来源于stack exchange,提问作者miniHessel

