2011-03-02 24 views
14

quiero tomar un objeto, digamos que este objeto:lista de visualización de objetos personalizados como un menú desplegable en el PropertiesGrid

public class BenchmarkList 
{ 
    public string ListName { get; set; } 
    public IList<Benchmark> Benchmarks { get; set; } 
} 

y tienen ese objeto mostrar su ListName como la parte "nombre" de la PropertiesGrid ("punto de referencia" sería bueno), y para la parte "valor" de la PropertyGrid, tener una lista desplegable de la IList <> de Puntos de referencia:

aquí es el objeto de referencia

public class Benchmark 
{ 
    public int ID {get; set;} 
    public string Name { get; set; } 
    public Type Type { get; set; } 
} 

Me gustaría que el menú desplegable mostrara la propiedad Name del Benchmark para ver lo que los usuarios pueden ver. Aquí está un ejemplo visual:

enter image description here

Así que, esencialmente, estoy tratando de conseguir una colección de objetos de referencia en una lista desplegable, y esos objetos deben mostrar su propiedad Name como el valor en el desplegable.

He leído otros artículos sobre el uso de PropertiesGrid, incluidos THIS y THIS, pero son más complejos de lo que estoy tratando de hacer.

Suelo trabajar en cosas del lado del servidor, y no se ocupan de la interfaz de usuario a través de Web Forms o Windows Forms, por lo que este PropertiesGrid que realmente está conmigo a dar un paseo ...

Yo sé que mi solución radica en la implementación "ICustomTypeDescriptor", que me permitirá decirle a PropertiesGrid qué valores debería mostrar, independientemente de las propiedades del objeto al que quiero vincular la lista desplegable, pero no estoy seguro de cómo o dónde implementar eso.

Cualquier sugerencia/ayuda sería muy apreciada.

Gracias, Mike

ACTUALIZACIÓN:

bien, así que voy a cambiar los detalles de todo un poco. Ya me estaba excediendo con los objetos que pensé que deberían estar involucrados, así que este es mi nuevo enfoque.

Tengo un objeto llamado Analytic. Este es el objeto que debe estar vinculado a PropertiesGrid. Ahora, si expongo una propiedad que es de tipo enum, PropertiesGrid se encargará de la lista desplegable, lo cual es muy bueno. Si expongo una propiedad que es una colección de un tipo personalizado, PropertiesGrid no es tan agradable ...

Este es el código para la analítica, el objeto de deseo de unirse a la PropertiesGrid:

public class Analytic 
{ 
    public enum Period { Daily, Monthly, Quarterly, Yearly }; 
    public Analytic() 
    { 
     this.Benchmark = new List<IBenchmark>(); 
    } 
    public List<IBenchmark> Benchmark { get; set; } 
    public Period Periods { get; set; } 
    public void AddBenchmark(IBenchmark benchmark) 
    { 
     if (!this.Benchmark.Contains(benchmark)) 
     { 
      this.Benchmark.Add(benchmark); 
     } 
    } 
} 

aquí está un ejemplo corto de dos objetos que implementan la interfaz IBenchmark:

public class Vehicle : IBenchmark 
{ 
    public Vehicle() 
    { 
     this.ID = "00000000-0000-0000-0000-000000000000"; 
     this.Type = this.GetType(); 
     this.Name = "Vehicle Name"; 
    } 

    public string ID {get;set;} 
    public Type Type {get;set;} 
    public string Name {get;set;} 
} 

public class PrimaryBenchmark : IBenchmark 
{ 
    public PrimaryBenchmark() 
    { 
     this.ID = "PrimaryBenchmark"; 
     this.Type = this.GetType(); 
     this.Name = "Primary Benchmark"; 
    } 

    public string ID {get;set;} 
    public Type Type {get;set;} 
    public string Name {get;set;} 
} 

Estos dos objetos se añadirán a la colección de lista de referencia del objeto analítico en el código de Windows Forms:

private void Form1_Load(object sender, EventArgs e) 
{ 
    Analytic analytic = new Analytic(); 
    analytic.AddBenchmark(new PrimaryBenchmark()); 
    analytic.AddBenchmark(new Vehicle()); 
    propertyGrid1.SelectedObject = analytic; 
} 

Aquí hay una captura de pantalla de la salida en PropertiesGrid.Tenga en cuenta que la propiedad expuesta como una enumeración obtiene una buena lista desplegable sin trabajo, pero la propiedad expuesta como Lista de obtiene un valor de (Cobro). Al hacer clic en (Colección), se obtiene el editor de la colección y luego se puede ver cada objeto, y sus respectivas propiedades:

enter image description here

Esto no es lo que estoy buscando. Al igual que en mi primera captura de pantalla en esta publicación, intento renderizar la propiedad Benchmark collection of List como una lista desplegable que muestra la propiedad del nombre del objeto como el texto de lo que se puede mostrar ...

Gracias

Respuesta

30

En general, una lista desplegable en una cuadrícula de propiedades se utiliza para establecer el valor de una propiedad, de una lista dada. Aquí eso significa que debería tener una propiedad como "Benchmark" de tipo IBenchmark y una posible lista de IBenchmark en otro lugar. Me he tomado la libertad de cambiar su clase Analítica así:

public class Analytic 
{ 
    public enum Period { Daily, Monthly, Quarterly, Yearly }; 
    public Analytic() 
    { 
     this.Benchmarks = new List<IBenchmark>(); 
    } 

    // define a custom UI type editor so we can display our list of benchmark 
    [Editor(typeof(BenchmarkTypeEditor), typeof(UITypeEditor))] 
    public IBenchmark Benchmark { get; set; } 

    [Browsable(false)] // don't show in the property grid   
    public List<IBenchmark> Benchmarks { get; private set; } 

    public Period Periods { get; set; } 
    public void AddBenchmark(IBenchmark benchmark) 
    { 
     if (!this.Benchmarks.Contains(benchmark)) 
     { 
      this.Benchmarks.Add(benchmark); 
     } 
    } 
} 

Lo que necesita ahora no es una ICustomTypeDescriptor, pero en lugar de un TypeConverter un un UITypeEditor. Lo necesario para decorar la propiedad de referencia con el UITypeEditor (como el anterior) y la interfaz IBenchmark con el TypeConverter así:

// use a custom type converter. 
// it can be set on an interface so we don't have to redefine it for all deriving classes 
[TypeConverter(typeof(BenchmarkTypeConverter))] 
public interface IBenchmark 
{ 
    string ID { get; set; } 
    Type Type { get; set; } 
    string Name { get; set; } 
} 

que aquí hay una implementación de ejemplo TypeConverter:

// this defines a custom type converter to convert from an IBenchmark to a string 
// used by the property grid to display item when non edited 
public class BenchmarkTypeConverter : TypeConverter 
{ 
    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) 
    { 
     // we only know how to convert from to a string 
     return typeof(string) == destinationType; 
    } 

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) 
    { 
     if (typeof(string) == destinationType) 
     { 
      // just use the benchmark name 
      IBenchmark benchmark = value as IBenchmark; 
      if (benchmark != null) 
       return benchmark.Name; 
     } 
     return "(none)"; 
    } 
} 

Y aquí es una muestra de Implementación de UITypeEditor:

// this defines a custom UI type editor to display a list of possible benchmarks 
// used by the property grid to display item in edit mode 
public class BenchmarkTypeEditor : UITypeEditor 
{ 
    private IWindowsFormsEditorService _editorService; 

    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) 
    { 
     // drop down mode (we'll host a listbox in the drop down) 
     return UITypeEditorEditStyle.DropDown; 
    } 

    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) 
    { 
     _editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)); 

     // use a list box 
     ListBox lb = new ListBox(); 
     lb.SelectionMode = SelectionMode.One; 
     lb.SelectedValueChanged += OnListBoxSelectedValueChanged; 

     // use the IBenchmark.Name property for list box display 
     lb.DisplayMember = "Name"; 

     // get the analytic object from context 
     // this is how we get the list of possible benchmarks 
     Analytic analytic = (Analytic)context.Instance; 
     foreach (IBenchmark benchmark in analytic.Benchmarks) 
     { 
      // we store benchmarks objects directly in the listbox 
      int index = lb.Items.Add(benchmark); 
      if (benchmark.Equals(value)) 
      { 
       lb.SelectedIndex = index; 
      } 
     } 

     // show this model stuff 
     _editorService.DropDownControl(lb); 
     if (lb.SelectedItem == null) // no selection, return the passed-in value as is 
      return value; 

     return lb.SelectedItem; 
    } 

    private void OnListBoxSelectedValueChanged(object sender, EventArgs e) 
    { 
     // close the drop down as soon as something is clicked 
     _editorService.CloseDropDown(); 
    } 
} 
+0

Simon, todo funciona genial! Entiendo la clase BenchmarkTypeConverter, pero la clase BenchmarkTypeEdit sigue siendo un misterio para mí. Parece que cada vez que hace clic en el menú desplegable, toda la lista debe reconstruirse y luego elegir el nuevo valor. Supongo que así es como PropertyGrid debe funcionar "detrás de escena", y es por eso que hacer algo que es muy directo puede llevar una buena cantidad de código. De cualquier manera, ¡muchas gracias por extender una mano amiga! Estoy mucho más avanzado que antes. ¡Algún buen Karma viene en tu camino! Mike –

+0

Algunos otros recursos geniales para explicar lo que Simon hizo aquí. Otra vez Simon, ¡muchas gracias! http://msdn.microsoft.com/en-us/library/ms171839.aspx http://msdn.microsoft.com/en-us/library/ms171840.aspx –

+0

Simon, has explicado muy bien el concepto y gracias por un ejemplo de trabajo muy bueno y simple :) ¡Salud! –

Cuestiones relacionadas