Pregunta

Estoy creando un programa donde el usuario tiene la opción de crear sus propias propiedades personalizadas que finalmente se mostrarán en un PropertyGrid.En este momento no quiero meterme con editores personalizados, así que solo permito propiedades de tipo primitivo (string, int, double, DateTime, bool etc.) que el PropertyGrid ya tiene editores integrados para.

Sin embargo, también quiero darle al usuario la opción de crear propiedades de opción múltiple donde pueda definir una lista de valores posibles que a su vez aparecerá como una lista desplegable en el PropertyGrid.

Cuando codifico un Enum en mi código, la cuadrícula de propiedades muestra automáticamente las propiedades de ese enum como una lista desplegable.Pero, ¿puedo crear o modificar una enumeración en tiempo de ejecución para que el usuario pueda agregar otra opción de propiedad y volver a la PropertyGrid ¿Y ver su nueva opción en un menú desplegable?

Actualizar

Teniendo en cuenta el comentario de Patrick, estoy pensando que Enums no son el camino correcto a seguir en este caso.Entonces, ¿cómo puedo usar una lista de cadenas para completar un menú desplegable en un PropertyGrid ¿artículo?¿Eso requeriría un editor personalizado?

¿Fue útil?

Solución

La respuesta está en una clase simple: Convertidor de tipos.(y sí, las enumeraciones no son adecuadas aquí).

Como no tengo muchos detalles, asumiré que tiene un PropertyGrid "vinculado" a una instancia de destino mediante la propiedad SelectedObject y que su instancia de destino implementa ICustomTypeDescriptor para que pueda agregar propiedades (es decir,PropertyDescriptors) en tiempo de ejecución.No conozco tu diseño pero si no te va así te aconsejo que le eches un vistazo.

Ahora digamos que agrega una propiedad de cadena y que desea permitir que su usuario especifique un conjunto de restricciones para esta propiedad.Su interfaz de usuario permite al usuario ingresar un conjunto de cadenas y, como resultado, obtiene una lista de cadenas.Tal vez mantenga un diccionario de propiedades en su instancia de destino, así que supongamos que esta nueva lista también está almacenada allí.

Ahora, simplemente escriba un nuevo convertidor derivado de TypeConverter (o quizás StringConverter en este ejemplo).Tendrá que anular GetStandardValuesSupported para devolver verdadero y Obtener valores estándar para devolver la lista de cadenas (use el parámetro de contexto para acceder a la propiedad Instancia y su lista de cadenas).Este convertidor será publicado por su PropertyDescriptor con la propiedad PropertyDescriptor.Converter.

Espero que esto no sea demasiado confuso.Si tiene una pregunta específica sobre este proceso, hágamelo saber.

Otros consejos

La solución de ingeniería típica a su problema es utilizar para mantener la lista como datos de referencia en su base de datos. En las enumeraciones generales pretenden ser constantes definidas en tiempo de compilación, y su modificación en posteriormente liberado de código no se recomienda (por no hablar de tiempo de ejecución), ya que puede causar efectos secundarios en los estados de conmutación.

Se puede crear código con el código, a continuación, guardarlo en un archivo de texto temporal, y luego usarlo. Esto sería lento, ya que implica el uso del disco duro. Yo recomendaría mirar en reflexión .

Editar: He encontrado el perfecto ejemplo de uno de mis libros, aquí está (es bastante largo, pero si lo copia en VS, que va a hacer más sentido)

.
namespace Programming_CSharp
{
   using System;
   using System.Diagnostics;
   using System.IO;
   using System.Reflection;
   using System.Reflection.Emit;
   using System.Threading;

   // used to benchmark the looping approach
   public class MyMath
   {
      // sum numbers with a loop
      public int DoSumLooping(int initialVal)
      {
         int result = 0;
         for(int i = 1;i <=initialVal;i++)
         {
            result += i;
         }
         return result;
      }
   }

   // declare the interface
   public interface IComputer
   {
      int ComputeSum(  );
   }

   public class ReflectionTest
   {
      // the private method which emits the assembly
      // using op codes
      private Assembly EmitAssembly(int theValue)
      {
         // Create an assembly name
         AssemblyName assemblyName = 
            new AssemblyName(  );
         assemblyName.Name = "DoSumAssembly";

         // Create a new assembly with one module
         AssemblyBuilder newAssembly =
            Thread.GetDomain(  ).DefineDynamicAssembly(
            assemblyName, AssemblyBuilderAccess.Run);
         ModuleBuilder newModule =
            newAssembly.DefineDynamicModule("Sum");

         //  Define a public class named "BruteForceSums " 
         //  in the assembly.
         TypeBuilder myType =
            newModule.DefineType(
            "BruteForceSums", TypeAttributes.Public);

         // Mark the class as implementing IComputer.
         myType.AddInterfaceImplementation(
            typeof(IComputer));

         // Define a method on the type to call. Pass an
         // array that defines the types of the parameters,
         // the type of the return type, the name of the 
         // method, and the method attributes.
         Type[] paramTypes = new Type[0];
         Type returnType = typeof(int);
         MethodBuilder simpleMethod =
            myType.DefineMethod(
            "ComputeSum",
            MethodAttributes.Public | 
            MethodAttributes.Virtual,
            returnType,
            paramTypes);

         // Get an ILGenerator. This is used
         // to emit the IL that you want.
         ILGenerator generator = 
            simpleMethod.GetILGenerator(  );

         // Emit the IL that you'd get if you 
         // compiled the code example 
         // and then ran ILDasm on the output.

         // Push zero onto the stack. For each 'i' 
         // less than 'theValue', 
         // push 'i' onto the stack as a constant
         // add the two values at the top of the stack.
         // The sum is left on the stack.
         generator.Emit(OpCodes.Ldc_I4, 0);
         for (int i = 1; i <= theValue;i++)
         {
            generator.Emit(OpCodes.Ldc_I4, i);
            generator.Emit(OpCodes.Add);

         }

         // return the value
         generator.Emit(OpCodes.Ret);

         //Encapsulate information about the method and
         //provide access to the method's metadata
         MethodInfo computeSumInfo =
            typeof(IComputer).GetMethod("ComputeSum");

         // specify the method implementation.
         // Pass in the MethodBuilder that was returned 
         // by calling DefineMethod and the methodInfo 
         // just created
         myType.DefineMethodOverride(simpleMethod, computeSumInfo);

         // Create the type.
         myType.CreateType(  );
         return newAssembly;
      }

      // check if the interface is null
      // if so, call Setup.
      public double DoSum(int theValue)
      {
         if (theComputer == null)
         {
            GenerateCode(theValue);
         }

         // call the method through the interface
         return (theComputer.ComputeSum(  ));
      }

      // emit the assembly, create an instance 
      // and get the interface
      public void GenerateCode(int theValue)
      {
         Assembly theAssembly = EmitAssembly(theValue);
         theComputer = (IComputer) 
            theAssembly.CreateInstance("BruteForceSums");
      }

      // private member data
      IComputer theComputer = null;

   }

   public class TestDriver
   {
      public static void Main(  )
      {
         const int val = 2000;  // Note 2,000

         // 1 million iterations!
         const int iterations = 1000000;
         double result = 0;

         // run the benchmark
         MyMath m = new MyMath(  ); 
         DateTime startTime = DateTime.Now;            
         for (int i = 0;i < iterations;i++)
            result = m.DoSumLooping(val);
         }
         TimeSpan elapsed = 
            DateTime.Now - startTime;
         Console.WriteLine(
            "Sum of ({0}) = {1}",val, result);
         Console.WriteLine(
            "Looping. Elapsed milliseconds: " + 
            elapsed.TotalMilliseconds + 
            " for {0} iterations", iterations);

         // run our reflection alternative
         ReflectionTest t = new ReflectionTest(  );

         startTime = DateTime.Now; 
         for (int i = 0;i < iterations;i++)
         {
            result = t.DoSum(val);
         }

         elapsed = DateTime.Now - startTime;
         Console.WriteLine(
            "Sum of ({0}) = {1}",val, result);
         Console.WriteLine(
            "Brute Force. Elapsed milliseconds: " + 
            elapsed.TotalMilliseconds  + 
            " for {0} iterations", iterations);
      }
   }
}

Salida: Suma de (2000) = 2.001.000
Looping. milisegundos transcurridos: Read 11.468,75 para 1000000 iteraciones
Suma de (2000) = 2.001.000
Fuerza bruta. milisegundos transcurridos: Read 406.25 para 1000000 iteraciones

Aquí es un enlace a todo el capítulo, si desea obtener más información.

Puede utilizar Enum.GetNames() y Enum.GetValues() para recuperar valores y agregarles dinámicamente otros nuevos.aunque te sugiero que uses una lista en lugar de una enumeración o replantees tu diseño.algo no huele bien.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top