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

基于WPF Binding&Reflection的动态DataGrid行详情属性编辑问询

实现元数据驱动的DataGrid动态RowDetails编辑

Hey there, let's walk through how to get dynamic RowDetails editing working for your auto-generated DataGrid. Since your rows are all derived from a base class that exposes a list of editable properties (via reflection), we need the RowDetails to adapt to that dynamic list instead of hardcoding controls. Here's how to pull it off:


1. 确认基类与数据模型结构

首先,确保你的基类(比如命名为BaseRecord)能正确返回当前记录需要编辑的属性列表。这里用反射筛选属性,你可以根据业务需求调整过滤逻辑:

public abstract class BaseRecord
{
    // 返回需要显示/编辑的属性集合
    public List<PropertyInfo> EditableProperties => 
        GetType().GetProperties()
            .Where(p => !p.GetGetMethod().IsStatic && p.CanWrite) // 排除静态、只读属性
            .ToList();
}

// 示例子类:实际业务中可以有多个不同的子类
public class CustomerRecord : BaseRecord
{
    public string FullName { get; set; }
    public int Age { get; set; }
    public DateTime RegistrationDate { get; set; }
    public bool IsActive { get; set; }
}

2. 动态生成RowDetails编辑控件

因为属性是动态的,我们有两种常用方案来生成对应的编辑控件,按需选择即可:

方案A:XAML模板驱动(适合轻量场景)

在DataGrid的XAML中定义RowDetailsTemplate,用ItemsControl遍历基类的EditableProperties,通过数据触发器根据属性类型自动匹配编辑控件:

<DataGrid x:Name="DynamicDataGrid" AutoGenerateColumns="True"
          ItemsSource="{Binding RecordList}"
          LoadingRowDetails="DynamicDataGrid_LoadingRowDetails">
    <DataGrid.RowDetailsTemplate>
        <DataTemplate>
            <StackPanel Margin="12" Orientation="Vertical">
                <TextBlock FontWeight="SemiBold" FontSize="14" Text="编辑记录"/>
                <!-- 遍历可编辑属性,动态生成编辑项 -->
                <ItemsControl ItemsSource="{Binding EditableProperties}" Margin="0,8,0,0">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal" Margin="0,4,0,0">
                                <TextBlock Text="{Binding Name}" Width="120" VerticalAlignment="Center"/>
                                <!-- 根据属性类型切换编辑控件 -->
                                <ContentControl>
                                    <ContentControl.Style>
                                        <Style TargetType="ContentControl">
                                            <!-- 字符串类型用TextBox -->
                                            <Style.Triggers>
                                                <DataTrigger Binding="{Binding PropertyType}" Value="{x:Type sys:String}">
                                                    <Setter Property="ContentTemplate">
                                                        <Setter.Value>
                                                            <DataTemplate>
                                                                <TextBox Width="220"
                                                                         Text="{Binding DataContext, RelativeSource={RelativeSource AncestorType=DataGridRow}, Path={Binding Name}, Mode=TwoWay}"/>
                                                            </DataTemplate>
                                                        </Setter.Value>
                                                    </Setter>
                                                </DataTrigger>
                                                <!-- 整数类型用带数字验证的TextBox -->
                                                <DataTrigger Binding="{Binding PropertyType}" Value="{x:Type sys:Int32}">
                                                    <Setter Property="ContentTemplate">
                                                        <Setter.Value>
                                                            <DataTemplate>
                                                                <TextBox Width="220"
                                                                         Text="{Binding DataContext, RelativeSource={RelativeSource AncestorType=DataGridRow}, Path={Binding Name}, Mode=TwoWay}"
                                                                         PreviewTextInput="NumericOnly_PreviewTextInput"/>
                                                            </DataTemplate>
                                                        </Setter.Value>
                                                    </Setter>
                                                </DataTrigger>
                                                <!-- 日期类型用DatePicker -->
                                                <DataTrigger Binding="{Binding PropertyType}" Value="{x:Type sys:DateTime}">
                                                    <Setter Property="ContentTemplate">
                                                        <Setter.Value>
                                                            <DataTemplate>
                                                                <DatePicker Width="220"
                                                                            SelectedDate="{Binding DataContext, RelativeSource={RelativeSource AncestorType=DataGridRow}, Path={Binding Name}, Mode=TwoWay}"/>
                                                            </DataTemplate>
                                                        </Setter.Value>
                                                    </Setter>
                                                </DataTrigger>
                                                <!-- 布尔类型用CheckBox -->
                                                <DataTrigger Binding="{Binding PropertyType}" Value="{x:Type sys:Boolean}">
                                                    <Setter Property="Content">
                                                        <Setter.Value>
                                                            <CheckBox IsChecked="{Binding DataContext, RelativeSource={RelativeSource AncestorType=DataGridRow}, Path={Binding Name}, Mode=TwoWay}"/>
                                                        </Setter.Value>
                                                    </Setter>
                                                </DataTrigger>
                                            </Style.Triggers>
                                        </Style>
                                    </ContentControl.Style>
                                </ContentControl>
                            </StackPanel>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
                <Button Content="保存修改" Margin="0,12,0,0" Width="100"
                        Click="SaveRowChanges_Click"/>
            </StackPanel>
        </DataTemplate>
    </DataGrid.RowDetailsTemplate>
</DataGrid>

注意:需要在XAML头部引入系统类型命名空间:xmlns:sys="clr-namespace:System;assembly=mscorlib"

方案B:后台代码生成(适合复杂场景)

如果需要更精细的控制(比如根据自定义Attribute设置验证规则、控件样式),可以在RowDetails加载时动态生成控件:

private void DynamicDataGrid_LoadingRowDetails(object sender, DataGridRowDetailsEventArgs e)
{
    var currentRecord = e.Row.DataContext as BaseRecord;
    if (currentRecord == null) return;

    // 获取RowDetails里的容器控件
    var container = e.DetailsElement.FindName("EditContainer") as StackPanel;
    if (container == null) return;

    container.Children.Clear();

    // 遍历属性生成编辑控件
    foreach (var prop in currentRecord.EditableProperties)
    {
        // 添加属性标签
        var label = new TextBlock 
        { 
            Text = prop.Name, 
            Width = 120, 
            Margin = new Thickness(0,4,0,0),
            VerticalAlignment = VerticalAlignment.Center
        };
        container.Children.Add(label);

        // 根据属性类型创建对应编辑控件
        FrameworkElement editControl = null;
        var binding = new Binding(prop.Name) { Mode = BindingMode.TwoWay, Source = currentRecord };

        switch (Type.GetTypeCode(prop.PropertyType))
        {
            case TypeCode.String:
                editControl = new TextBox { Width = 220 };
                BindingOperations.SetBinding(editControl, TextBox.TextProperty, binding);
                break;
            case TypeCode.Int32:
                editControl = new TextBox { Width = 220 };
                BindingOperations.SetBinding(editControl, TextBox.TextProperty, binding);
                ((TextBox)editControl).PreviewTextInput += NumericOnly_PreviewTextInput;
                break;
            case TypeCode.DateTime:
                editControl = new DatePicker { Width = 220 };
                BindingOperations.SetBinding(editControl, DatePicker.SelectedDateProperty, binding);
                break;
            case TypeCode.Boolean:
                editControl = new CheckBox { VerticalAlignment = VerticalAlignment.Center };
                BindingOperations.SetBinding(editControl, CheckBox.IsCheckedProperty, binding);
                break;
            // 可扩展其他类型,比如枚举用ComboBox
        }

        if (editControl != null)
        {
            container.Children.Add(editControl);
        }
    }

    // 添加保存按钮
    var saveBtn = new Button 
    { 
        Content = "保存修改", 
        Margin = new Thickness(0,12,0,0), 
        Width = 100 
    };
    saveBtn.Click += (s, args) => SaveRecordChanges(currentRecord);
    container.Children.Add(saveBtn);
}

private void SaveRecordChanges(BaseRecord record)
{
    // 这里写保存逻辑:比如提交到数据库、更新数据源等
    // 因为用了双向绑定,record的属性已经是最新值了
    MessageBox.Show($"已保存{record.GetType().Name}的修改");
}

private void NumericOnly_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
    e.Handled = !char.IsDigit(e.Text, 0);
}

对应的XAML需要给容器命名:

<DataGrid.RowDetailsTemplate>
    <DataTemplate>
        <StackPanel x:Name="EditContainer" Margin="12" Orientation="Vertical">
            <TextBlock FontWeight="SemiBold" FontSize="14" Text="编辑记录"/>
        </StackPanel>
    </DataTemplate>
</DataGrid.RowDetailsTemplate>

3. 关键注意事项

  • 属性筛选逻辑:基类的EditableProperties一定要做好过滤,排除不需要编辑的属性(比如主键、计算属性),避免生成多余控件。
  • 绑定路径正确性:不管用XAML还是后台绑定,都要确保绑定路径指向属性的Name,这样才能正确关联到当前记录的属性值。
  • 类型覆盖:根据你的业务数据类型扩展控件匹配逻辑,比如枚举类型可以绑定到ComboBox,浮点数可以用带小数点验证的TextBox。
  • 验证与错误提示:如果需要输入验证,可以在绑定中添加ValidationRules,或者给控件设置ToolTip显示错误信息。

内容的提问来源于stack exchange,提问作者Joe M. Doe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:00:29