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

WPF DataGrid动态列与动态绑定单元格模板的优化方案问询

最优实现方案

方案一:自定义DynamicPropertyColumn(推荐)

直接继承DataGridBoundColumn,根据PropertyWrapper的元数据动态生成单元格模板,同时内置属性存在性判断逻辑,完全符合MVVM规范,无需拼接XAML字符串。

1. 核心代码实现

public class DynamicPropertyColumn : DataGridBoundColumn
{
    public PropertyWrapper PropertyWrapper { get; set; }

    protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
    {
        var elementWrapper = dataItem as ElementWrapper;
        if (elementWrapper == null || !elementWrapper.HasProperty(PropertyWrapper.PropertyName))
        {
            // 无对应属性时显示#N/A
            return new TextBlock
            {
                Text = "#N/A",
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Center
            };
        }

        // 布尔类型属性渲染CheckBox
        if (PropertyWrapper.PropertyType == typeof(bool))
        {
            var checkBox = new CheckBox
            {
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Center
            };
            // 双向绑定到ElementWrapper的属性值
            var binding = new Binding($"Properties[{PropertyWrapper.PropertyName}].Value")
            {
                Mode = BindingMode.TwoWay,
                Source = elementWrapper
            };
            checkBox.SetBinding(CheckBox.IsCheckedProperty, binding);

            // 如需业务逻辑,绑定Command替代事件(MVVM方式)
            // checkBox.Command = PropertyWrapper.ToggleValueCommand;
            return checkBox;
        }

        // 其他类型默认渲染文本
        var textBlock = new TextBlock();
        textBlock.SetBinding(TextBlock.TextProperty, 
            new Binding($"Properties[{PropertyWrapper.PropertyName}].Value") { Source = elementWrapper });
        return textBlock;
    }

    protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
    {
        // 编辑模式复用生成逻辑,按需自定义
        return GenerateElement(cell, dataItem);
    }
}

2. 动态创建列的逻辑

在View的代码后台(属于UI层逻辑,不违反MVVM)添加列:

foreach (var propWrapper in ViewModel.PropertyWrappers)
{
    DataGrid.Columns.Add(new DynamicPropertyColumn
    {
        Header = propWrapper.DisplayName,
        PropertyWrapper = propWrapper
    });
}

方案二:DataTemplateSelector结合预定义模板

如果不想自定义列,可提前在XAML中定义模板,通过选择器根据数据上下文动态切换,同样满足需求。

1. 定义模板选择器

public class PropertyCellTemplateSelector : DataTemplateSelector
{
    public DataTemplate BooleanTemplate { get; set; }
    public DataTemplate MissingPropertyTemplate { get; set; }
    public DataTemplate DefaultTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        var cell = container as DataGridCell;
        var propWrapper = cell?.Column.Tag as PropertyWrapper;
        var elementWrapper = item as ElementWrapper;

        if (elementWrapper == null || !elementWrapper.HasProperty(propWrapper.PropertyName))
        {
            return MissingPropertyTemplate;
        }

        return propWrapper.PropertyType == typeof(bool) ? BooleanTemplate : DefaultTemplate;
    }
}

2. XAML中配置模板与选择器

<Window.Resources>
    <DataTemplate x:Key="BooleanCellTemplate">
        <CheckBox HorizontalAlignment="Center" VerticalAlignment="Center"
                  IsChecked="{Binding Properties[(Tag as PropertyWrapper).PropertyName].Value, Mode=TwoWay}"/>
    </DataTemplate>
    <DataTemplate x:Key="MissingPropertyTemplate">
        <TextBlock Text="#N/A" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </DataTemplate>
    <DataTemplate x:Key="DefaultCellTemplate">
        <TextBlock Text="{Binding Properties[(Tag as PropertyWrapper).PropertyName].Value}"/>
    </DataTemplate>

    <local:PropertyCellTemplateSelector x:Key="PropertySelector"
                                        BooleanTemplate="{StaticResource BooleanCellTemplate}"
                                        MissingPropertyTemplate="{StaticResource MissingPropertyTemplate}"
                                        DefaultTemplate="{StaticResource DefaultCellTemplate}"/>
</Window.Resources>

<DataGrid x:Name="MyDataGrid" ItemsSource="{Binding ElementWrappers}"/>

3. 后台创建列

foreach (var propWrapper in ViewModel.PropertyWrappers)
{
    MyDataGrid.Columns.Add(new DataGridTemplateColumn
    {
        Header = propWrapper.DisplayName,
        CellTemplateSelector = FindResource("PropertySelector") as PropertyCellTemplateSelector,
        Tag = propWrapper
    });
}

关键注意事项

  • MVVM合规性:两种方案均将UI逻辑限制在View层,ViewModel仅负责提供数据与业务逻辑,完全符合MVVM原则。
  • CheckBox事件处理:放弃直接使用Checked/Unchecked事件,改用双向绑定到PropertyWrapper.Value;如需业务逻辑,可在PropertyWrapper的Value属性变更时触发,或绑定ICommand到CheckBox的Command属性。
  • 性能优化:自定义列方案的性能优于模板选择器,尤其在属性数量较多时,无需频繁切换模板实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 14:12:40