You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:37:52