Frage

Ich erstelle ein Programm, in dem der Benutzer die Möglichkeit hat, seine eigenen benutzerdefinierten Eigenschaften zu erstellen, die letztendlich in einem angezeigt werden PropertyGrid.Im Moment möchte ich mich nicht mit benutzerdefinierten Editoren herumschlagen, deshalb erlaube ich nur Eigenschaften primitiver Typen (string, int, double, DateTime, bool usw.), dass die PropertyGrid verfügt bereits über integrierte Editoren für.

Allerdings möchte ich dem Benutzer auch die Möglichkeit geben, Multiple-Choice-Eigenschaften zu erstellen, in denen er eine Liste möglicher Werte definieren kann, die wiederum als Dropdown-Liste im angezeigt werden PropertyGrid.

Wenn ich eine fest codiere Enum In meinem Code zeigt das Eigenschaftenraster automatisch die Eigenschaften davon an enum als Dropdown-Liste.Aber kann ich zur Laufzeit eine Aufzählung erstellen und/oder ändern, damit der Benutzer eine weitere Eigenschaftsoption hinzufügen und zu dieser zurückkehren kann? PropertyGrid und ihre neue Option in einem Dropdown-Menü sehen?

Aktualisieren

Wenn ich Patricks Kommentar bedenke, denke ich das Enums sind in diesem Fall nicht der richtige Weg.Wie kann ich stattdessen eine Liste von Zeichenfolgen verwenden, um ein Dropdown-Menü in einem zu füllen? PropertyGrid Artikel?Wäre dafür ein benutzerdefinierter Editor erforderlich?

War es hilfreich?

Lösung

Die Antwort liegt in einer einfachen Klasse: TypeConverter.(Und ja, Aufzählungen sind hier nicht geeignet).

Da ich nicht viele Details habe, gehe ich davon aus, dass Sie ein PropertyGrid haben, das durch die SelectedObject-Eigenschaft mit einer Zielinstanz „verknüpft“ ist, und dass Ihre Zielinstanz ICustomTypeDescriptor implementiert, sodass Sie Eigenschaften hinzufügen können (d. h.PropertyDescriptors) zur Laufzeit.Ich kenne Ihr Design nicht, aber wenn Sie es nicht so machen, empfehle ich Ihnen, es sich anzusehen.

Nehmen wir nun an, Sie fügen eine Zeichenfolgeneigenschaft hinzu und möchten, dass Ihr Benutzer eine Reihe von Einschränkungen für diese Eigenschaft angibt.Auf Ihrer Benutzeroberfläche kann der Benutzer eine Reihe von Zeichenfolgen eingeben und als Ergebnis erhalten Sie eine Liste mit Zeichenfolgen.Möglicherweise führen Sie in Ihrer Zielinstanz ein Wörterbuch mit Eigenschaften. Nehmen wir also an, dass diese neue Liste auch dort gespeichert ist.

Schreiben Sie jetzt einfach einen neuen Konverter, der von TypeConverter (oder in diesem Beispiel vielleicht von StringConverter) abgeleitet ist.Sie müssen GetStandardValuesSupported überschreiben, um true und zurückzugeben GetStandardValues um die Liste der Zeichenfolgen zurückzugeben (verwenden Sie den Kontextparameter, um auf die Instanzeigenschaft und ihre Liste der Zeichenfolgen zuzugreifen).Dieser Konverter wird von Ihrem PropertyDescriptor mit der Eigenschaft PropertyDescriptor.Converter veröffentlicht.

Ich hoffe, das ist nicht zu nebulös.Wenn Sie eine spezielle Frage zu diesem Prozess haben, lassen Sie es mich einfach wissen.

Andere Tipps

Die typische Engineering-Lösung für Ihr Problem ist die Liste als Referenzdaten in der Datenbank zu verwenden, zu erhalten. Im Allgemeinen Aufzählungen sollen Konstanten bei der Kompilierung definiert werden, und deren Änderung in späteren Code freigegeben wird (ganz zu schweigen von der Laufzeit) abgeraten, da es Nebenwirkungen in switch-Anweisungen führen kann.

könnten Sie erstellen Code Ihren Code verwenden, ist es dann in eine temporäre Textdatei speichern, und dann verwenden. Dies wäre langsam, da es sich um die Festplatte verwenden. Ich würde empfehlen, Reflexion .

Bearbeiten: Ich fand das perfekte Beispiel in einem meiner Bücher, hier ist es (es ist ziemlich lange, aber wenn Sie es in VS kopieren, wird es mehr Sinn machen)

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

Ausgabe: Summe (2000) = 2.001.000
Looping. Abgelaufene Millisekunden:
11.468,75 für 1000000 Iterationen
Summe (2000) = 2.001.000
Rohe Gewalt. Abgelaufene Millisekunden:
406,25 für 1 Million Iterationen

Hier ist ein Link auf das gesamte Kapitel, wenn Sie möchten mehr info.

Sie können Enum.GetNames() und Enum.GetValues() verwenden, um Werte abzurufen und ihnen dynamisch neue hinzuzufügen.Ich empfehle Ihnen jedoch, eine Liste anstelle einer Aufzählung zu verwenden oder Ihr Design zu überdenken.etwas riecht nicht richtig.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top