尝试将 DataGridView 与 ICustomTypeDescriptor 一起使用

Trying to use DataGridView together with ICustomTypeDescriptor

我正在尝试使用 DataGridView 来显示对象列表。 在我想展示属性的 class 中,我有一些 C# 属性,并且出于某些原因我还想动态创建属性。

这里我有一个示例,它适用于 C# 属性 (FeatureId) 但动态创建的 属性 (Name) returns 是所有第一个实例的值实例。 为什么?

首先是实现 ICustomPropertyDescriptor 接口的 class

public abstract class PropertyPresentationSubBase : ICustomTypeDescriptor
{
public String GetClassName()
{
  return TypeDescriptor.GetClassName(this, true);
}

public AttributeCollection GetAttributes()
{
  return TypeDescriptor.GetAttributes(this, true);
}

public String GetComponentName()
{
  return TypeDescriptor.GetComponentName(this, true);
}

public TypeConverter GetConverter()
{
  return TypeDescriptor.GetConverter(this, true);
}

public EventDescriptor GetDefaultEvent()
{
  return TypeDescriptor.GetDefaultEvent(this, true);
}

public PropertyDescriptor GetDefaultProperty()
{
  return TypeDescriptor.GetDefaultProperty(this, true);
}

public object GetEditor(Type editorBaseType)
{
  return TypeDescriptor.GetEditor(this, editorBaseType, true);
}

public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
  return TypeDescriptor.GetEvents(this, attributes, true);
}

public EventDescriptorCollection GetEvents()
{
  return TypeDescriptor.GetEvents(this, true);
}


public virtual PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
  PropertyDescriptorCollection rtn = TypeDescriptor.GetProperties(this);

  //rtn = FilterReadonly(rtn, attributes);

  return new PropertyDescriptorCollection(rtn.Cast<PropertyDescriptor>().ToArray());


}

public virtual PropertyDescriptorCollection GetProperties()
{

  return TypeDescriptor.GetProperties(this, true);

}


public object GetPropertyOwner(PropertyDescriptor pd)
{
  return this;
}

[Browsable(false)]
public PropertyPresentationSubBase Parent
{
  get
  {
    return m_Parent;
  }
  set
  {
    m_Parent = value;
  }
}

PropertyPresentationSubBase m_Parent = null;

[Browsable(false)]
public Type ValueType
{
  get
  {
    return valueType;
  }
  set
  {
    valueType = value;
  }
}

private Type valueType = null;

[Browsable(false)]
public string Name
{
  get
  {
    return sName;
  }
  set
  {
    sName = value;
  }
}



public abstract object GetValue();

private string sName = string.Empty;

public abstract void Change(object value);


  }
}

我还有一个 class 继承自 PropertyDescriptor

public class MyCustomPropertyDescriptor : PropertyDescriptor
{
PropertyPresentationSubBase m_Property;

public MyCustomPropertyDescriptor(PropertyPresentationSubBase myProperty, Attribute[] attrs, int propertyNo)
  : base(myProperty.Name + propertyNo, attrs)
{
  m_Property = myProperty;
}

#region PropertyDescriptor specific

public override bool CanResetValue(object component)
{
  return false;
}

public override string Name
{
  get
  {
    return "MyName";
  }
}

public override Type ComponentType
{
  get
  {
    return null;
  }
}

public override object GetValue(object component)
{
  return m_Property.GetValue();
}


public override string Description
{
  get
  {
    return "Description";
  }
}



public object Value
{
  get
  {
    return m_Property;
  }
}


public override string Category
{
  get
  {
    return "Category";
  }
}

public override string DisplayName
{
  get
  {
    return m_Property.Name;
  }

}


public override bool IsReadOnly
{
  get
  {
    return false;
  }
}


public override void ResetValue(object component)
{
  //Have to implement
}

public override bool ShouldSerializeValue(object component)
{
  return false;
}




public override void SetValue(object component, object value)
{
  m_Property.Change(value);
}

public override Type PropertyType
{

  get
  {
    if ((m_Property != null) &&  (m_Property.ValueType != null))
    {
      return m_Property.ValueType;
    }
    else
    {
      return System.Type.Missing.GetType();

    }
  }
}

#endregion

}

保存数据的小 class:

public class QuadriFeatureItem
{
public QuadriFeatureItem(int featureId, string name)
{
  m_featureId = featureId;
  m_name = name;

}
public int m_featureId;

public string m_name;
}

我的class发送到网格(包含FeatureId 属性和动态创建的属性)

class FeaturePropertyPresentation : PropertyPresentationSubBase
{

public int FeatureId
{
  get
  {
    return m_feature.m_featureId;

  }
  set { m_feature.m_featureId = value; }
}

public FeaturePropertyPresentation(QuadriFeatureItem item)
{
  m_feature = item;
}

private QuadriFeatureItem m_feature;

public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
  PropertyDescriptorCollection rtn = base.GetProperties(attributes);

  CreateNameAttribute(ref rtn, attributes);

  return rtn;

}

private void CreateNameAttribute(ref PropertyDescriptorCollection pdc, Attribute[] attributes)
{

  NameProperty namePres = null;
  namePres = new NameProperty(m_feature, this);

  pdc.Add(new MyCustomPropertyDescriptor(namePres, attributes, pdc.Count));

}

public override void Change(object value)
{
  throw new NotImplementedException();
}

public override object GetValue()
{
  return this;
}

}

实现名称属性的class:

class NameProperty : PropertyPresentationSubBase
{

public NameProperty(QuadriFeatureItem feature, FeaturePropertyPresentation parent)
  : base()
 {
   m_quadriFeatureItem = feature;
   Parent = parent;
   ValueType = typeof(string);

 }

private QuadriFeatureItem m_quadriFeatureItem;

public override void Change(object value)
{
  m_quadriFeatureItem.m_name = (string)value;
}

public override object GetValue()
{

  return m_quadriFeatureItem.m_name;

}  

}

我的表单代码:

public Form1()
{
  InitializeComponent();

  ShowGrid();
}

private void ShowGrid()
{

  QuadriFeatureItem no1 = new QuadriFeatureItem(1, "Nummer1");
  QuadriFeatureItem no2 = new QuadriFeatureItem(2, "Nummer2");
  QuadriFeatureItem no3 = new QuadriFeatureItem(3, "Nummer3");

  BindingSource source = new BindingSource();

  FeaturePropertyPresentation no1Pres = new FeaturePropertyPresentation(no1);
  FeaturePropertyPresentation no2Pres = new FeaturePropertyPresentation(no2);
  FeaturePropertyPresentation no3Pres = new FeaturePropertyPresentation(no3);

  source.Add(no1Pres);
  source.Add(no2Pres);
  source.Add(no3Pres);

  dataGridView1.DataSource = source;

  Show();      
}

但网格显示所有行的 "Nummer1"。为什么?我在 属性 网格中使用此演示文稿 classes,它工作正常。我还在 属性 网格中使用了这个 MyCustomPropertyDescriptor。

我现在的愿望是能够在数据网格视图中重用此演示文稿classes 和 MyCustomPropertyDescriptor。是否可以在 MyCustomPropertyDescriptor 或 PropertyPresentationSubBase 中进行任何修改?

问题是您的自定义 属性 描述符绑定到具体实例。当您使用 单项数据绑定 (例如 TextBox 到您的对象 属性 或在 PropertyGrid 控件中选择您的对象时,这会起作用)。但是,当您使用需要 列表数据绑定的控件时 (如 DataGridViewListViewListBoxComboBox 列表等。 ) 这种技术不起作用。为了自动填充列,DataGridView 需要一组对所有项目 通用 的属性。为了做到这一点,它尝试了几种方法来获取该信息(一个很好的解释可以在这里找到 DataGridView not showing properites of objects which implement ICustomTypeDescriptor),其中一个是获取列表的第一项并询问属性(因此你的调试经验)。无论如何,为了在列表绑定场景中进行这项工作,您的 属性 描述符需要以不同的方式实现。

注意 PropertyDescriptors GetValue/SetValue 方法的签名。他们都有一个参数object component。这是您需要 return 或设置值的对象实例。您可以认为 属性 描述符与我们通常在编程语言中使用的描述符相反。所以而不是

var val = obj.Property;
obj.Property = val;

我们有

var val = propertyDescriptor.GetValue(obj);
propertyDescriptor.SetValue(obj, val);

换句话说,你不应该 "embed" 你的对象实例在 属性 描述符中,而是使用传递的参数。

这是一个 属性 描述符的通用实现示例:

public class SimplePropertyDescriptor<TComponent, TValue> : PropertyDescriptor
    where TComponent : class
{
    private readonly Func<TComponent, TValue> getValue;
    private readonly Action<TComponent, TValue> setValue;
    private readonly string displayName;
    public SimplePropertyDescriptor(string name, Attribute[] attrs, Func<TComponent, TValue> getValue, Action<TComponent, TValue> setValue = null, string displayName = null)
        : base(name, attrs)
    {
        Debug.Assert(getValue != null);
        this.getValue = getValue;
        this.setValue = setValue;
        this.displayName = displayName;
    }
    public override string DisplayName { get { return displayName ?? base.DisplayName; } }
    public override Type ComponentType { get { return typeof(TComponent); } }
    public override bool IsReadOnly { get { return setValue == null; } }
    public override Type PropertyType { get { return typeof(TValue); } }
    public override bool CanResetValue(object component) { return false; }
    public override bool ShouldSerializeValue(object component) { return false; }
    public override void ResetValue(object component) { }
    public override object GetValue(object component) { return getValue((TComponent)component); }
    public override void SetValue(object component, object value) { setValue((TComponent)component, (TValue)value); }
}

你的东西的使用示例:

public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
    var properties = base.GetProperties(attributes);
    // Custom name property
    properties.Add(new SimplePropertyDescriptor<FeaturePropertyPresentation, string>("FeatureName", attributes,
        getValue: component => component.m_feature.m_name,
        setValue: (component, value) => component.m_feature.m_name = value, // remove this line to make it readonly
        displayName: "Feature Name"
    ));
    return properties;
}

并且,把它们放在一起,一个与你的例子相当的小例子:

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;

namespace Samples
{
    // Generic implemenation of a property descriptor
    public class SimplePropertyDescriptor<TComponent, TValue> : PropertyDescriptor
        where TComponent : class
    {
        private readonly Func<TComponent, TValue> getValue;
        private readonly Action<TComponent, TValue> setValue;
        private readonly string displayName;
        public SimplePropertyDescriptor(string name, Attribute[] attrs, Func<TComponent, TValue> getValue, Action<TComponent, TValue> setValue = null, string displayName = null)
            : base(name, attrs)
        {
            Debug.Assert(getValue != null);
            this.getValue = getValue;
            this.setValue = setValue;
            this.displayName = displayName;
        }
        public override string DisplayName { get { return displayName ?? base.DisplayName; } }
        public override Type ComponentType { get { return typeof(TComponent); } }
        public override bool IsReadOnly { get { return setValue == null; } }
        public override Type PropertyType { get { return typeof(TValue); } }
        public override bool CanResetValue(object component) { return false; }
        public override bool ShouldSerializeValue(object component) { return false; }
        public override void ResetValue(object component) { }
        public override object GetValue(object component) { return getValue((TComponent)component); }
        public override void SetValue(object component, object value) { setValue((TComponent)component, (TValue)value); }
    }
    // Your stuff
    public abstract class PropertyPresentationSubBase : ICustomTypeDescriptor
    {
        public string GetClassName()
        {
            return TypeDescriptor.GetClassName(this, true);
        }

        public AttributeCollection GetAttributes()
        {
            return TypeDescriptor.GetAttributes(this, true);
        }

        public String GetComponentName()
        {
            return TypeDescriptor.GetComponentName(this, true);
        }

        public TypeConverter GetConverter()
        {
            return TypeDescriptor.GetConverter(this, true);
        }

        public EventDescriptor GetDefaultEvent()
        {
            return TypeDescriptor.GetDefaultEvent(this, true);
        }

        public PropertyDescriptor GetDefaultProperty()
        {
            return TypeDescriptor.GetDefaultProperty(this, true);
        }

        public object GetEditor(Type editorBaseType)
        {
            return TypeDescriptor.GetEditor(this, editorBaseType, true);
        }

        public EventDescriptorCollection GetEvents(Attribute[] attributes)
        {
            return TypeDescriptor.GetEvents(this, attributes, true);
        }

        public EventDescriptorCollection GetEvents()
        {
            return TypeDescriptor.GetEvents(this, true);
        }

        public virtual PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            PropertyDescriptorCollection rtn = TypeDescriptor.GetProperties(this);

            //rtn = FilterReadonly(rtn, attributes);

            return new PropertyDescriptorCollection(rtn.Cast<PropertyDescriptor>().ToArray());
        }

        public virtual PropertyDescriptorCollection GetProperties()
        {

            return TypeDescriptor.GetProperties(this, true);

        }


        public object GetPropertyOwner(PropertyDescriptor pd)
        {
            return this;
        }

        [Browsable(false)]
        public PropertyPresentationSubBase Parent
        {
            get
            {
                return m_Parent;
            }
            set
            {
                m_Parent = value;
            }
        }

        PropertyPresentationSubBase m_Parent = null;

        [Browsable(false)]
        public Type ValueType
        {
            get
            {
                return valueType;
            }
            set
            {
                valueType = value;
            }
        }

        private Type valueType = null;

        [Browsable(false)]
        public string Name
        {
            get
            {
                return sName;
            }
            set
            {
                sName = value;
            }
        }



        public abstract object GetValue();

        private string sName = string.Empty;

        public abstract void Change(object value);
    }
    public class QuadriFeatureItem
    {
        public QuadriFeatureItem(int featureId, string name)
        {
            m_featureId = featureId;
            m_name = name;

        }
        public int m_featureId;

        public string m_name;
    }
    class FeaturePropertyPresentation : PropertyPresentationSubBase
    {

        public int FeatureId
        {
            get
            {
                return m_feature.m_featureId;

            }
            set { m_feature.m_featureId = value; }
        }

        public FeaturePropertyPresentation(QuadriFeatureItem item)
        {
            m_feature = item;
        }

        private QuadriFeatureItem m_feature;

        public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            var properties = base.GetProperties(attributes);
            // Custom name property
            properties.Add(new SimplePropertyDescriptor<FeaturePropertyPresentation, string>("FeatureName", attributes,
                getValue: component => component.m_feature.m_name,
                setValue: (component, value) => component.m_feature.m_name = value, // remove this line to make it readonly
                displayName: "Feature Name"
            ));
            return properties;
        }
        public override void Change(object value)
        {
            throw new NotImplementedException();
        }

        public override object GetValue()
        {
            return this;
        }

    }
    // Test
    static class Test
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            var dataSet = Enumerable.Range(1, 10).Select(n => new FeaturePropertyPresentation(new QuadriFeatureItem(n, "Nummer" + n))).ToList();
            var form = new Form();
            var dg = new DataGridView { Dock = DockStyle.Fill, Parent = form };
            dg.DataSource = dataSet;
            Application.Run(form);
        }
    }
}

结果: