Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using System.Windows;
- namespace DependencyPropertyFactory
- {
- /// <summary>
- /// A dependency property factory to simplify creating and managing dependency properties for a certain type.
- /// </summary>
- /// <typeparam name="T">The type used as identifier for the dependency properties.</typeparam>
- /// <author>Steven Jeuris</author>
- public class DependencyPropertyFactory<T>
- {
- private readonly Type m_ownerType;
- /// <summary>
- /// A list containing the dependency properties.
- /// </summary>
- public Dictionary<T, DependencyProperty> Properties
- {
- get; private set;
- }
- private readonly Dictionary<T, DependencyPropertyKey> m_readOnlyProperties
- = new Dictionary<T, DependencyPropertyKey>();
- /// <summary>
- /// Create a new dependency property container for a specific type.
- /// </summary>
- /// <param name="ownerType">The owner type of the dependency properties.</param>
- public DependencyPropertyFactory(Type ownerType)
- {
- Properties = new Dictionary<T, DependencyProperty>();
- m_ownerType = ownerType;
- // Get all dependency property attributes.
- const BindingFlags ALL_PROPERTIES = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
- foreach (PropertyInfo property in m_ownerType.GetProperties(ALL_PROPERTIES))
- {
- object[] attribute = property.GetCustomAttributes(typeof (DependencyPropertyAttribute), false);
- if (attribute != null && attribute.Length == 1)
- {
- DependencyPropertyAttribute dependency = (DependencyPropertyAttribute)attribute[0];
- // Check whether the ID corresponds to the ID required for this container.
- if (!(dependency.DependencyPropertyID is T))
- {
- break;
- }
- // Set dependency property parameters.
- string name = dependency.Name ?? dependency.DependencyPropertyID.ToString();
- Type propertyType = property.PropertyType;
- // When no default value is set, and it is a value type, use the default value.
- object defaultValue = dependency.DefaultValue ??
- (propertyType.IsValueType ? Activator.CreateInstance( propertyType ) : null);
- // By default, readonly when setter is private.
- MethodInfo setMethod = property.GetSetMethod();
- bool readOnly = dependency.ReadOnlySet ?
- dependency.ReadOnly : (setMethod == null || setMethod.IsPrivate);
- T id = (T)dependency.DependencyPropertyID;
- // Find changed callback.
- PropertyChangedCallback changedCallback =
- (PropertyChangedCallback)CreateCallbackDelegate<DependencyPropertyChangedAttribute>(id);
- CoerceValueCallback coerceCallback =
- (CoerceValueCallback)CreateCallbackDelegate<DependencyPropertyCoerceAttribute>(id);
- if (readOnly)
- {
- CreateReadOnlyDependencyProperty(
- id, name, propertyType, defaultValue,
- changedCallback, coerceCallback
- );
- }
- else
- {
- CreateDependencyProperty(
- id, name, propertyType, defaultValue,
- changedCallback, coerceCallback
- );
- }
- }
- }
- }
- /// <summary>
- /// Create a callback delegate for a dependency property.
- /// </summary>
- /// <typeparam name="TCallbackAttribute">The callback attribute type.</typeparam>
- /// <param name="id">The ID of the dependency property.</param>
- /// <returns>A delegate which can be used as a callback.</returns>
- private Delegate CreateCallbackDelegate<TCallbackAttribute>(object id)
- where TCallbackAttribute : AbstractDependencyPropertyCallbackAttribute
- {
- return
- (from method in m_ownerType.GetMethods()
- let methodAttributes =
- method.GetCustomAttributes( typeof( TCallbackAttribute ), false )
- where methodAttributes != null && methodAttributes.Length == 1
- let changed =
- (TCallbackAttribute)methodAttributes[ 0 ]
- where changed.DependencyPropertyID.Equals( id )
- select Delegate.CreateDelegate( changed.CallbackType, method )).FirstOrDefault();
- }
- private void CreateReadOnlyDependencyProperty(
- T identifier,
- string name,
- Type propertyType,
- object defaultValue,
- PropertyChangedCallback changedCallback,
- CoerceValueCallback coerceCallback)
- {
- DependencyPropertyKey propertyKey =
- DependencyProperty.RegisterReadOnly(
- name,
- propertyType,
- m_ownerType,
- new PropertyMetadata(defaultValue, changedCallback, coerceCallback)
- );
- m_readOnlyProperties.Add(identifier, propertyKey);
- DependencyProperty property = propertyKey.DependencyProperty;
- Properties.Add(identifier, property);
- }
- private void CreateDependencyProperty(
- T identifier,
- string name,
- Type propertyType,
- object defaultValue,
- PropertyChangedCallback changedCallback,
- CoerceValueCallback coerceCallback)
- {
- DependencyProperty property =
- DependencyProperty.Register(
- name,
- propertyType,
- m_ownerType,
- new PropertyMetadata(defaultValue, changedCallback, coerceCallback)
- );
- Properties.Add(identifier, property);
- }
- /// <summary>
- /// Returns the dependency property for a given ID.
- /// </summary>
- /// <param name="id">The ID of the dependency propert. (enum type of the class type parameter)</param>
- /// <returns>The dependency property for the given ID.</returns>
- public DependencyProperty this[T id]
- {
- get
- {
- if (!Properties.ContainsKey(id))
- {
- throw new KeyNotFoundException(
- "The dependency property with the specified key doesn't exist. " +
- "Did you forget to add a DependencyPropertyAttribute?" );
- }
- return Properties[ id ];
- }
- }
- /// <summary>
- /// Set the value of a readonly property.
- /// </summary>
- /// <param name="o">The dependency object on which to set the value.</param>
- /// <param name="property">The property to set.</param>
- /// <param name="value">The new value for the property.</param>
- public void SetReadOnlyProperty(DependencyObject o, T property, object value)
- {
- if (!m_readOnlyProperties.ContainsKey(property))
- {
- throw new KeyNotFoundException(
- "The readonly dependency property with the specified key doesn't exist. " +
- "Is the DependencyPropertyAttribute set to ReadOnly?");
- }
- DependencyPropertyKey key = m_readOnlyProperties[ property ];
- o.SetValue( key, value );
- }
- }
- /// <summary>
- /// When applied to a member,
- /// specifies how the dependency property should be created by the DependencyPropertyContainer.
- /// </summary>
- /// <author>Steven Jeuris</author>
- public abstract class AbstractDependencyPropertyAttribute : Attribute
- {
- /// <summary>
- /// The ID of the dependency property.
- /// </summary>
- public object DependencyPropertyID
- {
- get;
- private set;
- }
- /// <summary>
- /// Create a new attribute which specifies how the dependency property should be created.
- /// </summary>
- /// <param name="id">The ID of the dependency property.</param>
- protected AbstractDependencyPropertyAttribute(object id)
- {
- DependencyPropertyID = id;
- }
- }
- /// <summary>
- /// When applied to the property of a type,
- /// specifies how the dependency property should be created by the DependencyPropertyContainer.
- /// </summary>
- /// <author>Steven Jeuris</author>
- [AttributeUsage(AttributeTargets.Property, AllowMultiple=false)]
- public class DependencyPropertyAttribute : AbstractDependencyPropertyAttribute
- {
- /// <summary>
- /// The name to use for the dependency property.
- /// </summary>
- public string Name
- {
- get; set;
- }
- /// <summary>
- /// Set to true if ReadOnly is set.
- /// </summary>
- public bool ReadOnlySet
- {
- get; private set;
- }
- /// <summary>
- /// Should the dependency property be a readonly dependency property or not.
- /// By default, true when the setter is private, false otherwise.
- /// </summary>
- public bool ReadOnly
- {
- get; private set;
- }
- /// <summary>
- /// The default value for the dependency property. Should be of the same type as the property.
- /// </summary>
- public object DefaultValue
- {
- get; set;
- }
- /// <summary>
- /// Create a new attribute which gives information about the dependency property to create for a given property.
- /// </summary>
- /// <param name="id">The ID of the dependency property. This should be an enum.</param>
- public DependencyPropertyAttribute(object id) : base(id)
- {
- }
- /// <summary>
- /// Create a new attribute which gives information about the dependency property to create for a given property.
- /// </summary>
- /// <param name="id">The ID of the dependency property.</param>
- /// <param name="readOnly">Should the dependency property be a readonly dependency property.</param>
- public DependencyPropertyAttribute(object id, bool readOnly) : this(id)
- {
- ReadOnly = readOnly;
- ReadOnlySet = true;
- }
- }
- /// <summary>
- /// When applied to a member,
- /// specifies a callback for a dependency property.
- /// </summary>
- /// <author>Steven Jeuris</author>
- public abstract class AbstractDependencyPropertyCallbackAttribute : AbstractDependencyPropertyAttribute
- {
- /// <summary>
- /// The delegate type for the callback.
- /// </summary>
- public abstract Type CallbackType
- {
- get;
- }
- /// <summary>
- /// Create a new attribute which specifies how the dependency property should be created.
- /// </summary>
- /// <param name="id">The ID of the dependency property.</param>
- protected AbstractDependencyPropertyCallbackAttribute(object id) : base(id)
- {
- }
- }
- /// <summary>
- /// When applied to a PropertyChangedCallback method,
- /// specifies the changed callback for a dependency property.
- /// </summary>
- /// <author>Steven Jeuris</author>
- [AttributeUsage(AttributeTargets.Method, AllowMultiple=false)]
- public class DependencyPropertyChangedAttribute : AbstractDependencyPropertyCallbackAttribute
- {
- /// <summary>
- /// Create a new attribute to assign a function as changed callback to a given dependency property ID.
- /// </summary>
- /// <param name="id">The ID of the dependency property.</param>
- public DependencyPropertyChangedAttribute(object id) : base(id)
- {
- }
- /// <summary>
- /// The delegate type for this callback.
- /// </summary>
- public override Type CallbackType
- {
- get { return typeof( PropertyChangedCallback ); }
- }
- }
- /// <summary>
- /// When applied to a PropertyChangedCallback method,
- /// specifies the coerce callback for a dependency property.
- /// </summary>
- /// <author>Steven Jeuris</author>
- [AttributeUsage(AttributeTargets.Method, AllowMultiple=false)]
- public class DependencyPropertyCoerceAttribute : AbstractDependencyPropertyCallbackAttribute
- {
- /// <summary>
- /// Create a new attribute to assign a function as coerce callback to a given dependency property ID.
- /// </summary>
- /// <param name="id">The ID of the dependency property.</param>
- public DependencyPropertyCoerceAttribute(object id) : base(id)
- {
- }
- /// <summary>
- /// The delegate type for this callback.
- /// </summary>
- public override Type CallbackType
- {
- get { return typeof (CoerceValueCallback); }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement