2009-04-14 18 views
8

私は、ユーザーが最終的にPropertyGridに表示される独自のカスタムプロパティを作成するオプションを持つプログラムを作成しています。今はカスタムエディタを使いたくないので、PropertyGridには既にエディタが組み込まれているプリミティブ型プロパティ(stringintdoubleDateTimeboolなど)のみを許可しています。実行時に列挙を作成/変更する

ただし、ユーザーに複数の選択肢プロパティを作成するオプションを与え、可能な値のリストを定義して、PropertyGridのドロップダウンリストとして表示することもできます。

Enumをコードにハードコードすると、プロパティグリッドにドロップダウンリストとしてそのプロパティenumが自動的に表示されます。しかし、ユーザーが別のプロパティオプションを追加してPropertyGridに戻り、新しいオプションをドロップダウンリストに表示できるように、実行時に列挙を作成または変更することはできますか?

更新パトリックのコメントを考慮し

、私はEnum sが、この場合に行くための正しい方法ではないことを考えています。その代わりに、文字列のリストを使用してPropertyGridアイテムのドロップダウンを設定するにはどうすればよいですか?カスタムエディタが必要ですか?

答えて

5

答えは簡単なクラスです:タイプコンバーターです。 (そして、はい、列挙型はここでは適していません)。

私は詳細はあまり知られていないので、SelectedObjectプロパティでPropertyGridをターゲットインスタンスに「リンク」し、ターゲットインスタンスがICustomTypeDescriptorを実装しているため、プロパティ(つまりPropertyDescriptors)をランタイム。あなたのデザインは分かりませんが、このようなことをしていない場合は、見てみることをお勧めします。

ここで、文字列プロパティを追加し、このプロパティに対してユーザーが一連の制約を指定できるようにしたいとします。あなたのUIはユーザーに一連の文字列を入力させ、結果として文字列のリストを取得します。おそらく、あなたのターゲットインスタンスにプロパティの辞書を保持しているので、この新しいリストもそこに保存されていると仮定しましょう。

ここで、TypeConverter(またはこの例ではStringConverter)から派生した新しいコンバータを作成してください。 GetStandardValuesSupportedをオーバーライドしてtrueを返し、GetStandardValuesを使用して文字列のリストを返します(contextパラメータを使用してInstanceプロパティとその文字列リストにアクセスする必要があります)。このコンバータはPropertyDescriptorによってPropertyDescriptor.Converterプロパティと共に発行されます。

これはあまりにも曖昧ではないことを願っています。このプロセスに関する特定の質問がある場合は、私に知らせてください。

+0

そのソリューションのサンプルコードを提供できますか – Cracker

0

コードを使用してコードを作成し、それを一時的なテキストファイルに保存してから使用できます。これは、HDDの使用に伴い遅くなります。 reflectionを調べることをおすすめします。

編集:私の本の1つに完璧な例がありましたが、これはかなり長いですが、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

-7

Enum.GetNames()およびEnum.GetValues()を使用して値を取得し、新しい値を動的に追加できます。列挙型の代わりにリストを使用するか、デザインを再考することをお勧めします。何か右の臭いはありません。

+2

あなたはできません。 – Samuel

+0

反射で追加できますか? –

+0

GETNamesとGETValuesは、そのメソッドで列挙型を変更できないというヒントを与える必要があります。 –

3

問題の典型的なエンジニアリングソリューションは、リストをデータベースの参照データとして維持することです。一般に、列挙型はコンパイル時に定義される定数であることを意図しており、後でリリースされるコードでの変更は、switch文に副作用を引き起こす可能性があるため、(ランタイムはもちろん)推奨されません。

関連する問題