【问题标题】:Custom, Complicated, Dynamic Reflection Solution - C#自定义、复杂、动态反射解决方案 - C#
【发布时间】:2012-08-03 23:40:49
【问题描述】:

我有许多正在使用的自定义类,我将对其进行解释和发布示例。在解释了他们都做了什么之后,我将尝试清楚地描述我的错误发生的条件。

首先,我使用 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;}
}

字符串属性“名称”在 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;}
}

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

您可以在上面看到我在最后两个属性上方添加的 Lines

[TypeConverterAttribute(typeof(ExpandableObjectConverter))]

这一行解决了我在 propertyGrid 中显示 CustomObject 的问题(几乎……稍后会详细介绍)。但是,对于我的自定义列表属性,它的工作方式不同。在自定义列表上,它扩展为仅显示计数和容量(列表的实际属性)这是有道理的,但这不是我想要的。我想查看列表中实际包含的对象。

所以这是我的复杂解决方案,最初来自this question

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

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

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

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

这是我已经尝试过但有错误的课程。我还没有尝试用我的 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
   }

运行时,PropertyGrid 会按原样显示:

但是,一旦您单击展开箭头,什么都不会发生,并且箭头会消失:

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

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

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

提前致谢!如果有人通过这个问题,我会印象深刻。

【问题讨论】:

  • 为什么不使用标准集合 UITypeEditor(标准的两列表单)?
  • 因为我将无法递归地将此可扩展属性添加到属性中包含的属性中。我希望用户能够深入了解他们想要完全查看不同对象之间的关系
  • 标准集合表单包含一个属性网格,因此它也是递归的。
  • 我能否将该数据网格动态绑定到 Display 类而不是基础对象本身?
  • 都是标准的属性网格,所以支持自定义的TypeDescriptors

标签: c# winforms reflection propertygrid


【解决方案1】:

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

两个自定义类:

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

请注意 ToString 被覆盖,如果没有为给定类型定义 TypeConverter,这就是属性网格默认使用的内容。

一个拥有自定义对象集合的“持有者”类:

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

    public string Name { get; set; }

    [TypeConverter(typeof(MyCollectionConverter))]
    public List<object> Objects { get; private set; }
}

请注意直接应用于Objects 属性的自定义TypeConverter。以下是出处:

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

请注意,我们会覆盖 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)
    {
    }
}

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

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

结果:

【讨论】:

  • 抱歉前几天没能回复你的评论,我一直在旅行,刚刚回到SO,这个答案正是我所需要的,非常感谢。
  • 另外,如果您不介意我问.. 您现在已经回答了我的一些问题,我非常感谢您的帮助,但我忍不住在您的个人资料中注意到你回答的问题,你一个都没问过!?
  • @John - 不,我通常会单独找到我的问题的答案(很多在 SO BTW):-)
【解决方案2】:

我自己使用过 TypeConverters,我可以确认它们是底部的主要痛苦。你得到关于实际出了什么问题的 nada 信息,只有奇怪的输出......

Idk 如果有帮助的话,但是将一个空 (null) 数组添加到任何 不是 IEnumerable 的东西可能是个问题?尝试将 add 指令移到 if (...) 的范围内。我不认为这有什么坏处。

另外,您确定(在最后一个使用 EndJoint 的示例中)getter 不返回空指针吗?空白条目闻起来像从我的经验中传递的空指针。

【讨论】:

  • Def 理解您对不良反馈和 TypeConverters 的含义,不确定我是否理解您将添加到 if 范围内的含义。你能提供一些代码来做一个直观的说明吗?谢谢
猜你喜欢
  • 2021-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-13
  • 1970-01-01
  • 2017-12-26
  • 2012-09-26
  • 2010-12-07
相关资源
最近更新 更多