Pergunta

Estou criando um programa onde o usuário tem a opção de criar suas próprias propriedades personalizadas que acabará por ser exibidos em uma PropertyGrid. Agora eu não quero mexer com editores personalizados, então eu só estou permitindo propriedades primitivas tipo (string, int, double, DateTime, bool etc.) que o PropertyGrid já construídas em editores de.

No entanto, eu também quero dar ao usuário a opção de criar várias propriedades de escolha onde podem definiu uma lista de possíveis valores que por sua vez vai aparecer como uma lista suspensa na PropertyGrid para baixo.

Quando eu disco rígido código um Enum no meu código a grade de propriedades mostra automaticamente propriedades dessa enum como uma lista suspensa. Mas eu posso criar e ou modificar uma enumeração em tempo de execução para que o usuário pode adicionar uma outra opção de propriedade, e voltar para o PropertyGrid e ver sua nova opção em um drop-down?

Atualizar

Considerando Patricks comentário, eu estou pensando que Enums não é o caminho certo a seguir neste caso. Então, em vez como eu posso usar uma lista de strings para preencher um drop-down em um item de PropertyGrid? Oxalá exigem um editor personalizado?

Foi útil?

Solução

A resposta está em uma classe simples: TypeConverter . (E sim, enums não são adequados aqui).

Uma vez que eu não tenho um monte de detalhes, vou assumir que você tem um PropertyGrid "ligada" a uma instância de destino pela propriedade SelectedObject e que seu alvo exemplo implementa ICustomTypeDescriptor de modo que você pode adicionar propriedades (ou seja PropertyDescriptors) em tempo de execução . Eu não sei o seu design, mas se você não está fazendo assim, eu aconselho você a ter um olhar para ele.

Agora, vamos dizer que você adicionar uma propriedade string e que deseja deixar o seu usuário especificar um conjunto de restrições para esta propriedade. Seu let UI é o usuário digite um conjunto de cordas e você terá uma lista de strings como resultado. Talvez você manter um dicionário de propriedades na sua instância de destino então vamos assumir essa nova lista é armazenada lá também.

Agora, basta escrever um novo conversor derivado de TypeConverter (ou StringConverter talvez neste exemplo). Você terá que substituir GetStandardValuesSupported para retornar verdadeiro e GetStandardValues ?? para retornar a lista de strings (use o parâmetro de contexto para acessar a propriedade Instância e sua lista de strings). Este conversor será publicado pelo seu PropertyDescriptor com a propriedade PropertyDescriptor.Converter.

Espero que isso não é muito nebuloso. Se você tem uma pergunta específica sobre esse processo, é só me avisar.

Outras dicas

A solução de engenharia típica para o seu problema é a utilização para manter a lista de dados de referência em seu banco de dados. Em enums gerais se destinam a ser constantes definidas em tempo de compilação, e sua modificação mais tarde lançada do código é desencorajado (muito menos tempo de execução), pois pode causar efeitos secundários em declarações switch.

Você pode criar código usando seu código, em seguida, salve-o em um arquivo de texto temporário e, em seguida, usá-lo. Este seria lenta, uma vez que envolve o uso do HDD. Eu recomendaria para reflexão .

Editar: Eu encontrei o exemplo perfeito de um dos meus livros, aqui está (é bastante longo, mas se você copiá-lo em VS, ele vai fazer mais 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);
      }
   }
}

Saída: Soma de (2000) = 2.001.000
Looping. milissegundos decorridos:
11.468,75 para 1.000.000 iterações
Soma de (2000) = 2.001.000
Brute Force. milissegundos decorridos:
406,25 para 1000000 iterações

Aqui é um link para o capítulo inteiro se você gostaria de mais informações.

Você pode usar Enum.GetNames () e Enum.GetValues ??() para valores retreive e dinamicamente adicionar novas para eles. embora eu sugiro que você use uma lista em vez de enum ou repensar o seu design. Algo não cheira bem.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top