Fileds to Properties Proxy

988 Views Asked by At

let's imagine i have the following classes that i am not allowed to change:

public class BaseType
{
    public UInt32 m_baseMember = 1;
    public bool m_baseMemberBool = false;
}

public class ComposedType
{
    public ComposedType()
    {
        m_baseData = new BaseType();
    }
    public UInt32 m_newMember = 2;


    public BaseType m_baseData;
}

Now i want to edit those data by putting it in an PropertyGrid. I created two Wrapper classes like those ( http://msdn.microsoft.com/en-us/magazine/cc163816.aspx )

    public class FieldsToPropertiesProxyTypeDescriptor : ICustomTypeDescriptor 
{
    private object _target; 
    // object to be described 
    public FieldsToPropertiesProxyTypeDescriptor(object target) 
    { 
        if (target == null) 
            throw new ArgumentNullException("target"); 
        _target = target; 
    }

    public object GetProxiedObject()
    {
        return _target;
    }

    object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) 
    { 
        return _target; 
        // properties belong to the target object 
    }

    AttributeCollection ICustomTypeDescriptor.GetAttributes() 
    { 
        // Gets the attributes of the target object 
        return TypeDescriptor.GetAttributes(_target, true);
    } 

    string ICustomTypeDescriptor.GetClassName() 
    { 
        // Gets the class name of the target object 
        return TypeDescriptor.GetClassName(_target, true);
    }

    string ICustomTypeDescriptor.GetComponentName()
    {
        return TypeDescriptor.GetComponentName(_target, true);
    }

    TypeConverter ICustomTypeDescriptor.GetConverter()
    {
        return TypeDescriptor.GetConverter(_target, true);
    }

    EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
    {
        return TypeDescriptor.GetDefaultEvent(_target, true);
    }

    PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
    {
        return TypeDescriptor.GetDefaultProperty(_target, true);
    }

    object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
    {
        return TypeDescriptor.GetEditor(_target, editorBaseType);
    }

    EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
    {
        return TypeDescriptor.GetEvents(_target, attributes);
    }

    EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
    {
        return TypeDescriptor.GetEvents( _target );
    }

    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() 
    {
        return ((ICustomTypeDescriptor)this).GetProperties(null); 
    }  

    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties( Attribute[] attributes) 
    { 
        bool filtering = (attributes != null && attributes.Length > 0); 
        PropertyDescriptorCollection props = new PropertyDescriptorCollection(null);
        foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(_target, attributes, true)) 
        { 
            props.Add(prop); 
        }
        foreach (FieldInfo field in _target.GetType().GetFields()) 
        {             
            FieldPropertyDescriptor fieldDesc = new FieldPropertyDescriptor(field);
            if (!filtering || fieldDesc.Attributes.Contains(attributes)) 
                props.Add(fieldDesc);
        }                       
        return props; 
    } 
}

public class FieldPropertyDescriptor : PropertyDescriptor
{ 
    private FieldInfo _field; 
    public FieldPropertyDescriptor(FieldInfo field) : base(field.Name, (Attribute[])field.GetCustomAttributes(typeof(Attribute), true)) 
    { 
        _field = field; 
    } 

    public FieldInfo Field 
    { 
        get { return _field; } 
    } 

    public override bool Equals(object obj) 
    { 
        FieldPropertyDescriptor other = obj as FieldPropertyDescriptor; 
        return other != null && other._field.Equals(_field); 
    } 

    public override int GetHashCode() 
    { 
        return _field.GetHashCode(); 
    } 

    public override bool IsReadOnly 
    { 
        get { return false; } 
    }

    public override AttributeCollection Attributes
    {
        get
        {
            if (_field.FieldType.IsClass || _field.FieldType.IsArray)
            {                   
                Attribute[] expandable = new Attribute[1];
                expandable[0] = new ExpandableObjectAttribute();
                return AttributeCollection.FromExisting(base.Attributes, expandable);
            }
            return base.Attributes;
        }
    }

    public override void ResetValue(object component)
    {

    } 

    public override bool CanResetValue(object component) 
    { 
        return false; 
    } 

    public override bool ShouldSerializeValue(object component) 
    { 
        return true; 
    } 

    public override Type ComponentType 
    { 
        get { return _field.DeclaringType; } 
    } 

    public override Type PropertyType 
    { 
        get { return _field.FieldType; } 
    }

    public override object GetValue(object component) 
    {
        if (component is FieldsToPropertiesProxyTypeDescriptor)
        {
            FieldsToPropertiesProxyTypeDescriptor proxy = (FieldsToPropertiesProxyTypeDescriptor)component;
            return _field.GetValue(proxy.GetProxiedObject()); 
        }             
        return _field.GetValue(component); 
    } 

    public override void SetValue(object component, object value) 
    {
        if (component is FieldsToPropertiesProxyTypeDescriptor)
        {
            FieldsToPropertiesProxyTypeDescriptor proxy = (FieldsToPropertiesProxyTypeDescriptor)component;
            _field.SetValue(proxy.GetProxiedObject(), value);
            OnValueChanged(proxy.GetProxiedObject(), EventArgs.Empty);
            return;
        }  
        _field.SetValue(component, value); 
        OnValueChanged(component, EventArgs.Empty); 
    } 
}

I can see and edit the 'm_newMember' in the PropertyGrid but i need to wrap the access to 'm_baseData' via FieldsToPropertiesProxyTypeDescriptor. How could i achieve this. Or is there a better way to wrap fields into Properties?

1

There are 1 best solutions below

0
On

You can change attributes of a given class at runtime without changing that class. So you could write a custom TypeConverter and set it to your classes, something like this:

    TypeDescriptor.AddAttributes(typeof(ComposedType), new TypeConverterAttribute(typeof(FieldsExpandableObjectConverter)));
    TypeDescriptor.AddAttributes(typeof(BaseType), new TypeConverterAttribute(typeof(FieldsExpandableObjectConverter)));

With the following TypeConverter (re-using your FieldDescriptor class):

public class FieldsExpandableObjectConverter : ExpandableObjectConverter
{
    public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
    {
        List<PropertyDescriptor> properties = new List<PropertyDescriptor>(base.GetProperties(context, value, attributes).OfType<PropertyDescriptor>());
        if (value != null)
        {
            foreach (FieldInfo field in value.GetType().GetFields())
            {
                FieldPropertyDescriptor fieldDesc = new FieldPropertyDescriptor(field);
                {
                    properties.Add(fieldDesc);
                }
            }
        }
        return new PropertyDescriptorCollection(properties.ToArray());
    }
}