using System; using System.ComponentModel; using System.Linq; using System.Reflection; namespace Core.Common.Gui.PropertyBag { /// /// This class represents a single property. /// public class PropertySpec { private readonly System.Reflection.PropertyInfo propertyInfo; /// /// Initializes a new instance of the class for a given /// property meta-data object. /// /// The property information. /// When is /// an index property. public PropertySpec(System.Reflection.PropertyInfo propertyInfo) { if (propertyInfo.GetIndexParameters().Length > 0) { throw new ArgumentException("Index properties are not allowed.", "propertyInfo"); } this.propertyInfo = propertyInfo; Name = propertyInfo.Name; TypeName = propertyInfo.PropertyType.AssemblyQualifiedName; var attributeList = propertyInfo.GetCustomAttributes(true).OfType().Select(attrib => attrib).ToList(); if (propertyInfo.GetSetMethod() == null) { attributeList.Add(new ReadOnlyAttribute(true)); } Attributes = attributeList.ToArray(); } /// /// Gets or sets a collection of additional s for this property. /// This can be used to specify attributes beyond those supported intrinsically by the /// class, such as /// and . /// public Attribute[] Attributes { get; set; } /// /// Gets the name of the property. /// public string Name { get; private set; } /// /// Gets the fully qualified name of the type of this property. /// public string TypeName { get; private set; } /// /// Sets the property represented by this instance of some object instance. /// /// The instance to be updated. /// The new value for the property. /// When /// /// Represented property is an index-property. /// Property setter is not available. /// does not match the target type. /// Property is an instance property but is null. /// is of incorrect type. /// An error occurred while setting the property value. The /// property indicates the reason for the error. /// /// Calling this method while property /// has no setter. public void SetValue(object instance, object newValue) { var setMethodInfo = propertyInfo.GetSetMethod(); if (setMethodInfo == null) { throw new InvalidOperationException("Property lacks public setter!"); } try { setMethodInfo.Invoke(instance, new[] { newValue }); } catch (TargetException e) { object type = instance == null ? null : instance.GetType(); var message = string.Format("Are you calling SetValue on the correct instance? Expected '{0}', but was '{1}'", propertyInfo.DeclaringType, type); throw new ArgumentException(message, "instance", e); } catch (TargetInvocationException e) { var message = string.Format("Something went wrong while setting property with value '{0}'; Check InnerException for more information.", newValue); throw new ArgumentException(message, "newValue", e); } } /// /// Gets the property value represented by this instance of some object instance. /// /// The instance that holds the property to be retrieved. /// When /// /// Represented property is an index-property. /// Property getter is not available. /// does not match the target type. /// Property is an instance property but is null. /// An error occurred while setting the property value. The /// property indicates the reason for the error. /// /// Calling this method while property /// has no getter. public object GetValue(object instance) { var getMethodInfo = propertyInfo.GetGetMethod(); if (getMethodInfo == null) { throw new InvalidOperationException("Property lacks public getter!"); } try { return getMethodInfo.Invoke(instance, new object[0]); } catch (TargetException e) { object type = instance == null ? null : instance.GetType(); var message = string.Format("Are you calling GetValue on the correct instance? Expected '{0}', but was '{1}'", propertyInfo.DeclaringType, type); throw new ArgumentException(message, "instance", e); } catch (TargetInvocationException e) { throw new ArgumentException("Something went wrong while getting property; Check InnerException for more information.", "instance", e); } } /// /// Determines whether the captured property is decorated with [is non custom expandable object property]. /// that is configured to use . /// /// Returns true if a is declared using /// , false if no match has been found or when /// the type converter inherits from . /// Custom implementations of is /// likely to have behavior that Core.Common.Gui namespace cannot account for. As /// such those properties will be considered not having the expandable object type converter. public bool IsNonCustomExpandableObjectProperty() { var typeConverterClassName = propertyInfo.GetCustomAttributes(typeof(TypeConverterAttribute), false) .OfType() .Select(tca => tca.ConverterTypeName) .Where(n => !string.IsNullOrEmpty(n)); foreach (string typeName in typeConverterClassName) { try { var type = Type.GetType(typeName); if (type != null) { if (typeof(ExpandableObjectConverter) == type) { return true; } } } catch (Exception) { // gulp } } return false; } } }