Binding for an interface and displaying properties in the base interface

This question (along with his answer) explains why you cannot easily bind a DataGridView to an interface type and get columns for properties inherited from the base interface.

The proposed solution is to implement a custom TypeConverter. My attempt below. However, creating a DataSource and a DataGridView bound to an ICamel still results in only one column (Humps). I do not think that my converter is used by .NET to determine the properties that 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; } } 
+4
source share
3 answers

DataGridView does not use TypeConverter ; PropertyGrid uses TypeConverter .

If this applies to list controls, such as a DataGridView , then another answer is incorrect.

To provide custom properties in a list, you need to:

  • ITypedList in the data source
  • TypeDescriptionProvider by type

Both are nontrivial.

+2
source

My workaround is when binding dgv. I need the base interfaces and the inheriting interfaces to remain in the same structure, simply because I do other things with the width of the final concerete class, not only display the data in the DataGridView. So for example:

 interface IGenericPerson { int ID { get; set; } string Name { get; set; } } interface IOperator : IGenericPerson { bool IsAdmin { get; set; } } 

specific class:

 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; } } 

and in the gateway class:

 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; } 

Now, if I try to bind a datagridView as follows:

 Gateway gt = new Gateway(); dgv.DataSource = gt.GetOperators(); 

I get a DataGridView with only a single IsAdmin bool column from the IOperator interface, not with an identifier, nor with Name attributes from its base interface.

but if I do this:

 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; 

Everything is working correctly.

Thus, I do not need to change the intarfaces chain structure, useful for other purposes, and only when displaying the data I just insert the fragment above.

+1
source

My suggestion was to create an interface that "reinstalls" the properties you need:

Let's say you have two interfaces:

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

And the 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, if you indicated that outside btw., If you have a List with objects of a specific type HasTwoNames and you bind this list to dgv, it displays only the (Name2) IHasName2 element.

The workaround is to create a new IHasEverything interface that inherits from IHasName2 and therefore from IHasName1 and reuses the properties you need in your binding (you can do this with the new operator

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

Now your concrete HasTwoNames class also needs to implement IHasEverything:

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

You can bind this list to the datagridview view:

  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 change the implementation class. But it does work. (If you remove the property from IHasName2, the code will still compile, but you will receive a warning that IHasEverything does not require a new keyword.

0
source

Source: https://habr.com/ru/post/1286808/


All Articles