2010-08-24 25 views
7

Me encanta PropertyGrid, bueno, al menos el concepto subyacente: use la reflexión y los atributos para editar sus objetos sin escribir mucho código de UI.Alternativas PropertyGrid

Mi entusiasmo se agotó bastante rápido, el envío predeterminado de PropertyGrid con WinForms es una mierda. Bueno, está bien para editar objetos simples, pero eso es todo.

  • No muestra los UITypeEditors apropiados para las propiedades dinámicas que tienen el tipo "Objeto".
  • Tan pronto como sus objetos contengan colecciones, es posible que pueda editarlos con el denominado Editor de colecciones. Sin embargo, no activará el evento PropertyValueChanged. Entonces, una vez que necesitas agregar la funcionalidad de deshacer, estás jodido.
  • Y todavía no he encontrado una forma elegante de agregar validación para CollectionEditor.
  • También es problemático implementar deshacer si tiene varios objetos seleccionados, porque en ese caso el argumento PropertyValueChanged args ChangedItem es nulo.

Pronto me encontré escribiendo hacks para abordar esos problemas con resultados poco agradables.

¿Qué harías? ¿Existe una solución elegante para al menos los primeros tres problemas? ¿Hay una opción propertygrid? Preferiblemente gratis & sin PInvokes?

+0

En cuanto a propertychanged para colecciones. Me preocuparía si algo cambia la referencia de la instancia de colección. Piénsalo. – leppie

Respuesta

5

Gran parte de la elegancia de PropertyGrid proviene de su simplicidad. Por encima de todo, está diseñado para jugar bien con Visual Studio, y espero verlo utilizado principalmente en las extensiones personalizadas UITypeEditor y no en el código de la aplicación.

¿Presumiblemente los objetos que está adjuntando al PropertyGrid son clases de su propio diseño? Descubrí que, para hacer un buen uso de la cuadrícula de propiedades, debe decorar fuertemente sus clases y miembros con atributos.

Puede encontrar algo de alegría en escribir sus propias subclases de CollectionEditor (y otros tipos de editores) y adjuntarlas a miembros de la clase utilizando el atributo [Editor]; si puede adjuntar este atributo a sus propiedades dinámicas, puede forzar el uso de un editor en particular.

La única forma en que puedo pensar en agregar validación a CollectionEditor es anular el método CreateCollectionForm(), devolviendo una instancia de su propia subclase personalizada de CollectionEditor.CollectionForm. Existe la posibilidad de que puedas disparar los eventos de cambio desde aquí.

Lamentablemente, todo lo que puedo hacer es asentir y estar de acuerdo con la afirmación sobre la implementación de deshacer. Es posible que deba recurrir a la "copia de seguridad" de los objetos afectados mediante clonación o serialización para implementar deshacer.

He visto alternativas al control de cuadrícula de propiedad integrado, pero existen principalmente para ofrecer diferentes estilos visuales.

2

Si alguien está interesado: aquí hay una solución para el problema PropertyValueChanged que simula un cambio invocando la función MemberwiseClone de System.Object si se ha disparado PropertyValueChanged de CollectionEditor ...

public class FixedCollectionEditor : CollectionEditor 
{   
    bool modified; 

    public FixedCollectionEditor(Type type) 
     : base(type) 
    { } 

    public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value) 
    {    
     value = base.EditValue(context, provider, value); 
     if (value != null && modified) 
     { 
      value = value.GetType() 
       .GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic) 
       .Invoke(value, new object[] { });     
     } 
     modified = false; 
     return value; 
    } 

    protected override CollectionForm CreateCollectionForm() 
    { 
     CollectionForm collectionForm = base.CreateCollectionForm(); 

     foreach (Control table in collectionForm.Controls) 
     { 
      if (!(table is TableLayoutPanel)) { continue; } 
      foreach (Control c1 in table.Controls) 
      { 
       if (c1 is PropertyGrid) 
       { 
        PropertyGrid propertyGrid = (PropertyGrid)c1; 
        propertyGrid.PropertyValueChanged += new PropertyValueChangedEventHandler(GotModifiedHandler); 
       } 
       if (c1 is TableLayoutPanel) 
       { 
        foreach (Control c2 in c1.Controls) 
        { 
         if (!(c2 is Button)) { continue; } 
         Button button = (Button)c2; 
         if (button.Name == "addButton" || button.Name == "removeButton") 
         { 
          button.Click += new EventHandler(GotModifiedHandler); 
          if (button.ContextMenuStrip != null) 
          { 
           button.ContextMenuStrip.ItemClicked += new ToolStripItemClickedEventHandler(GotModifiedHandler); 
          } 
         } 
        } 
       } 
      } 
     } 
     return collectionForm; 
    } 

    void GotModifiedHandler(object sender, EventArgs e) 
    { 
     modified = true; 
    } 
} 
1

Visualhint vende un reemplazo para la red de la propiedad que puede ayudar. Como nunca lo he usado en un proyecto real, no sé qué tan bien funciona.