8

I want to pass in the attribute name and return the value. This will be in a generic util and it will not know the attribute type.

Update This is the actual working code if someone needs to do this. I needed a way to have the core code parse the attributes without knowing what they were.

public void LoadPropertiesToGrid(BaseGridPropertyModel model)
{
    foreach (PropertyInfo prop in ReflectionUtil.FindPublicPropeties(model))
    {
        object editTyp = ReflectionUtil.GetPropertyAttributes(prop, "EditorType"); 
        object rowIdx = ReflectionUtil.GetPropertyAttributes(prop, "ColIndex");
        object name = ReflectionUtil.GetPropertyAttributes(prop, "Name");
        object visible = ReflectionUtil.GetPropertyAttributes(prop, "Visible");
        ConfigureColumn((string) name, (int) rowIdx, (bool) visible, (string) editTyp);
     }
}
[Serializable]
public class CanvasPropertiesViewModel : BaseGridPropertyModel
{
    [PropertiesGrid(Name = "TEsting Name 0", ColIndex = 0)]
    public string StringData1 { get; set; }

    [PropertiesGrid(Name = "TEsting Name 2", ColIndex = 2)]
    public string StringData2 { get; set; }

    [PropertiesGrid(Name = "TEsting Name 1", ColIndex = 1)]
    public string StringData3 { get; set; }
}
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
public sealed class PropertiesGridAttribute : Attribute
{
    /// <summary>
    /// Editor type
    /// </summary>
    public Type EditorType { get; set; }

    /// <summary>
    /// Sets Column Index
    /// </summary>
    public int ColIndex { get; set; }

    /// <summary>
    /// Visible to Grid
    /// </summary>
    public bool Visible { get; set; }

    /// <summary>
    /// Dispaly Name of the property
    /// </summary>
    public string Name { get; set; }
}
public static object GetPropertyAttributes(PropertyInfo prop, string attributeName)
{
    // look for an attribute that takes one constructor argument
    foreach (CustomAttributeData attribData in prop.GetCustomAttributesData())
    {
        string typeName = attribData.Constructor.DeclaringType.Name;
        //if (attribData.ConstructorArguments.Count == 1 && (typeName == attributeName || typeName == attributeName + "Attribute"))
        //{
        //    return attribData.ConstructorArguments[0].Value;
        //}
        foreach (CustomAttributeNamedArgument att in attribData.NamedArguments)
        {
            if(att.GetPropertyValue<string>("MemberName") == attributeName)
            {
                return att.TypedValue.Value;
            }
        }
    }
    return null;
}

//PropertyExpressionParser 
public static TRet GetPropertyValue<TRet>(this object obj, string propertyPathName)
{
    if (obj == null)
    {
        throw new ArgumentNullException("obj");
    }

    string[] parts = propertyPathName.Split('.');
    string path = propertyPathName;
    object root = obj;

    if (parts.Length > 1)
    {
        path = parts[parts.Length - 1];
        parts = parts.TakeWhile((p, i) => i < parts.Length - 1).ToArray();
        string path2 = String.Join(".", parts);
        root = obj.GetPropertyValue<object>(path2);
    }

    var sourceType = root.GetType();
    var value = (TRet)sourceType.GetProperty(path).GetValue(root, null);
    return value;
}
Paul Fleming
  • 24,238
  • 8
  • 76
  • 113
Steve Coleman
  • 1,987
  • 2
  • 17
  • 28

2 Answers2

7

If you mean "given an attribute that takes one parameter, give me that value", for example:

[DisplayName("abc")] <===== "abc"
[Browsable(true)] <===== true

then this is easiest in .NET 4.5, via the new CustomAttributeData API:

using System.ComponentModel;
using System.Reflection;

public static class Program
{
    static void Main()
    {
        PropertyInfo prop = typeof(Foo).GetProperty("Bar");
        var val = GetPropertyAttributes(prop, "DisplayName");
    }
    public static object GetPropertyAttributes(PropertyInfo prop, string attributeName)
    {
        // look for an attribute that takes one constructor argument
        foreach(CustomAttributeData attribData in prop.GetCustomAttributesData()) 
        {
            string typeName = attribData.Constructor.DeclaringType.Name;
            if(attribData.ConstructorArguments.Count == 1 &&
                (typeName == attributeName || typeName == attributeName + "Attribute"))
            {
                return attribData.ConstructorArguments[0].Value;
            }
        }
        return null;
    }
}

class Foo
{
    [DisplayName("abc")]
    public string Bar { get; set; }
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • This works but it was not quite what I needed. I updated my questions with the answer based on your code. Thanks you got what I needed! – Steve Coleman Oct 26 '12 at 17:20
0

As Mark said, Attributes don't have values, per se. You could, however, create an interface with with a Value property which you implement for each of your custom Attributes. Then all you have to do is verify that the Attribute implements you interface and then get the Value.

Example:

public static object GetPropertyAttributes(PropertyInfo prop, string attributeName)
{
    object[] attrs = prop.GetCustomAttributes(true);

    foreach (object attr in attrs)
    {
        //THIS SHOULD BE REFLECTION
        if (attr is IMyCustomAttribute) // check if attr implements interface (you may have to reflect to get this)
        {
            return (attr as IMyCustomAttribute).Value
        }

    }
}

// Your interface
public interface IMyCustomAttribute
{
    object Value { get; set; }
}

// Attribute implementing interface
public SomeCustomAttribute : Attribute, IMyCustomAttribute
{
    object Value { get; set; }

    // Other attribute code goes here
}
Steve Konves
  • 2,648
  • 3
  • 25
  • 44
  • This would put me in the same position of knowing the interface type. Though I could create the interface in the core code to access the values... – Steve Coleman Oct 25 '12 at 20:22
  • In essence, knowing that a class (in this can a custom attribute) has a Value property is the same as declaring an interface with a Value property. If you don't have the ability to implement an interface with your custom attributes, you can still use reflection to find a property named Value. – Steve Konves Oct 25 '12 at 20:28
  • Yep, That's what I did. Thanks for the help. – Steve Coleman Oct 26 '12 at 17:21