views:

331

answers:

2

This question (along with its answer) explains why you can't easily bind a DataGridView to an interface type and get columns for properties inherited from a base interface.

The suggested solution is to implement a custom TypeConverter. My attempt is below. However, creating a DataSource and DataGridView bound to ICamel still only results in one column (Humps). I don't think that my converter is being used by .NET to decide which properties it can see for ICamel. What am I doing wrong?

[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;
    }
}
+2  A: 

DataGridView does not use TypeConverter; PropertyGrid uses TypeConverter.

If it relates to list-controls like DataGridView, then the other answer is wrong.

To provide custom properties on a list, you need one of:

  • ITypedList on the data-source
  • TypeDescriptionProvider on the type

Both are non-trivial.

Marc Gravell
Thank you. It's not possible to apply `TypeDescriptionProvider` to an interface, but implementing `ITypedList` in my data source works perfectly at run-time. Seeing my custom properties at design-time would be nice, but I think I can find a workaround that will be sufficient.
spatulon
A: 

My Suggestion would be to create a Interface that "reimplements" the propertys you want:

Let's say you have two interfaces:

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

public interface IHasName2 : IHasName1
{
    String Name2 { get; set; }
}

And a class that implements IHasName2:

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

    #region IHasName2 Member
    public string Name2 {get; set; }
    #endregion
}

Now, thx for figuring that out btw., if you have a List with objects of concrete type HasTwoNames and you bind that list to a dgv, it only displays the member (Name2) of IHasName2.

A "workaround" is to create a new interface "IHasEverything" that inherits from IHasName2 and therefore from IHasName1 and reimplements the Propertys you need in your binding (you can do that with the new statement

public interface IHasEverything : IHasName2
{
    new String Name1 { get; set; }
    new String Name2 { get; set; }
}

Now your concrete class "HasTwoNames" needs to implement IHasEverything, too:

public class HasTwoNames : IHasName2, IHasEverything
{
    ...
}

You can bind this List to a datagridview:

    public List<IHasEverything> elements = new List<IHasEverything> {
        new HasTwoNames { Name1 = "Name1", Name2 = "Name2"},
        new HasTwoNames { Name1 = "Name3", Name2 = "Name4"},
    };

I know that this is just a workaround and only possible if you can modify the implementing class. But it works. (If you remove a property from IHasName2, the code will still compile but you get a warning that IHasEverything does not need the new keyword.

SchlaWiener