C# 绑定到接口并在基本接口中显示属性

C# 绑定到接口并在基本接口中显示属性,c#,datagridview,interface,datasource,C#,Datagridview,Interface,Datasource,(及其答案)解释了为什么不能轻松地将DataGridView绑定到接口类型并获取从基接口继承的属性列 建议的解决方案是实现自定义类型转换器。我的尝试如下。但是,创建绑定到ICamel的DataSource和DataGridView仍然只会产生一列(驼峰)。我不认为.NET正在使用我的转换器来决定它可以看到ICamel的哪些属性。我做错了什么 [TypeConverter(typeof(MyConverter))] public interface IAnimal { string Nam

(及其答案)解释了为什么不能轻松地将DataGridView绑定到接口类型并获取从基接口继承的属性列

建议的解决方案是实现自定义类型转换器。我的尝试如下。但是,创建绑定到ICamel的DataSource和DataGridView仍然只会产生一列(驼峰)。我不认为.NET正在使用我的转换器来决定它可以看到ICamel的哪些属性。我做错了什么

[TypeConverter(typeof(MyConverter))]
public interface IAnimal
{
    string Name { get; set; }
    int Legs { get; set; }
}

[TypeConverter(typeof(MyConverter))]
public interface ICamel : IAnimal
{
    int Humps { get; set; }
}

public class MyConverter : TypeConverter
{
    public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
    {
        if(value is Type && (Type)value == typeof(ICamel))
        {
            List<PropertyDescriptor> propertyDescriptors = new List<PropertyDescriptor>();
            foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(typeof(ICamel)))
            {
                propertyDescriptors.Add(pd);
            }
            foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(typeof(IAnimal)))
            {
                propertyDescriptors.Add(pd);
            }
            return new PropertyDescriptorCollection(propertyDescriptors.ToArray());
        }
        return base.GetProperties(context, value, attributes);
    }

    public override bool GetPropertiesSupported(ITypeDescriptorContext context)
    {
        return true;
    }
}
[TypeConverter(typeof(MyConverter))]
公共接口IAnimal
{
字符串名称{get;set;}
int Legs{get;set;}
}
[TypeConverter(typeof(MyConverter))]
公共接口ICamel:IAnimal
{
int驼峰{get;set;}
}
公共类MyConverter:TypeConverter
{
公共重写属性描述或集合GetProperties(ITypeDescriptorContext上下文、对象值、属性[]属性)
{
如果(值为类型&&(类型)值==typeof(ICamel))
{
List propertyDescriptors=新列表();
foreach(TypeDescriptor.GetProperties中的PropertyDescriptor pd(typeof(ICamel)))
{
propertyDescriptors.Add(pd);
}
foreach(TypeDescriptor.GetProperties中的PropertyDescriptor pd(typeof(IAnimal)))
{
propertyDescriptors.Add(pd);
}
返回新的PropertyDescriptorCollection(propertyDescriptors.ToArray());
}
返回base.GetProperties(上下文、值、属性);
}
公共覆盖布尔GetPropertiesSupported(ITypeDescriptorContext上下文)
{
返回true;
}
}

DataGridView
不使用
类型转换器
属性网格
使用
类型转换器

如果它与列表控件(如
DataGridView
)相关,则另一个答案是错误的

要在列表上提供自定义属性,您需要以下选项之一:

  • 数据源上的ITypedList
  • TypeDescriptionProvider
    在类型上

这两个都是非常重要的。

我的建议是创建一个接口,“重新实现”您想要的属性:

假设您有两个接口:

public interface IHasName1
{
    String Name1 { get; set; }
}

public interface IHasName2 : IHasName1
{
    String Name2 { get; set; }
}
以及一个实现IHasName2的类:

public class HasTwoNames : IHasName2
{
    #region IHasName1 Member
    public string Name1 { get; set; }
    #endregion

    #region IHasName2 Member
    public string Name2 {get; set; }
    #endregion
}
现在,为了解决这个问题,顺便说一句,如果您有一个包含具体类型HasTwoNames对象的列表,并且您将该列表绑定到一个dgv,那么它只显示IHasName2的成员(Name2)

“解决方法”是创建一个新接口“IHasEverything”,该接口继承自IHasName2,因此继承自IHasName1,并重新实现绑定中所需的属性(您可以使用新语句来实现)

public interface IHasEverything : IHasName2
{
    new String Name1 { get; set; }
    new String Name2 { get; set; }
}
现在,您的具体类“HasTwoNames”也需要实现IHasEverything:

public class HasTwoNames : IHasName2, IHasEverything
{
    ...
}
您可以将此列表绑定到datagridview:

    public List<IHasEverything> elements = new List<IHasEverything> {
        new HasTwoNames { Name1 = "Name1", Name2 = "Name2"},
        new HasTwoNames { Name1 = "Name3", Name2 = "Name4"},
    };
公共列表元素=新列表{
新的HasTwoNames{Name1=“Name1”,Name2=“Name2”},
新的HasTwoNames{Name1=“Name3”,Name2=“Name4”},
};
我知道这只是一种变通方法,只有在您可以修改实现类的情况下才有可能,但它是有效的。
(如果从IHasName2中删除属性,代码仍将编译,但您会收到一条警告:IHasEverything不需要新关键字。

我的解决方法发生在dgv的绑定中。 我确实需要基本接口和继承接口保持在相同的结构中,因为我在最终的Concrete类中做了其他事情,而不仅仅是在DataGridView上显示数据。例如:

interface IGenericPerson
{
    int ID { get; set; }
    string Name { get; set; }
}

interface IOperator : IGenericPerson
{
    bool IsAdmin { get; set; }
}
混凝土等级:

class Operator : IOperator
{
    public Operator(){}

    public Operator(int id, string name, bool isAdmin)
    {
        this.ID = id;
        this.Name = name;
        thsi.IsAdmin = isAdmin;
    }

    public int ID { get; set; }
    public string name { get; set; }
    public bool IsAdmin { get; set; }
}
在网关类中:

public IList<IOperator> GetOperators()
{
    IList<IOperator> list = new List<IOperator>();

    list.add(new Operator(112, "Mark Twain", false);
    list.add(new Operator(112, "Charles Manson", false);
    list.add(new Operator(112, "Richard Nixon", true);

    return list;
}
我从IOperator接口获得了一个DataGridView,其中只有bool IsAdmin列,没有ID,也没有基本接口的Name propertys

但如果我这样做:

Gateway gt = new Gateway();

IList<IOperator> list = gt.GetOperators();

IList<Operator> ds = new List<Operator>();

foreach(IOperator op in list)
    ds.add((Operator)op);

dgv.DataSource = ds;
Gateway gt=new Gateway();
IList list=gt.GetOperators();
IList ds=新列表();
foreach(列表中的IOperator op)
ds.add((操作员)op);
dgv.DataSource=ds;
一切都以正确的方式运作


通过这种方式,我不需要更改intarfaces链的结构,这对其他用途很有用,而且只有在显示数据时,我才插入上面的代码片段。

另外两篇文章可能会让您感兴趣:谢谢。不可能将
TypeDescriptionProvider
应用于接口,而是实现
ITypedList
在我的数据库中,数据源在运行时工作得非常好。在设计时查看我的自定义属性会很好,但我认为我可以找到一个解决方法,这就足够了。
Gateway gt = new Gateway();

IList<IOperator> list = gt.GetOperators();

IList<Operator> ds = new List<Operator>();

foreach(IOperator op in list)
    ds.add((Operator)op);

dgv.DataSource = ds;