WPF DataGrid动态列绑定问题:单元格需独立SettingRow对象
解决WPF DataGrid动态列单元格绑定独立SettingRow的问题
你当前的核心问题是数据源结构和需求不匹配:用ObservableCollection<SettingRow>作为DataGrid的ItemsSource时,每行对应一个SettingRow对象,导致所有动态列的单元格都会绑定同一个行对象。要实现每个单元格对应独立的SettingRow,我们需要重新设计数据结构,让每行包含多个SettingRow(对应不同相位)。
下面是具体的解决方案:
1. 重新设计数据模型
创建一个RowViewModel类,用来表示DataGrid的一行,包含固定列的属性(比如Name),以及存储各相位SettingRow的集合:
public class RowViewModel : ObservableObject { public string Name { get; set; } public ObservableCollection<SettingRow> PhaseSettings { get; set; } = new ObservableCollection<SettingRow>(); }
原有SettingRow类可以保留,无需修改:
public class SettingRow : ObservableObject { public int ID { get; set; } public object Value { get; set; } public string Name { get; set; } public string Unit { get; set; } // 可选:添加可见性控制属性 private bool _isVisible = true; public bool IsVisible { get => _isVisible; set { _isVisible = value; RaisePropertyChangedEvent(nameof(IsVisible)); } } }
2. 更新ViewModel的数据源
把原来的SettingsCollection替换为ObservableCollection<RowViewModel>,并初始化每行对应的各相位SettingRow:
public class DynamicDataGridViewModel : ObservableObject { private ObservableCollection<RowViewModel> _rowCollection; public ObservableCollection<RowViewModel> RowCollection { get => _rowCollection; set { _rowCollection = value; RaisePropertyChangedEvent(nameof(RowCollection)); } } public DynamicDataGridViewModel() { RowCollection = new ObservableCollection<RowViewModel>(); // 第一行:Phase Active var activeRow = new RowViewModel { Name = "Phase Active" }; activeRow.PhaseSettings.Add(new SettingRow { ID = 0, Value = true, Unit = "" }); activeRow.PhaseSettings.Add(new SettingRow { ID = 1, Value = false, Unit = "" }); activeRow.PhaseSettings.Add(new SettingRow { ID = 2, Value = true, Unit = "" }); activeRow.PhaseSettings.Add(new SettingRow { ID = 3, Value = false, Unit = "" }); RowCollection.Add(activeRow); // 第二行:Minimum phase duration var durationRow = new RowViewModel { Name = "Minimum phase duration" }; durationRow.PhaseSettings.Add(new SettingRow { ID = 4, Value = 900, Unit = "s" }); durationRow.PhaseSettings.Add(new SettingRow { ID = 5, Value = 800, Unit = "s" }); durationRow.PhaseSettings.Add(new SettingRow { ID = 6, Value = 700, Unit = "s" }); durationRow.PhaseSettings.Add(new SettingRow { ID = 7, Value = 600, Unit = "s" }); RowCollection.Add(durationRow); // 第三行:Min Supply air temperature var tempRow = new RowViewModel { Name = "Min Supply air temperature" }; tempRow.PhaseSettings.Add(new SettingRow { ID = 8, Value = 50.0302, Unit = "C" }); tempRow.PhaseSettings.Add(new SettingRow { ID = 9, Value = 48.12, Unit = "C" }); tempRow.PhaseSettings.Add(new SettingRow { ID = 10, Value = 52.5, Unit = "C" }); tempRow.PhaseSettings.Add(new SettingRow { ID = 11, Value = 49.7, Unit = "C" }); RowCollection.Add(tempRow); } }
3. 调整动态列的生成逻辑
生成动态列时,让每个列绑定到RowViewModel.PhaseSettings中对应索引的SettingRow,并修正模板绑定逻辑:
public void GenerateColumns() { // 清除原有动态列(避免重复生成) var dynamicColumns = OverviewDataGridView.Columns.Cast<DataGridColumn>().Skip(1).ToList(); foreach (var col in dynamicColumns) OverviewDataGridView.Columns.Remove(col); // 生成4个相位列 for(int i = 0; i < 4; i++) { DataGridTemplateColumn templateCol = new DataGridTemplateColumn(); templateCol.Header = $"Phase {i}"; // 创建绑定路径:PhaseSettings[i],指向对应相位的SettingRow var binding = new Binding($"PhaseSettings[{i}]") { Mode = BindingMode.TwoWay }; // 构造单元格模板,将Content绑定到目标SettingRow var cellTemplate = new DataTemplate(); var contentPresenterFactory = new FrameworkElementFactory(typeof(ContentPresenter)); contentPresenterFactory.SetBinding(ContentPresenter.ContentProperty, binding); // 绑定模板选择器 var templateSel = new TemplateSelector(); templateSel.BooleanTemplate = this.Resources["BooleanTemplate"] as DataTemplate; templateSel.TextTemplate = this.Resources["TextTemplate"] as DataTemplate; contentPresenterFactory.SetValue(ContentPresenter.ContentTemplateSelectorProperty, templateSel); cellTemplate.VisualTree = contentPresenterFactory; templateCol.CellTemplate = cellTemplate; this.OverviewDataGridView.Columns.Add(templateCol); } }
4. 修正DataTemplateSelector
现在单元格的Content已经是独立的SettingRow,直接使用传入的item即可,无需再从DataGridCell获取上下文:
public class TemplateSelector : DataTemplateSelector { public DataTemplate BooleanTemplate { get; set; } public DataTemplate TextTemplate { get; set; } public override DataTemplate SelectTemplate(object item, DependencyObject container) { if(item is SettingRow row && row.Value != null) { switch (Type.GetTypeCode(row.Value.GetType())) { case TypeCode.Boolean: return BooleanTemplate; default: return TextTemplate; } } return null; } }
5. 更新XAML中的DataGrid配置
修改ItemsSource绑定到新的RowCollection,并保留固定的Name列(关闭自动生成列避免冲突):
<Window.Resources> <!-- 布尔值模板(可选添加可见性控制) --> <DataTemplate x:Key="BooleanTemplate"> <StackPanel Orientation="Horizontal" Margin="2" Visibility="{Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"> <CheckBox IsChecked="{Binding Value, Mode=TwoWay}"/> </StackPanel> </DataTemplate> <!-- 文本/数值模板 --> <DataTemplate x:Key="TextTemplate"> <StackPanel Orientation="Horizontal" Margin="2" Visibility="{Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"> <TextBox Height="25" MinWidth="70" FontSize="14"> <TextBox.Text> <MultiBinding StringFormat="{}{0} {1}"> <Binding Path="Value" Mode="TwoWay"/> <Binding Path="Unit" Mode="OneWay"/> </MultiBinding> </TextBox.Text> </TextBox> </StackPanel> </DataTemplate> <!-- 布尔值转可见性转换器 --> <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/> </Window.Resources> <DataGrid x:Name="OverviewDataGridView" ItemsSource="{Binding RowCollection, Mode=TwoWay}" AutoGenerateColumns="False" CanUserAddRows="False"> <DataGrid.Columns> <DataGridTextColumn Header="Name" Binding="{Binding Name}" IsReadOnly="True"/> </DataGrid.Columns> </DataGrid>
关键说明
- 现在每个动态列的单元格都绑定了独立的
SettingRow实例,支持独立编辑和模板选择。 - 如果需要控制单个单元格的可见性,已经在
SettingRow中添加了IsVisible属性,配合BooleanToVisibilityConverter即可实现。 - 记得在ViewModel初始化完成后调用
GenerateColumns()方法,比如在窗口的Loaded事件中执行:private void Window_Loaded(object sender, RoutedEventArgs e) { var vm = DataContext as DynamicDataGridViewModel; if(vm != null) GenerateColumns(); }
内容的提问来源于stack exchange,提问作者R Bakker
相关产品推荐
相关产品推荐

