using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using Core.Common.Gui.Attributes; using Core.Common.Gui.Forms.PropertyGridView; namespace Core.Common.Gui.PropertyBag { /// /// Defines a custom type descriptor for an object to be used as view-model for . /// It processes the special attributes defined in Core.Common.Gui.Attributes /// to dynamically affect property order or adding/removing . /// public class DynamicPropertyBag : ICustomTypeDescriptor { /// /// Instantiates a new instance of , wrapping another /// object an exposing properties for that object. /// /// The object to be wrapped. public DynamicPropertyBag(object propertyObject) { Properties = new PropertySpecCollection(); Initialize(propertyObject); } /// /// Gets the collection of properties contained within this . /// public PropertySpecCollection Properties { get; private set; } /// /// Gets the object wrapped inside this /// public object WrappedObject { get; private set; } public override string ToString() { return WrappedObject.ToString(); } /// /// Raises the GetValue event. /// /// A PropertySpecEventArgs that contains the event data. /// internal void OnGetValue(PropertySpecEventArgs e, PropertySpec propertySpec) { var attributeList = new List(); attributeList.AddRange(propertySpec.Attributes.ToList()); //check all of the attributes: if we find a dynamic one, evaluate it and possibly add/overwrite a static attribute foreach (Attribute customAttribute in propertySpec.Attributes) { if (customAttribute is DynamicReadOnlyAttribute) { attributeList.RemoveAll(x => x is ReadOnlyAttribute); if (DynamicReadOnlyAttribute.IsReadOnly(WrappedObject, propertySpec.Name)) { //condition is true: the dynamic attribute should be applied (as static attribute) attributeList.Add(new ReadOnlyAttribute(true)); //add static read only attribute } } if (customAttribute is DynamicVisibleAttribute) { attributeList.RemoveAll(x => x is BrowsableAttribute); if (!DynamicVisibleAttribute.IsVisible(WrappedObject, propertySpec.Name)) { attributeList.Add(new BrowsableAttribute(false)); } } } propertySpec.Attributes = attributeList.ToArray(); var propertyInfo = WrappedObject.GetType().GetProperty(propertySpec.Name); var value = propertyInfo.GetValue(WrappedObject, null); var isNestedPropertiesObject = IsNestedExpandablePropertiesObject(propertyInfo); // if nested properties object, wrap in DynamicPropertyBag to provide support for things like DynamicReadOnly e.Value = isNestedPropertiesObject ? new DynamicPropertyBag(value) : value; } private void Initialize(object propertyObject) { WrappedObject = propertyObject; foreach (var propertyInfo in propertyObject.GetType().GetProperties().OrderBy(x => x.MetadataToken).ToArray()) { Properties.Add(new PropertySpec(propertyInfo)); } } private bool IsNestedExpandablePropertiesObject(System.Reflection.PropertyInfo propertyInfo) { try { var typeConverterAttributes = propertyInfo.GetCustomAttributes(typeof(TypeConverterAttribute), false); foreach (TypeConverterAttribute typeConverterAttribute in typeConverterAttributes) { var typeString = typeConverterAttribute.ConverterTypeName; var type = Type.GetType(typeString); if (type != null) { if (typeof(ExpandableObjectConverter) == type) { return true; } } } } catch (Exception) { //gulp } return false; } #region ICustomTypeDescriptor explicit interface definitions #region Implementations delegated to System.ComponentModel.TypeDescriptor public AttributeCollection GetAttributes() { return TypeDescriptor.GetAttributes(this, true); } public string GetClassName() { return TypeDescriptor.GetClassName(this, true); } public string GetComponentName() { return TypeDescriptor.GetComponentName(this, true); } public System.ComponentModel.TypeConverter GetConverter() { return TypeDescriptor.GetConverter(this, true); } public EventDescriptor GetDefaultEvent() { return TypeDescriptor.GetDefaultEvent(this, true); } public object GetEditor(Type editorBaseType) { return TypeDescriptor.GetEditor(this, editorBaseType, true); } public EventDescriptorCollection GetEvents() { return TypeDescriptor.GetEvents(this, true); } public EventDescriptorCollection GetEvents(Attribute[] attributes) { return TypeDescriptor.GetEvents(this, attributes, true); } public PropertyDescriptorCollection GetProperties() { return GetProperties(new Attribute[0]); } #endregion public PropertyDescriptor GetDefaultProperty() { return Properties.Count > 0 ? new PropertySpecDescriptor(Properties[0], this, Properties[0].Name, null) : null; } public PropertyDescriptorCollection GetProperties(Attribute[] attributes) { // Rather than passing this function on to the default TypeDescriptor, // which would return the actual properties of PropertyBag, I construct // a list here that contains property descriptors for the elements of the // Properties list in the bag. var props = new List(); var propsToOrder = new List>(); foreach (PropertySpec property in Properties) { var attrs = new ArrayList(); // Additionally, append the custom attributes associated with the // PropertySpec, if any. if (property.Attributes != null) { attrs.AddRange(property.Attributes); } Attribute[] attrArray = (Attribute[])attrs.ToArray(typeof(Attribute)); // Create a new property descriptor for the property item, and add // it to the list. var pd = new PropertySpecDescriptor(property, this, property.Name, attrArray); var propertyOrderAttribute = property.Attributes != null ? property.Attributes.OfType().FirstOrDefault() : null; if (propertyOrderAttribute != null) { propsToOrder.Add(new Tuple(propertyOrderAttribute.Order, pd)); } else { props.Add(pd); } } var orderedProperties = propsToOrder.OrderBy(p => p.Item1).Select(p => p.Item2).ToList(); // Convert the list of PropertyDescriptors to a collection that the // ICustomTypeDescriptor can use, and return it. var browsableAttribute = attributes.OfType().FirstOrDefault(); var propertySpecDescriptors = (browsableAttribute != null) ? orderedProperties.Concat(props).Where(p => p.IsBrowsable == browsableAttribute.Browsable) : orderedProperties.Concat(props); return new PropertyDescriptorCollection(propertySpecDescriptors.ToArray()); } public object GetPropertyOwner(PropertyDescriptor pd) { return this; } #endregion } }