C#grid DataSource多态

Rob*_*ert 4 c# datagridview winforms

我有一个网格,我正在设置DataSource一个List<IListItem>.我想要的是让列表绑定到底层类型,并显示这些属性,而不是在中定义的属性IListItem.所以:

public interface IListItem
{
    string Id;
    string Name;
}

public class User : IListItem
{
    string Id { get; set; };
    string Name { get; set; };
    string UserSpecificField { get; set; };
}

public class Location : IListItem
{
    string Id { get; set; };
    string Name { get; set; };
    string LocationSpecificField { get; set; };
}
Run Code Online (Sandbox Code Playgroud)

如何绑定到网格,以便如果我List<IListItem>包含用户,我将看到特定于用户的字段?编辑:请注意,我想绑定到Datagrid的任何给定列表将由单个底层类型组成.

Mar*_*ell 6

数据绑定到列表遵循以下策略:

  1. 数据源是否实现IListSource?如果是这样,转到2的结果GetList()
  2. 数据源是否实现IList?如果没有,抛出错误; 清单预计
  3. 数据源是否实现ITypedList?如果是这样,请将其用于元数据(退出)
  4. 数据源是否有非对象索引器public Foo this[int index](对某些人来说Foo)?如果是这样,typeof(Foo)用于元数据
  5. 列表中有什么东西吗?如果是这样,请使用第一个项目(list[0])作为元数据
  6. 没有元数据可用

List<IListItem>落在上面的"4",因为它有一个类型的类型索引器IListItem- 因此它将通过获取元数据TypeDescriptor.GetProperties(typeof(IListItem)).

所以现在,您有三种选择:

  • 写一个TypeDescriptionProvider返回属性的IListItem- 我不确定这是否可行,因为你不可能知道具体的类型是什么IListItem
  • 使用正确键入的列表(List<User>等) - 只是作为IList一个非对象索引器的简单方法
  • 写一个ITypedList包装(很多工作)
  • 使用类似的东西ArrayList(即没有公共非对象索引器) - 非常hacky!

我的偏好是使用正确的类型List<>......这是一种AutoCast方法,可以为您完成此操作而无需知道类型(使用样本);

请注意,这仅适用于同类数据(即所有对象都相同),并且它需要列表中至少有一个对象来推断类型...

// infers the correct list type from the contents
static IList AutoCast(this IList list) {
    if (list == null) throw new ArgumentNullException("list");
    if (list.Count == 0) throw new InvalidOperationException(
          "Cannot AutoCast an empty list");
    Type type = list[0].GetType();
    IList result = (IList) Activator.CreateInstance(typeof(List<>)
          .MakeGenericType(type), list.Count);
    foreach (object obj in list) result.Add(obj);
    return result;
}
// usage
[STAThread]
static void Main() {
    Application.EnableVisualStyles();
    List<IListItem> data = new List<IListItem> {
        new User { Id = "1", Name = "abc", UserSpecificField = "def"},
        new User { Id = "2", Name = "ghi", UserSpecificField = "jkl"},
    };
    ShowData(data, "Before change - no UserSpecifiedField");
    ShowData(data.AutoCast(), "After change - has UserSpecifiedField");
}
static void ShowData(object dataSource, string caption) {
    Application.Run(new Form {
        Text = caption,
        Controls = {
            new DataGridView {
                Dock = DockStyle.Fill,
                DataSource = dataSource,
                AllowUserToAddRows = false,
                AllowUserToDeleteRows = false
            }
        }
    });
}
Run Code Online (Sandbox Code Playgroud)