|
///////////////////////////////////////////////////////////////////////////////////////////////
/// NOTE THAT: The basic architecture and the cache-mechanism used in ExpandableObjects
/// are lifted from Stephen Taub's article in NET Matters April/May issues,
///
/// NET Matters ICustomTypeDescriptor, Part 1 -- MSDN Magazine, April 2005
/// http://msdn.microsoft.com/msdnmag/issues/05/04/NETMatters/default.aspx
/// NET Matters ICustomTypeDescriptor, Part 2 -- MSDN Magazine, May 2005
/// http://msdn.microsoft.com/msdnmag/issues/05/05/NETMatters/default.aspx
///
/// My thanks to the author for the information/techniques covered therein and permission to
/// reuse code for non-profit purposes.
////////////////////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.Diagnostics;
namespace ExpandableObjects
{
public abstract class ExpandableObject : IDisposable
{
#region Fields
private ExpandablePropertiesTypeDescriptionProvider _provider = null;
#endregion
#region WAYOUT
~ExpandableObject()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Ensure that subclasses call this implementation of Dispose. Failing this, they MUST call:
/// TypeDescriptor.RemoveProvider to remove the ExpandablePropertiesTypeDescriptionProvider for this instance
/// </summary>
/// <param name="bDisposing"></param>
protected virtual void Dispose(bool bDisposing)
{
if(bDisposing){}
if(_provider != null)
TypeDescriptor.RemoveProvider(_provider, this);
_provider = null;
}
#endregion
#region CTOR
/// <summary>
/// Construct the base-class object and register it's own ExpandablePropertiesTypeDescriptionProvider which
/// will return (where applicable) an ExpandableObjectConverter whenever a TypeConverter is requested for
/// the instance.
/// Note that this is an INSTANCE-LEVEL extender.
/// </summary>
protected ExpandableObject()
{
_provider = new ExpandablePropertiesTypeDescriptionProvider(GetType());
TypeDescriptor.AddProvider(_provider, this);
}
#endregion
}
public class DefaultPropertyDescriptor : PropertyDescriptor
{
#region The Described Property
private PropertyInfo _prop;
public PropertyInfo Prop { get { return _prop; } }
#endregion
#region Read-Only / Read-Write
public override bool IsReadOnly
{
get
{
if(this.Prop.GetSetMethod() == null)
return true;
return false;
}
}
#endregion
#region PropertyDescriptor pass-thrus
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 _prop.DeclaringType; } }
public override Type PropertyType { get { return _prop.PropertyType; } }
#endregion
#region CTOR
public DefaultPropertyDescriptor(PropertyInfo prop) : base(prop.Name, (Attribute[])prop.GetCustomAttributes(typeof(Attribute), true))
{
_prop = prop;
}
#endregion
#region GetValue/SetValue
public override object GetValue(object component)
{
return _prop.GetValue(component, null);
}
public override void SetValue(object component, object value)
{
_prop.SetValue(component, value, null);
OnValueChanged(component, EventArgs.Empty);
}
#endregion
#region Equality
public override int GetHashCode() { return _prop.GetHashCode(); }
public override bool Equals(object obj)
{
DefaultPropertyDescriptor other = obj as DefaultPropertyDescriptor;
return other != null && other._prop.Equals(_prop);
}
#endregion
}
/// <summary>
/// This class is a mofified version of a class from Stephen Taub's NET Matters, ICustomTypeDescriptor article in NET Mattter April/May 2005
/// </summary>
public class ExpandablePropertiesTypeDescriptionProvider : TypeDescriptionProvider
{
#region Fields
private TypeDescriptionProvider _baseProvider;
private PropertyDescriptorCollection _propCache;
private FilterCache _filterCache;
#endregion
#region CTOR
public ExpandablePropertiesTypeDescriptionProvider(Type t)
{
_baseProvider = TypeDescriptor.GetProvider(t);
}
#endregion
#region GET TYPE DESCRIPTOR
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
return new ExpandablePropertiesTypeDescriptor(this, _baseProvider.GetTypeDescriptor(objectType, instance), objectType);
}
#endregion
#region FilterCache
/// <summary>
/// This class is a direct lift from Stephen Taub's NET Matters, ICustomTypeDescriptor article in NET Mattter April/May 2005
/// </summary>
private class FilterCache
{
public Attribute[] Attributes;
public PropertyDescriptorCollection FilteredProperties;
/// <summary>
/// Perform straight-match on attributes: non-null, same-length, same contents
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public bool IsValid(Attribute[] other)
{
if (other == null || Attributes == null) return false;
if (Attributes.Length != other.Length) return false;
for (int i = 0; i < other.Length; i++)
{
if (!Attributes[i].Match(other[i])) return false;
}
return true;
}
}
#endregion
#region Dialgnostics
[Conditional("DEBUG")]
private static void DumpAttributes(PropertyInfo p, Attribute[] a)
{
string accessMods = string.Format("\nACCESS: {0}", p.PropertyType.IsPublic ? "public" : "private");
Debug.WriteLine("DUMPING ATTRIBUTES for: " + p.Name + accessMods);
foreach (Attribute aa in a)
Debug.WriteLine(aa);
}
#endregion
#region ExpandabPropertyDescriptor
public class ExpandabPropertyDescriptor : DefaultPropertyDescriptor
{
#region CTOR
public ExpandabPropertyDescriptor(PropertyInfo prop) : base(prop)
{ }
#endregion
#region REPLACE TypeConverter with ExpandableObjectConverter
private TypeConverter conv = null;
public override TypeConverter Converter
{
get
{
if (conv == null)
if (base.Converter.GetType() != typeof(TypeConverter))
{
// if there is a custom-converter already - return it
conv = base.Converter;
}
else // force an expandable-converter for this property
conv = new ExpandableObjectConverter();
return conv;
}
}
#endregion
}
#endregion
#region ExpandablePropertiesTypeDescriptor
/// <summary>
/// This class is a modified version of a class from Stephen Taub's NET Matters, ICustomTypeDescriptor article:
/// </summary>
private class ExpandablePropertiesTypeDescriptor : CustomTypeDescriptor
{
private Type _objectType;
private ExpandablePropertiesTypeDescriptionProvider _provider;
public ExpandablePropertiesTypeDescriptor(ExpandablePropertiesTypeDescriptionProvider provider, ICustomTypeDescriptor descriptor, Type objectType)
: base(descriptor)
{
if (provider == null) throw new ArgumentNullException("provider");
if (descriptor == null) throw new ArgumentNullException("descriptor");
if (objectType == null) throw new ArgumentNullException("objectType");
_objectType = objectType;
_provider = provider;
}
public override PropertyDescriptorCollection GetProperties()
{
return GetProperties(null);
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
// Retrieve cached properties and filtered properties
bool filtering = attributes != null && attributes.Length > 0;
FilterCache cache = _provider._filterCache;
PropertyDescriptorCollection props = _provider._propCache;
// Use a cached version if we can
if (filtering && cache != null && cache.IsValid(attributes))
return cache.FilteredProperties;
else if (!filtering && props != null)
return props;
// Otherwise, create the property collection
props = new PropertyDescriptorCollection(null);
foreach (PropertyInfo p in _objectType.GetProperties())
{
// FieldInfo[] pflds = p.PropertyType.GetFields();
PropertyInfo[] pprops = p.PropertyType.GetProperties();
PropertyDescriptor desc = null;
//// if the property in not an array and has public fields or properties - use ExpandablePropertyDescriptor
if (!p.PropertyType.HasElementType && ((pprops.Length > 0)))
desc = new ExpandabPropertyDescriptor(p);
else
desc = new DefaultPropertyDescriptor(p);
if (!filtering || desc.Attributes.Contains(attributes))
props.Add(desc);
}
// Store the updated properties
if (filtering)
{
cache = new FilterCache();
cache.FilteredProperties = props;
cache.Attributes = attributes;
_provider._filterCache = cache;
}
else _provider._propCache = props;
// Return the computed properties
return props;
}
}
#endregion
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
The whole world's a circus... don't you be the clown