定制,复杂,动态反射解决方案 - C#

jth*_*h41 9 c# reflection propertygrid winforms

我有很多我正在使用的自定义类,我将解释并发布示例.在解释他们所做的一切之后,我将尝试清楚地描述我的错误发生的条件.

首先,我使用PropertyGrid来显示几种不同类型对象的属性.因为PropertyGrid的默认绑定不是我想要的描述,我创建了一些自定义类,我将其称为"显示"类.这些Display类是通过传入一个对象然后创建属性来构造的,这些属性返回格式良好的字符串以及传入的真实对象的公共属性(在某些情况下是方法)的描述.

我将用一些简短的示例代码演示这个:

以下是我想在PropertyGrid中显示的对象示例:

public class Joint
{
   public Joint(...)
   {...}

   //properties
   public string Name { get; set;}
   public CustomObject CC { get; set;}
   public List<CustomObject> Custom List { get; set;}
}
Run Code Online (Sandbox Code Playgroud)

字符串属性"Name"在PropertyGrid中显示正常但是CustomObject和List没有以对用户友好的方式显示.

所以我尝试通过编写这个类来创建一个解决方案:

public class DisplayJoint
{       

   private Joint _jnt;

   public DisplayJoint(Joint jnt)
   {
      _jnt = jnt;
   }

   //properties
   public string Name {  get { return _jnt.Name; } }

   [TypeConverterAttribute(typeof(ExpandableObjectConverter))]
   public DisplayCustomObject CC {  get { return new DisplayCustomObject(_jnt.CC); } }

   [TypeConverterAttribute(typeof(ExpandableObjectConverter))]
   public List<CustomObject> CustomList { get; set;}
}
Run Code Online (Sandbox Code Playgroud)

正如您在上面的代码中看到的,我为Joint类和CustomObject类创建了特殊的DisplayClasses.在我的项目中,我有许多不同类型的对象需要相同类型的重叠显示类属性.

在上面你可以看到我在最后两个属性上面添加的行

[TypeConverterAttribute(typeof运算(ExpandableObjectConverter))]

这行解决了我在propertiesGrid中显示CustomObject的问题(几乎......后面会详细介绍).但是,对于我的自定义列表属性,它的工作方式不同.在自定义列表中,它展开以仅显示计数和容量(列表的实际属性)这是有道理的,为什么这是,但它不是我想要的.我想在列表中看到实际包含的对象.

在此输入图像描述

所以这是我复杂的解决方案,最初来自这个问题:

我有两个类,我用它以属性的形式动态地将对象添加到propertyGrid绑定列表.第一个(CustomClass)可以在这里下载.它用于动态创建属性.我使用的第二个类(DisplayIEnumerable)是从第一个派生的,可以在这里找到.

DisplayIEnumerable类循环遍历列表对象,并使用每个对象中包含的信息向其自身添加属性.传入DisplayClass以准确定义如何在Grid中表示这些对象属性.

到目前为止,一切都很好!如图所示(图片不是使用提供的类创建的,字符串在我使用的类中的格式不同,删除格式代码以帮助您专注于相关代码:

在此输入图像描述

现在经过那么长的介绍,真正的问题.使用上面的技术,我想编写一个可以动态处理我没有为其编写唯一显示类的CustomObjects的类.我打算为那些使用应用程序进行测试的人留下这些代码,以便他们可以更有效地测试,而无需为我公司的每个CustomObjects都有一个完整的Display Class.(有数百个)相反,通过将propertyGrid与下面的类绑定,我希望拥有列表和CustomObjects的所有属性都有相应的DisplayClasses绑定在它们的位置.

这是我已经尝试过并且有错误的类.我还没有尝试用我的DisplayIEnumerable类实现Lists的替换,我希望首先使用基本功能:

using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Reflection;
using System.Collections;
using System.Windows.Forms;

   internal class DisplayObject : CustomClass<T>
   {
      #region Variables
      protected T _obj;
      #endregion

      #region Constructor
      public DisplayObject(T obj)
      {
         if (obj != null)
         {
            try
            {
               Type currentType = typeof(T);
               foreach (PropertyInfo propertyInfo in currentType.GetProperties())
               {
                  Attribute[] attributes = new Attribute[1];
                  if (propertyInfo.GetType() is IEnumerable)
                     attributes[0] = new TypeConverterAttribute(typeof(ExpandableObjectConverter));
                  else
                     attributes[0] = null;
                  this.Add(new CustomProperty(propertyInfo.Name, propertyInfo, propertyInfo.GetType(), false, true, attributes));
               }
            }
            catch
            {
               MessageBox.Show("Failure!");
            }
         }
      }
      #endregion

      #region Properties
      [Browsable(false)]
      public object Item
      {
         get { return _obj; }
         set { _obj = value; }
      }
      #endregion
   }
Run Code Online (Sandbox Code Playgroud)

运行时,PropertyGrid应显示为: 之前

但是,一旦您单击展开箭头,没有任何反应,箭头消失: 后

上面的类有什么问题,我的DisplayIEnumerable类没有错,导致这种行为差异?

我正在使用DisplayObject类(在DisplayClass中):

  [TypeConverterAttribute(typeof(ExpandableObjectConverter))]
  public DisplayObject EndJoint { get { if (_member.bcEnd != null) { return new DisplayObject(_member.EndJoint); } else return null; } }
Run Code Online (Sandbox Code Playgroud)

提前致谢!如果有人通过这个问题,我会非常感动.

Sim*_*ier 5

您不必创建特殊类来使用属性网格.只需使用适当的属性修饰属性即可.这是一个例子:

两个自定义类:

public class MyObjType1
{
    public int Id { get; set; }
    public string Name { get; set; }

    public override string ToString()
    {
        return Name;
    }
}

public class MyObjType2
{
    public string Reference { get; set; }

    public override string ToString()
    {
        return Reference;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,ToString覆盖是,如果没有为给定类型定义TypeConverter,则默认情况下属性网格使用该属性.

一个"holder"类,包含一组自定义对象:

public class MyHolder
{
    public MyHolder()
    {
        Objects = new List<object>();
    }

    public string Name { get; set; }

    [TypeConverter(typeof(MyCollectionConverter))]
    public List<object> Objects { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)

请注意TypeConverter直接应用于Objects属性的自定义.这是来源:

public class MyCollectionConverter : ExpandableObjectConverter
{
    public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
    {
        IEnumerable enumerable = value as IEnumerable;
        if (enumerable == null)
            return base.GetProperties(context, value, attributes);

        int i = 0;
        List<PropertyDescriptor> list = new List<PropertyDescriptor>();
        foreach (object obj in enumerable)
        {
            MyItemPropertyDescriptor index = new MyItemPropertyDescriptor(i.ToString(), obj);
            list.Add(index);
            i++;
        }
        return new PropertyDescriptorCollection(list.ToArray());
    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType != typeof(string))
            return base.ConvertTo(context, culture, value, destinationType);

        IEnumerable enumerable = value as IEnumerable;
        if (enumerable == null)
            return base.ConvertTo(context, culture, value, destinationType);

        StringBuilder sb = new StringBuilder();
        foreach (object obj in enumerable)
        {
            if (sb.Length > 0)
            {
                sb.Append(',');
            }
            sb.AppendFormat("{0}", obj);
        }
        return sb.ToString();
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我们覆盖ConvertTo并为其指定一个特殊字符串,该字符串在列表中显示以逗号分隔的对象列表.该GetProperties也被覆盖的,并使用一个特殊的PropertyDescriptor; 它ExpandableObjectConverter为子对象添加了一个属性,因此它们也可以扩展:

public class MyItemPropertyDescriptor : PropertyDescriptor
{
    private object _value;

    public MyItemPropertyDescriptor(string name, object value)
        : base(name, new[] { new TypeConverterAttribute(typeof(ExpandableObjectConverter)) })
    {
        _value = value;
    }

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

    public override object GetValue(object component)
    {
        return _value;
    }

    public override Type PropertyType
    {
        get { return _value == null ? typeof(object) : _value.GetType(); }
    }

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

    public override Type ComponentType
    {
        get { return typeof(object); }
    }

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

    public override void ResetValue(object component)
    {
    }

    public override void SetValue(object component, object value)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,这是一些示例代码:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        MyHolder holder = new MyHolder();
        for (int i = 0; i < 3; i++)
        {
            holder.Objects.Add(new MyObjType1 { Id = i, Name = i + "Name" });
        }
        for (int i = 0; i < 3; i++)
        {
            holder.Objects.Add(new MyObjType2 { Reference = "Ref" + i });
        }
        propertyGrid1.SelectedObject = holder;
    }
}
Run Code Online (Sandbox Code Playgroud)

结果如下:

在此输入图像描述