2009-04-14 108 views
8

我正在创建一个程序,用户可以选择创建自己的自定义属性,最终将显示在PropertyGrid中。现在我不想混淆自定义编辑器,所以我只允许PropertyGrid已经内置编辑器的原始类型属性(string,int,double,,bool等)。在运行时创建/修改枚举

但是,我也想让用户选择创建多个选项属性,他们可以在其中定义可能的值列表,然后将其显示为PropertyGrid中的下拉列表。

当我在我的代码中硬编码Enum时,属性网格会自动将该enum的属性显示为下拉列表。但是,我可以在运行时创建和修改枚举,以便用户可以添加其他属性选项,并返回到PropertyGrid并在下拉列表中查看其新选项?

更新

考虑帕特里克评论,我在想,Enum s为在此情况下,以正确的方式去。那么我该如何使用字符串列表填充PropertyGrid项目中的下拉列表?这需要一个自定义编辑器吗?

回答

5

答案是在一个简单的类:TypeConverter。 (是的,枚举在这里不适合)。由于我没有很多细节,我将假设你有一个PropertyGrid通过SelectedObject属性“链接”到一个目标实例,并且你的目标实例实现了ICustomTypeDescriptor,这样你可以添加属性(即PropertyDescriptors)在运行。我不知道你的设计,但如果你不这样做,我建议你看看它。

现在让我们假设您添加一个字符串属性,并且希望让用户为此属性指定一组约束。您的用户界面让用户输入一组字符串,并得到一个字符串列表。也许你会在目标实例中保存一个属性字典,所以我们假设这个新列表也存储在那里。

现在,只需编写一个从TypeConverter派生的新转换器(或者在此示例中可能是StringConverter)。您将不得不重写GetStandardValuesSupported以返回true,并返回字符串列表(使用上下文参数来访问实例属性及其字符串列表),以返回true和GetStandardValues。该转换器将由您的PropertyDescriptor与PropertyDescriptor.Converter属性一起发布。

我希望这不是太模糊。如果您对此流程有具体问题,请告诉我。

0

您可以使用您的代码创建代码,然后将其保存到临时文本文件,然后使用它。这将会很慢,因为它涉及使用硬盘。我会建议调查reflection。我在我的一本书中发现了一个完美的例子,在这里(它很长,但是如果你将它复制到VS中,它会更有意义)。

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

输出:(2000)= 2001000
循环的 总和。经过的毫秒数:
11468.75对于1000000次迭代
(2000)的总和= 2001000
蛮力。经过毫秒:
406.25达到1000000次迭代

Here是链接到整个章节,如果你想了解更多的信息。

+1

我真的很讨厌它,当人们投票回答没有解释为什么。这不行吗?它不回答这个问题吗? – Tarynn 2012-12-17 18:21:46

-7

您可以使用Enum.GetNames()和Enum.GetValues()来检索值并动态地向它们添加新值。尽管我建议你使用列表而不是枚举或重新考虑你的设计。有些东西不闻到对。

+2

呃,不,你不行。 – Samuel 2009-04-14 02:46:15

+0

我们可以通过反射来添加它们吗? – 2009-04-14 03:16:21

3

您的问题的典型工程解决方案是使用维护列表作为数据库中的参考数据。一般而言,枚举的目的是在编译时定义常量,并且在稍后发布的代码中对它们的修改是不鼓励的(更不用说运行时),因为它可能导致switch语句中的副作用。