Привязка к интерфейсу и отображение свойств в базовом интерфейсе - PullRequest
5 голосов
/ 26 июня 2009

Этот вопрос (вместе с ответом) объясняет, почему вы не можете легко связать DataGridView с типом интерфейса и получить столбцы для свойств, унаследованных от базового интерфейса.

Предлагаемое решение заключается в реализации пользовательского TypeConverter. Моя попытка ниже. Однако создание DataSource и DataGridView, связанных с ICamel, по-прежнему приводит только к одному столбцу (Humps). Я не думаю, что мой конвертер используется .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;
    }
}

Ответы [ 3 ]

3 голосов
/ 26 июня 2009

DataGridView не использует TypeConverter; PropertyGrid использует TypeConverter.

Если это относится к элементам управления списком, таким как DataGridView, тогда другой ответ неверен.

Чтобы предоставить пользовательские свойства в списке, вам нужно одно из:

  • ITypedList на источнике данных
  • TypeDescriptionProvider по типу

Оба нетривиальны.

1 голос
/ 02 июля 2011

Мой обходной путь происходит в привязке dgv. Мне нужно, чтобы базовые интерфейсы и наследующие интерфейсы оставались в одной структуре, просто потому, что я делаю другие вещи, расширяя конечный конкретный класс, а не только показываю данные в 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;
}

Теперь, если я попытаюсь связать datagridView следующим образом:

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

Я получаю DataGridView с единственным столбцом Isolmin bool из интерфейса IOperator, а не ID и свойства Name из базового интерфейса.

но если я сделаю это:

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;

Все работает правильно.

Таким образом, мне не нужно изменять структуру цепочки intarfaces, полезную для других целей, и только при отображении данных я просто вставляю фрагмент выше.

0 голосов
/ 26 июня 2009

Мое предложение было бы создать интерфейс, который "переопределяет" нужные вам свойства:

Допустим, у вас есть два интерфейса:

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, он отображает только члена (Name2) IHasName2.

«Обходной путь» - создать новый интерфейс «IHasEverything», который наследуется от IHasName2 и, следовательно, от IHasName1 и переопределяет свойства Propertys, необходимые для привязки (вы можете сделать это с помощью нового оператора

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

Теперь ваш конкретный класс "HasTwoNames" должен также реализовать IHasEverything:

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

Вы можете связать этот список с сеткой данных:

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

Я знаю, что это всего лишь обходной путь, и он возможен только в том случае, если вы можете изменить класс реализации. Но это работает. (Если вы удалите свойство из IHasName2, код все равно будет скомпилирован, но вы получите предупреждение о том, что IHasEverything не нуждается в новом ключевом слове.

...