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

WPF DataGrid通用化绑定多类型数据源及自定义列头实现咨询

WPF DataGrid通用化绑定多类型数据源及自定义列头实现咨询

嘿,你的需求完全可以搞定!我分两部分给你详细说清楚怎么实现:

一、实现通用构造函数,告别重复代码

你现在写多个构造函数的方式太繁琐了,咱们可以用泛型协变的思路,只写一个构造函数就能兼容所有你的实体类(包括以后新增的ClassC、ClassD这类)。

首先把原来的两个构造函数删掉,换成下面的代码:

private IEnumerable<object> _dataSource;

public ListaDanych(IEnumerable<object> data)
{
    InitializeComponent();
    _dataSource = data;
    // 直接绑定数据源到DataGrid
    DGDane.ItemsSource = _dataSource;
    // 这里可以调用后续设置自定义列头的方法
}

调用的时候完全不用改你原来的代码,直接传List或者List就行:

// ClassA的调用代码完全不变
ListaDanych FLista = new ListaDanych(a);
FLista.Show();

// ClassB的调用也一样
ListaDanych FLista = new ListaDanych(b);
FLista.Show();

为啥能直接传?因为IEnumerable<T>是协变接口,当T是引用类型(你的ClassA、ClassB都是类)时,IEnumerable<ClassA>可以自动转换成IEnumerable<object>,完美适配咱们的通用构造函数。

二、根据类类型自定义DataGrid列头

这里给你两种常用的实现方式,你可以根据自己的场景选:

方式1:用特性标记(最优雅)

给实体类的属性加上DisplayNameAttribute,然后在DataGrid的自动生成列事件里读取这个特性作为列头。

首先修改你的实体类:

using System.ComponentModel; // 别忘了加这个命名空间

public class ClassA
{
    [DisplayName("A类编号")]
    public decimal ID_A { get; set; } = 0;
    [DisplayName("通用属性1")]
    public string MyProperty1 { get; set; } = "";
    [DisplayName("通用属性2")]
    public string MyProperty2 { get; set; } = "";
}

public class ClassB
{
    [DisplayName("B类编号")]
    public decimal ID_B { get; set; } = 0;
    [DisplayName("通用属性1")]
    public string MyProperty1 { get; set; } = "";
    [DisplayName("通用属性2")]
    public string MyProperty2 { get; set; } = "";
    [DisplayName("B类专属属性")]
    public string MyProperty3 { get; set; } = "";
}

然后在ListaDanych窗口的XAML里,给DataGrid添加AutoGeneratingColumn事件:

<DataGrid x:Name="DGDane" AutoGeneratingColumn="DGDane_AutoGeneratingColumn" />

最后在后台代码实现这个事件:

private void DGDane_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
    // 读取当前属性的DisplayName特性
    var displayNameAttr = e.PropertyDescriptor.Attributes
        .OfType<DisplayNameAttribute>()
        .FirstOrDefault();
    
    if (displayNameAttr != null)
    {
        // 把列头换成特性里的自定义文本
        e.Column.Header = displayNameAttr.DisplayName;
    }
    // 可选:如果没有加特性,自动把驼峰命名转成空格分隔的名称(比如MyProperty1变成"Property 1")
    // else
    // {
    //     e.Column.Header = System.Text.RegularExpressions.Regex.Replace(e.PropertyName, @"(\B[A-Z])", " $1");
    // }
}

这种方式的好处是,列头配置和实体类绑定在一起,以后新增属性或者类,直接加特性就行,非常直观。

方式2:用字典映射(适合复杂场景)

如果你的列头需要根据类类型做不同的配置(比如ClassA的MyProperty1叫"A属性1",ClassB的叫"B属性1"),可以维护一个字典来做映射:

首先在ListaDanych窗口里定义映射字典:

// 键是(实体类型+属性名),值是自定义列头文本
private readonly Dictionary<(Type ItemType, string PropertyName), string> _headerMappings = new()
{
    { (typeof(ClassA), nameof(ClassA.ID_A)), "A类编号" },
    { (typeof(ClassA), nameof(ClassA.MyProperty1)), "A属性1" },
    { (typeof(ClassA), nameof(ClassA.MyProperty2)), "A属性2" },
    { (typeof(ClassB), nameof(ClassB.ID_B)), "B类编号" },
    { (typeof(ClassB), nameof(ClassB.MyProperty1)), "B属性1" },
    { (typeof(ClassB), nameof(ClassB.MyProperty2)), "B属性2" },
    { (typeof(ClassB), nameof(ClassB.MyProperty3)), "B专属属性3" },
};

然后还是用AutoGeneratingColumn事件来设置列头:

private void DGDane_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
    var dataGrid = sender as DataGrid;
    if (dataGrid?.ItemsSource == null) return;
    
    // 获取当前数据源的实体类型
    var itemType = dataGrid.ItemsSource.GetType().GetGenericArguments().FirstOrDefault();
    if (itemType == null) return;
    
    // 查找对应的自定义列头
    var mappingKey = (itemType, e.PropertyName);
    if (_headerMappings.TryGetValue(mappingKey, out var customHeader))
    {
        e.Column.Header = customHeader;
    }
}

这种方式灵活性更高,适合需要对不同类型做差异化列头配置的场景。

备注:内容来源于stack exchange,提问作者PawelZ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 15:14:29