2012-08-07 9 views
9

Tengo muchas clases personalizadas que estoy usando y explicaré y publicaré ejemplos de. Después de explicar lo que hacen todos, trataré de describir claramente las condiciones bajo las cuales está ocurriendo mi error.Solución de reflexión dinámica personalizada, complicada, C#

En primer lugar, estoy usando un PropertyGrid para mostrar las propiedades de varios tipos diferentes de objetos. Debido a que el enlace predeterminado de PropertyGrid no era tan descriptivo como yo quería que fuera, creé unas pocas clases personalizadas a las que me referiré como clases "Display". Estas clases de visualización se construyen pasando un objeto y luego creando propiedades que devuelven cadenas y descripciones muy bien formateadas para las propiedades públicas (y en algunos casos métodos) del objeto real que se pasó.

Lo demostraré con un código de ejemplo abreviado:

Aquí es un ejemplo de un objeto que desea mostrar en mi PropertyGrid:

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

    //properties 
    public string Name { get; set;} 
    public CustomObject CC { get; set;} 
    public List<CustomObject> Custom List { get; set;} 
} 

la cadena de propiedad "Nombre" muestra bien en el PropertyGrid Sin embargo el CustomObject y lista DID no mostrar de manera que parecía muy útil r amistoso para mí.

Así que intentaron crear una solución escribiendo esta clase:

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

Como se puede ver en el código anterior, he creado DisplayClasses especiales tanto para mi clase conjunta y mi clase CustomObject. En mi proyecto, tengo muchos, muchos tipos diferentes de objetos que requieren el mismo tipo de propiedades de clase de pantalla superpuestas.

arriba, Usted puede ver las líneas que he añadido por encima de las dos últimas propiedades

[TypeConverterAttribute (typeof (ExpandableObjectConverter))]

Esta línea resuelve mi problema de mostrar el CustomObject como quiero a en la propertGrid (casi ... más sobre esto más adelante). Sin embargo, no funciona de la misma manera para mi propiedad Lista personalizada. En la lista personalizada, se expande para mostrar únicamente el recuento y la capacidad (las propiedades reales de la lista) Tiene sentido por qué ocurre esto, pero no era lo que yo quería. Quería ver el objeto contenido real dentro de la lista.

enter image description here

Así que aquí es mi solución complicada, tomado inicialmente de this question:

Tengo dos clases que estoy usando para añadir dinámicamente objetos con destino a la lista PropertyGrid en forma de propiedades. El primero (CustomClass) puede ser downloaded here. Se usa para crear dinámicamente propiedades. La segunda clase (DisplayIEnumerable) que estoy utilizando se deriva de la primera y se puede encontrar here.

La clase DisplayIEnumerable recorre los objetos de la lista y agrega una propiedad a sí misma con la información contenida dentro de cada objeto. Se pasa un DisplayClass para definir exactamente cómo deben representarse esas propiedades de los objetos dentro de la Grilla.

¡Hasta este punto todo funciona muy bien!como lo demuestra esta imagen (imagen no se ha creado usando las clases establecidas, las cadenas tienen un formato diferente en las clases que estoy usando, eliminado el formato de código para ayudarle a concentrarse en el código relevante:

enter image description here

Ahora después de eso Intro largo, la verdadera pregunta. Usando las técnicas anteriores, me gustaría escribir una clase que pueda manejar de forma dinámica los CustomObjects para los que no he escrito clases de visualización únicas. Tengo la intención de dejar este código para aquellos que usan la aplicación para probar puede probar de manera más efectiva sin tener que tener una clase de visualización completa para cada uno de los objetos personalizados de la empresa. (hay cientos) En su lugar, al vincular el propertyGrid con la clase siguiente, espero tener todas las propiedades que son listas y CustomObjects que tienen DisplayClasses correspondientes para vincularlos en su lugar.

Aquí está la clase que ya he probado y tengo un error. Todavía no he intentado implementar la sustitución de las listas con mi clase DisplayIEnumerable sin embargo, quería obtener la funcionalidad básica de trabajo en primer lugar:

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 
    } 

Cuando se ejecuta, aparece El PropertyGrid como debería: Before

Sin embargo , una vez que se hace clic en la flecha Expandir, no pasa nada, y la flecha desaparece: After

lo que está mal en la clase anterior que no es malo en mi clase DisplayIEnumerable, que causa esta variación en el comportamiento?

Estoy utilizando la clase DisplayObject como este (dentro de un displayclass):

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

Gracias de antemano! Estaré muy impresionado si alguien supera esta pregunta.

+0

Por qué ¿no utilizas una colección estándar UITypeEditor (el formulario estándar con dos columnas)? –

+0

Porque no podré agregar recursivamente este atributo expandible a las propiedades que se encuentran dentro de las propiedades. Quiero que el usuario pueda profundizar tanto como lo desee para ver completamente las relaciones entre los diferentes objetos. – jth41

+0

El formulario de recopilación estándar incluye una cuadrícula de propiedades, por lo que también es recurrente. –

Respuesta

5

No es necesario crear clases especiales para usar la cuadrícula de propiedades. Simplemente decore las propiedades con los atributos adecuados. He aquí un ejemplo:

Dos clases personalizadas:

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

Nota del ToString está anulado, eso es lo que utiliza la cuadrícula de propiedades por defecto si no TypeConverter se define para un tipo dado.

Una clase "titular" que tiene una colección de objetos personalizados:

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

    public string Name { get; set; } 

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

Nota la costumbre TypeConverter aplicado directamente a la propiedad Objects. Aquí está la fuente:

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

Nota sobreescribimos ConvertTo y darle una cadena especial que muestra una lista separada por comas de los objetos de la lista. El GetProperties también se reemplaza y utiliza un PropertyDescriptor especial; Se añade un atributo ExpandableObjectConverter a objetos sub para que puedan ampliarse también:

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) 
    { 
    } 
} 

Ahora, aquí es un código de ejemplo:

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

Y el resultado:

enter image description here

+0

Perdón por no poder responder a su comentario hace unos días, he estado viajando y acabo de regresar a SO, esta respuesta es justo lo que necesitaba Muchas gracias. – jth41

+0

Además, si no te molesta que pregunte ... Has respondido algunas de mis preguntas ahora y realmente aprecio la ayuda, pero no pude evitar notar en tu perfil que con todas las preguntas que has respondido, no le he preguntado a nadie !? – jth41

+0

@John - no, generalmente encuentro respuestas a mis preguntas solo (muchas de ellas TANTO) :-) –

1

Al haber trabajado con TypeConverters, puedo confirmar que son un gran dolor en las partes inferiores. Se obtiene información nada acerca de lo que realmente está pasando mal, sólo se emite raro ...

Idk si ayuda, pero tal vez es un problema que se agrega un conjunto vacío (nulo) a todo lo que es no un IEnumerable ? Intente mover la instrucción add al alcance de if (...). No creo que haya ningún daño en eso.

Además, ¿está seguro de que (en el último ejemplo con EndJoint) el captador no devuelve un puntero nulo? Las entradas en blanco huelen como punteros nulos que pasan de mis experiencias.

+0

Def. Entiende lo que quieres decir con los pobres comentarios y TypeConverters, no estoy seguro de entender lo que quieres decir con mover el complemento al ámbito de if. ¿Puedes proporcionar algún código para una ilustración visual? gracias – jth41

Cuestiones relacionadas