[c#] रनटाइम पर विशेषता का पैरामीटर बदलें



Answers

यदि कोई और इस एवेन्यू को चलाता है, तो जवाब यह है कि आप इसे प्रतिबिंब के साथ कर सकते हैं, सिवाय इसके कि आप ऐसा नहीं कर सकते क्योंकि ढांचे में एक बग है। यहां बताया गया है कि आप इसे कैसे करेंगे:

    Dim prop As PropertyDescriptor = TypeDescriptor.GetProperties(GetType(UserInfo))("Age")
    Dim att As CategoryAttribute = DirectCast(prop.Attributes(GetType(CategoryAttribute)), CategoryAttribute)
    Dim cat As FieldInfo = att.GetType.GetField("categoryValue", BindingFlags.NonPublic Or BindingFlags.Instance)
    cat.SetValue(att, "A better description")

सभी अच्छी और अच्छी, सिवाय इसके कि श्रेणी गुण सभी गुणों के लिए बदल दिया गया है, न केवल 'आयु'।

Question

मुझे यकीन नहीं है कि रनटाइम के दौरान विशेषता के पैरामीटर को बदलना संभव है या नहीं? उदाहरण के लिए, एक असेंबली के अंदर मेरे पास निम्न श्रेणी है

public class UserInfo
{
    [Category("change me!")]
    public int Age
    {
        get;
        set;
    }
    [Category("change me!")]
    public string Name
    {
        get;
        set;
    }
}

यह एक वर्ग है जो किसी तृतीय पक्ष विक्रेता द्वारा प्रदान किया जाता है और मैं कोड नहीं बदल सकता । लेकिन अब मुझे पता चला है कि उपर्युक्त विवरण सटीक नहीं हैं, और मैं उपरोक्त वर्ग के एक उदाहरण ग्रिड में बाध्य करते समय "मुझे बदलें" श्रेणी का नाम बदलना चाहता हूं।

क्या मुझे पता चलेगा कि यह कैसे करें?




यह देखते हुए कि PropertyGrid का चयनित आइटम "आयु" है:

SetCategoryLabelViaReflection(MyPropertyGrid.SelectedGridItem.Parent,
    MyPropertyGrid.SelectedGridItem.Parent.Label, "New Category Label");

जहां SetCategoryLabelViaReflection() को निम्नानुसार परिभाषित किया गया है:

private void SetCategoryLabelViaReflection(GridItem category,
                                           string oldCategoryName,
                                           string newCategoryName)
{
    try
    {
        Type t = category.GetType();
        FieldInfo f = t.GetField("name",
                                 BindingFlags.NonPublic | BindingFlags.Instance);
        if (f.GetValue(category).Equals(oldCategoryName))
        {
            f.SetValue(category, newCategoryName);
        }
    }
    catch (Exception ex)
    {
        System.Diagnostics.Trace.Write("Failed Renaming Category: " + ex.ToString());
    }
}

जहां तक ​​प्रोग्रामिक रूप से चयनित आइटम को व्यवस्थित रूप से सेट किया गया है, उस मूल श्रेणी की जिसमें आप बदलना चाहते हैं; कई सरल समाधान हैं। Google "एक विशिष्ट संपत्ति ग्रिड प्रॉपर्टी पर फ़ोकस करें"।




इस बीच में मैं निम्नलिखित लेखों से प्राप्त आंशिक समाधान में आया हूं:

  1. ICustomTypeDescriptor, भाग 1
  2. ICustomTypeDescriptor, भाग 2
  3. रनटाइम पर PropertyGrid से (निकालें) आइटम (निकालें) जोड़ें

असल में आप एक जेनेरिक क्लास CustomTypeDescriptorWithResources<T> , जो प्रतिबिंब और लोड फ़ाइल के माध्यम से गुणों और Category माध्यम से गुण प्राप्त करेंगे (मुझे लगता है कि आपको स्थानीयकृत टेक्स्ट प्रदर्शित करने की आवश्यकता है ताकि आप संसाधन फ़ाइल ( .resx ) का उपयोग कर सकें)




दुर्भाग्यवश विशेषताएँ रनटाइम पर बदलने के लिए नहीं हैं। आपके पास मूल रूप से दो विकल्प हैं:

  1. System.Reflection.Emit . का उपयोग कर फ्लाई पर एक समान प्रकार को मनोरंजन करें जैसा नीचे दिखाया गया है।

  2. इस कार्यक्षमता को जोड़ने के लिए अपने विक्रेता से पूछें। यदि आप Xceed.WpfToolkit का उपयोग कर रहे हैं। विस्तारित आप here से स्रोत कोड डाउनलोड कर सकते here और आसानी से इंटरफ़ेस को कार्यान्वित कर सकते हैं जैसे IResolveCategoryName जो रनटाइम पर विशेषता को हल करेगा। मैंने उससे थोड़ा अधिक किया, PropertyGrid अंदर एक DoubleUpDown में संख्यात्मक मान संपादित करते समय सीमाओं जैसे अधिक कार्यक्षमता को जोड़ना बहुत आसान था।

    namespace Xceed.Wpf.Toolkit.PropertyGrid
    {
        public interface IPropertyDescription
        {
            double MinimumFor(string propertyName);
            double MaximumFor(string propertyName);
            double IncrementFor(string propertyName);
            int DisplayOrderFor(string propertyName);
            string DisplayNameFor(string propertyName);
            string DescriptionFor(string propertyName);
            bool IsReadOnlyFor(string propertyName);
        }
    }
    

पहले विकल्प के लिए: हालांकि, संभावित वस्तु को संपादित करने के परिणामस्वरूप प्रतिबिंबित करने के लिए बाध्यकारी उचित संपत्ति की कमी है।

    private static void CreatePropertyAttribute(PropertyBuilder propertyBuilder, Type attributeType, Array parameterValues)
    {
        var parameterTypes = (from object t in parameterValues select t.GetType()).ToArray();
        ConstructorInfo propertyAttributeInfo = typeof(RangeAttribute).GetConstructor(parameterTypes);
        if (propertyAttributeInfo != null)
        {
            var customAttributeBuilder = new CustomAttributeBuilder(propertyAttributeInfo,
                parameterValues.Cast<object>().ToArray());
            propertyBuilder.SetCustomAttribute(customAttributeBuilder);
        }
    }
    private static PropertyBuilder CreateAutomaticProperty(TypeBuilder typeBuilder, PropertyInfo propertyInfo)
    {
        string propertyName = propertyInfo.Name;
        Type propertyType = propertyInfo.PropertyType;

        // Generate a private field
        FieldBuilder field = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

        // Generate a public property
        PropertyBuilder property = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType,
            null);

        // The property set and property get methods require a special set of attributes:
        const MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.HideBySig;

        // Define the "get" accessor method for current private field.
        MethodBuilder currGetPropMthdBldr = typeBuilder.DefineMethod("get_" + propertyName, getSetAttr, propertyType, Type.EmptyTypes);

        // Intermediate Language stuff...
        ILGenerator currGetIl = currGetPropMthdBldr.GetILGenerator();
        currGetIl.Emit(OpCodes.Ldarg_0);
        currGetIl.Emit(OpCodes.Ldfld, field);
        currGetIl.Emit(OpCodes.Ret);

        // Define the "set" accessor method for current private field.
        MethodBuilder currSetPropMthdBldr = typeBuilder.DefineMethod("set_" + propertyName, getSetAttr, null, new[] { propertyType });

        // Again some Intermediate Language stuff...
        ILGenerator currSetIl = currSetPropMthdBldr.GetILGenerator();
        currSetIl.Emit(OpCodes.Ldarg_0);
        currSetIl.Emit(OpCodes.Ldarg_1);
        currSetIl.Emit(OpCodes.Stfld, field);
        currSetIl.Emit(OpCodes.Ret);

        // Last, we must map the two methods created above to our PropertyBuilder to 
        // their corresponding behaviors, "get" and "set" respectively. 
        property.SetGetMethod(currGetPropMthdBldr);
        property.SetSetMethod(currSetPropMthdBldr);

        return property;

    }

    public static object EditingObject(object obj)
    {
        // Create the typeBuilder
        AssemblyName assembly = new AssemblyName("EditingWrapper");
        AppDomain appDomain = System.Threading.Thread.GetDomain();
        AssemblyBuilder assemblyBuilder = appDomain.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.Run);
        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assembly.Name);

        // Create the class
        TypeBuilder typeBuilder = moduleBuilder.DefineType("EditingWrapper",
            TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.AnsiClass |
            TypeAttributes.BeforeFieldInit, typeof(System.Object));

        Type objType = obj.GetType();
        foreach (var propertyInfo in objType.GetProperties())
        {
            string propertyName = propertyInfo.Name;
            Type propertyType = propertyInfo.PropertyType;

            // Create an automatic property
            PropertyBuilder propertyBuilder = CreateAutomaticProperty(typeBuilder, propertyInfo);

            // Set Range attribute
            CreatePropertyAttribute(propertyBuilder, typeof(Category), new[]{"My new category value"});

        }

        // Generate our type
        Type generetedType = typeBuilder.CreateType();

        // Now we have our type. Let's create an instance from it:
        object generetedObject = Activator.CreateInstance(generetedType);

        return generetedObject;
    }
}



आप क्लास लेवल (ऑब्जेक्ट नहीं) पर रनटाइम पर विशेषता मान बदल सकते हैं:

var attr = TypeDescriptor.GetProperties(typeof(UserContact))["UserName"].Attributes[typeof(ReadOnlyAttribute)] as ReadOnlyAttribute;
attr.GetType().GetField("isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(attr, username_readonly);





Links