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

如何在MVVM架构的WPF DataGrid中通过多选下拉菜单动态添加列

界面示意图

可行实现方案(完全符合MVVM架构规范,无业务逻辑耦合到后台代码)

方案核心思路

通过附加属性扩展DataGrid的列集合绑定能力,监听下拉菜单选项的选中状态变化,动态增删列集合中的元素,自动同步到UI层。

具体实现步骤

1. 定义下拉选项实体类,实现属性变化通知

你的下拉选项需要在IsSelected属性变更时发出通知,才能触发后续的列更新逻辑:

public class ColumnOption : INotifyPropertyChanged
{
    private string _position;
    public string Position
    {
        get => _position;
        set { _position = value; OnPropertyChanged(); }
    }

    private bool _isSelected;
    public bool IsSelected
    {
        get => _isSelected;
        set { _isSelected = value; OnPropertyChanged(); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

2. 实现DataGrid可绑定列的附加属性

WPF原生的DataGrid.Columns不是依赖属性,无法直接绑定,我们通过附加属性扩展支持绑定:

public static class DataGridBindableColumnsExtension
{
    public static readonly DependencyProperty BindableColumnsProperty =
        DependencyProperty.RegisterAttached(
            "BindableColumns", 
            typeof(ObservableCollection<DataGridColumn>), 
            typeof(DataGridBindableColumnsExtension),
            new PropertyMetadata(null, OnBindableColumnsChanged));

    public static ObservableCollection<DataGridColumn> GetBindableColumns(DependencyObject obj)
    {
        return (ObservableCollection<DataGridColumn>)obj.GetValue(BindableColumnsProperty);
    }

    public static void SetBindableColumns(DependencyObject obj, ObservableCollection<DataGridColumn> value)
    {
        obj.SetValue(BindableColumnsProperty, value);
    }

    private static void OnBindableColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var dataGrid = d as DataGrid;
        if (dataGrid == null) return;

        dataGrid.Columns.Clear();
        if (e.NewValue is not ObservableCollection<DataGridColumn> columns) return;
        
        // 初始化列
        foreach (var column in columns)
        {
            dataGrid.Columns.Add(column);
        }

        // 监听列集合变化,同步到UI
        columns.CollectionChanged += (sender, args) =>
        {
            switch (args.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (DataGridColumn column in args.NewItems)
                        dataGrid.Columns.Add(column);
                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (DataGridColumn column in args.OldItems)
                        dataGrid.Columns.Remove(column);
                    break;
                case NotifyCollectionChangedAction.Reset:
                    dataGrid.Columns.Clear();
                    break;
            }
        };
    }
}

3. ViewModel中处理选中状态和列的同步逻辑

在ViewModel中声明两个集合,分别绑定下拉选项和DataGrid动态列,监听选中状态变化:

public class MainViewModel : INotifyPropertyChanged
{
    // 下拉菜单绑定的选项集合
    public ObservableCollection<ColumnOption> ColumnOptions { get; set; }
    // DataGrid绑定的动态列集合
    public ObservableCollection<DataGridColumn> DynamicColumns { get; set; }
    // DataGrid绑定的数据源,根据自己的业务替换
    public ObservableCollection<YourBusinessModel> GridDataList { get; set; }

    public MainViewModel()
    {
        ColumnOptions = new ObservableCollection<ColumnOption>();
        DynamicColumns = new ObservableCollection<DataGridColumn>();
        GridDataList = new ObservableCollection<YourBusinessModel>();

        // 监听选项集合变更,给新增的选项加属性监听
        ColumnOptions.CollectionChanged += (s, e) =>
        {
            if (e.NewItems != null)
                foreach (ColumnOption item in e.NewItems)
                    item.PropertyChanged += OnOptionSelectedChanged;
            if (e.OldItems != null)
                foreach (ColumnOption item in e.OldItems)
                    item.PropertyChanged -= OnOptionSelectedChanged;
        };

        // 给初始加载的选项加属性监听
        foreach (var option in ColumnOptions)
        {
            option.PropertyChanged += OnOptionSelectedChanged;
        }
    }

    private void OnOptionSelectedChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName != nameof(ColumnOption.IsSelected)) return;
        var option = sender as ColumnOption;
        if (option == null) return;

        if (option.IsSelected)
        {
            // 构造新增列,可根据需求替换为DataGridTemplateColumn等其他列类型
            var newColumn = new DataGridTextColumn
            {
                Header = option.Position,
                Binding = new Binding(option.Position) // 绑定到业务模型的对应字段
            };
            // 避免重复添加
            if (!DynamicColumns.Any(c => c.Header?.ToString() == option.Position))
                DynamicColumns.Add(newColumn);
        }
        else
        {
            // 移除取消选中的列
            var targetColumn = DynamicColumns.FirstOrDefault(c => c.Header?.ToString() == option.Position);
            if (targetColumn != null)
                DynamicColumns.Remove(targetColumn);
        }
    }

    // 实现INotifyPropertyChanged,和ColumnOption类的实现逻辑一致即可
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

4. XAML绑定配置

首先引入附加属性的命名空间,再完成控件绑定,你原有的ComboBox模板无需修改:

<!-- 顶部引用命名空间,替换为你自己的扩展类所在命名空间 -->
<Window xmlns:ext="clr-namespace:YourProjectNamespace.Extensions">

    <!-- 多选下拉菜单 -->
    <ComboBox ItemsSource="{Binding ColumnOptions}" Width="220" Margin="10">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <CheckBox
                    VerticalAlignment="Center"
                    ClickMode="Press"
                    Content="{Binding Position}"
                    IsChecked="{Binding IsSelected}" />
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>

    <!-- DataGrid配置 -->
    <DataGrid 
        ItemsSource="{Binding GridDataList}"
        AutoGenerateColumns="False"
        ext:DataGridBindableColumnsExtension.BindableColumns="{Binding DynamicColumns}"
        Margin="10">
        <!-- 如果有固定列(比如序号、操作列)可以直接写在这里,不会被动态逻辑清除 -->
    </DataGrid>
</Window>

注意事项

  • 如果需要保留固定列,只需修改附加属性的OnBindableColumnsChanged逻辑,不要调用dataGrid.Columns.Clear(),仅同步动态列即可
  • 若需要使用模板列,只需在构造列时替换为DataGridTemplateColumn,从资源中加载对应的CellTemplate赋值即可
  • 如果不想使用附加属性,也可以用Microsoft.Xaml.Behaviors.Wpf包的Behavior实现列同步,逻辑和附加属性完全一致,同样符合MVVM规范

内容的提问来源于stack exchange,提问作者Raya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 10:06:03