2011-03-23 70 views
0

我想使用PropertyGrid輸入方法參數。使用PropertyGrid輸入方法參數

我有一些應用程序,將動態加載用戶的DLL和調用與特定簽名(已知的返回類型)的方法。

我想選擇與PropertyGrid控件呈現用戶輸入的參數被調用方法容易。

問題是 - PropertyGrid的作品上的對象,而不是在方法。

我想在運行時以某種方式「改造」的方法爲對象與屬性反映它的參數,調用它,當使輸入值的方法。

Offcourse我想有型校驗等(如果由PropertyGrid的規定,不,現在記不清了)。

對此有任何簡單的解決方案?

謝謝!

+0

我已經解僱了LinqPad並內置CodeDom中使用的樣本,但我不想以這種態度向某人發佈此消息。 (我不是在積分後,只是感覺錯誤。) – 2011-03-23 11:11:24

+0

你指的是我的態度嗎?我在這裏是新的,我不知道我需要將它標記爲已接受。我標記爲「接受」的答案很少,但這仍然沒有更新。我很抱歉,我已經接受了這個問題 – 2011-03-23 11:16:43

回答

2

那麼這是我昨天寫的。 這意味着要運行在LinqPad,這是一個很棒的免費工具來測試linq查詢或代碼片段。 (與廉價的升級來獲得智能感知)

的代碼應該告訴你如何對待不同類型的參數(參考,出來),以及是否你調用一個實例方法或沒有。 (翻轉主的意見,以測試實例方法)

在LinqPad,你可以使用轉儲()擴展方法讓它顯示在結果窗口中的對象。這很方便查看實際發生的情況。

所以,如果你想知道如何動態地構造一個類型,並調用它,這應該讓你開始:

編輯:我完全忘了提,那你就需要添加這兩個命名空間到查詢。你這樣做,通過點擊F4->額外的命名空間的進口和添加這些2:
System.CodeDom.Compiler
System.CodeDom

public static String TestMethod1(int a, ref int X, out string t) 
{ 
    a += X; 
    X = a * 2; 
    t = "...>" + (X + a); 
    return a.ToString() + "..."; 
} 

public class TestClass 
{ 
    public int SomeMethod(int a, DateTime? xyz) 
    { 
     if(xyz != null) 
      a+= xyz.GetValueOrDefault().Day; 
     return 12 + a; 
    } 
} 

void Main() 
{ 
    var sb = new StringBuilder(); 

    var methodInfo = typeof(UserQuery).GetMethod("TestMethod1"); 

    dynamic instance = CreateWrapper(methodInfo, sb); 

    instance.a = 11; 
    instance.X = 2; 

    instance.CallMethod(); 

    /* 
    var methodInfo = typeof(TestClass).GetMethod("SomeMethod"); 

    dynamic instance = CreateWrapper(methodInfo, sb); 

    instance.a = 11; 
    instance.xyz = new DateTime(2010, 1, 2); 

    instance.CallMethod(new TestClass()); 
*/ 
    ((Object)instance).Dump(); 

    sb.ToString().Dump(); 
} 

static object CreateWrapper(MethodInfo methodInfo, StringBuilder sb) 
{ 
    // pick either C#, VB or another language that can handle generics 
    var codeDom = CodeDomProvider.CreateProvider("C#"); 

    var unit = new CodeCompileUnit(); 
    var codeNameSpace = new CodeNamespace(); 
    codeNameSpace.Name = "YourNamespace"; 
    var wrapperType = AddWrapperType(codeDom, codeNameSpace, methodInfo, "WrapperType",  "MethodResultValue"); 

    unit.Namespaces.Add(codeNameSpace); 

    // this is only needed so that LinqPad can dump the code 
    codeDom.GenerateCodeFromNamespace(codeNameSpace, new StringWriter(sb), new CodeGeneratorOptions()); 

    // put the temp assembly in LinqPad's temp folder 
    var outputFileName = Path.Combine(Path.GetDirectoryName(new Uri(typeof(UserQuery).Assembly.CodeBase).AbsolutePath), 
                     Guid.NewGuid() + ".dll"); 
    var results = codeDom.CompileAssemblyFromDom(new CompilerParameters(new[]{new Uri(methodInfo.DeclaringType.Assembly.CodeBase).AbsolutePath, 
                       new Uri(typeof(UserQuery).Assembly.CodeBase).AbsolutePath, 
                       new Uri(typeof(UserQuery).BaseType.Assembly.CodeBase).AbsolutePath}.Distinct().ToArray(), 
                                         outputFileName), 
                          unit); 
    results.Errors.Dump(); 
    new Uri(results.CompiledAssembly.CodeBase).AbsolutePath.Dump(); 

    if(results.Errors.Count == 0) 
    { 
     var compiledType = results.CompiledAssembly.GetType(codeNameSpace.Name + "." + wrapperType.Name); 

     return Activator.CreateInstance(compiledType); 
    } 
    return null; 
} 


static CodeTypeDeclaration AddWrapperType(CodeDomProvider codeDom, 
              CodeNamespace codeNameSpace, 
                        MethodInfo methodInfo, 
                        string typeName, 
                        string resultPropertyName) 
{ 
    var parameters = (from parameter in methodInfo.GetParameters() 
         select parameter).ToList(); 

    var returnValue = methodInfo.ReturnType; 

    if(!String.IsNullOrEmpty(methodInfo.DeclaringType.Namespace)) 
     codeNameSpace.Imports.Add(new CodeNamespaceImport(methodInfo.DeclaringType.Namespace)); 

    var wrapperType = new CodeTypeDeclaration(typeName); 

    var defaultAttributes = MemberAttributes.Public | MemberAttributes.Final; 
    var thisRef = new CodeThisReferenceExpression(); 

    Func<Type, Type> getRealType = t => t.IsByRef || t.IsPointer ? t.GetElementType(): t; 


    Func<String, String> getFieldName = parameterName => "m_" + parameterName + "_Field"; 


    Action<ParameterInfo> addProperty = p => 
    { 
     var realType = getRealType(p.ParameterType); 
     var usedName = p.Position == -1 ? resultPropertyName : p.Name; 

     wrapperType.Members.Add(new CodeMemberField 
     { 
      Name = getFieldName(usedName), 
      Type = new CodeTypeReference(realType), 
      Attributes= MemberAttributes.Private 
     }); 

     var property = new CodeMemberProperty 
             { 
              Name = usedName, 
              Type = new CodeTypeReference(realType), 
              Attributes= defaultAttributes 
             }; 

     property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(thisRef, 
                            getFieldName(usedName)))); 
     property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(thisRef, getFieldName(usedName)), 
                  new CodeArgumentReferenceExpression("value"))); 

     wrapperType.Members.Add(property); 
    }; 

    parameters.ForEach(addProperty); 
    if(methodInfo.ReturnParameter != null) 
    { 
     addProperty(methodInfo.ReturnParameter); 
    } 

    var callMethod = new CodeMemberMethod 
    { 
     Name="CallMethod", 
     Attributes=defaultAttributes 
    }; 

    CodeMethodInvokeExpression invokeExpr; 

    if(!methodInfo.IsStatic) 
    { 
     callMethod.Parameters.Add(new CodeParameterDeclarationExpression(methodInfo.DeclaringType, 
                     "instance")); 
     invokeExpr = new CodeMethodInvokeExpression(new CodeArgumentReferenceExpression("instance"), 
                methodInfo.Name); 
    } 
    else 
     invokeExpr = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(methodInfo.DeclaringType), methodInfo.Name); 

    foreach(var parameter in parameters) 
    { 
     CodeExpression fieldExpression = new CodeFieldReferenceExpression(thisRef, 
                      getFieldName(parameter.Name)); 

     if(parameter.ParameterType.IsByRef && !parameter.IsOut) 
      fieldExpression = new CodeDirectionExpression(FieldDirection.Ref, fieldExpression); 
     else if(parameter.IsOut) 
      fieldExpression = new CodeDirectionExpression(FieldDirection.Out, fieldExpression); 
     else if(parameter.IsIn) 
      fieldExpression = new CodeDirectionExpression(FieldDirection.In, fieldExpression); 

     invokeExpr.Parameters.Add(fieldExpression); 
    } 

    wrapperType.Members.Add(callMethod); 

    if(returnValue != typeof(void)) 
     callMethod.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(thisRef, 
                          getFieldName(resultPropertyName)), 
                             invokeExpr)); 
    else 
     callMethod.Statements.Add(invokeExpr); 

    codeNameSpace.Types.Add(wrapperType); 

    return wrapperType; 
} 
+0

謝謝!這需要檢查一點......有一件事是我不能使用.net 4(不能使用動態關鍵字)。 – 2011-03-27 06:42:02

+0

然後,您必須獲取「CallMethod」方法的方法信息並調用該方法。屬性可以通過propertygrid設置/讀取。你也可以在linqpad中創建一個帶有propertygrid的表單,這樣你就可以把它弄糟,直到你明白它是如何工作的。 – 2011-03-27 06:44:46

2

我想你可以添加一個新類到您的項目實現了ICustomTypeDescriptor接口。並使用此類的實例作爲方法參數的包裝。

這裏是一個article展示瞭如何通過實現ICustomTypeDescriptor自定義屬性網格顯示。

+0

,但它仍然表示0%。感謝你通知我這件事。 – 2011-03-23 09:58:15

+0

我的問題是我如何在運行時創建具有每個方法參數屬性的新對象? – 2011-03-23 13:03:12

+0

您可以使用'ICustomTypeDescriptor'界面來控制pgrid中顯示的內容。您不需要使用這些屬性創建對象。這就是爲什麼'ICustomTypeDescriptor'在那裏。請閱讀文章。 – wenqiang 2011-03-27 17:19:05