Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /// <summary>
- /// A dependency property factory to simplify creating and managing dependency properties for a certain type.
- /// </summary>
- /// <typeparam name="T">An enum used to identify the dependency properties.</typeparam>
- /// <author>Steven Jeuris</author>
- public class DependencyPropertyFactory<T>
- {
- private readonly Type m_ownerType;
- private readonly bool m_enforceWpfConvention = true;
- /// <summary>
- /// A list containing the dependency properties.
- /// TODO: Make this a readonly collection.
- /// </summary>
- public Dictionary<T, global::System.Windows.DependencyProperty> Properties
- {
- get; private set;
- }
- private readonly Dictionary<T, DependencyPropertyKey> m_readOnlyProperties = new Dictionary<T, DependencyPropertyKey>();
- /// <summary>
- /// Create a new dependency property factory for a specific set of properties.
- /// When naming conventions aren't followed, an exception is thrown.
- /// (http://msdn.microsoft.com/en-us/library/bb613563.aspx)
- /// The type which encloses the type parameter is used as the owner type.
- /// </summary>
- public DependencyPropertyFactory() : this(true)
- {
- }
- /// <summary>
- /// Create a new dependency property factory for a specific set of properties.
- /// The type which encloses the type parameter is used as the owner type.
- /// </summary>
- /// <param name="enforceNamingConventions">
- /// Whether or not to throw exceptions when the naming conventions aren't followed.
- /// See http://msdn.microsoft.com/en-us/library/bb613563.aspx.
- /// </param>
- public DependencyPropertyFactory(bool enforceNamingConventions) : this(null, enforceNamingConventions)
- {
- }
- /// <summary>
- /// Create a new dependency property factory for a specific set of properties.
- /// When naming conventions aren't followed, an exception is thrown.
- /// (http://msdn.microsoft.com/en-us/library/bb613563.aspx)
- /// </summary>
- /// <param name="ownerType">The owner type of the dependency properties.</param>
- public DependencyPropertyFactory(Type ownerType) : this(ownerType, true)
- {
- }
- /// <summary>
- /// Create a new dependency property factory for a specific set of properties.
- /// </summary>
- /// <param name="ownerType">The owner type of the dependency properties.</param>
- /// <param name="enforceNamingConventions">
- /// Whether or not to throw exceptions when the naming conventions aren't followed.
- /// See http://msdn.microsoft.com/en-us/library/bb613563.aspx.
- /// </param>
- public DependencyPropertyFactory(Type ownerType, bool enforceNamingConventions)
- {
- Properties = new Dictionary<T, global::System.Windows.DependencyProperty>();
- m_ownerType = ownerType;
- m_enforceWpfConvention = enforceNamingConventions;
- // Check whether the ID type is an enum.
- Type enumType = typeof (T);
- if (!enumType.IsEnum)
- {
- throw new ArgumentException(
- "The DependencyPropertyFactory requires an enum specifying all properties " +
- "which should be dependency properties as template parameter.");
- }
- // Check whether the type parameter has an owner type when no owner set.
- if (m_ownerType == null)
- {
- if (enumType.IsNested)
- {
- m_ownerType = enumType.DeclaringType;
- }
- else
- {
- throw new ArgumentException(
- "When not specifying an owner type in the constructor, " +
- "the enum type passed as a template parameter should be nested inside the desired owner.");
- }
- }
- // Check whether the factory itself is defined as a static field.
- FieldInfo[] fields = m_ownerType.GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
- bool validFactory =
- fields.Where(
- field => field.FieldType == typeof (DependencyPropertyFactory<T>)).Where(
- field => field.IsStatic).Any();
- if (!validFactory)
- {
- throw new ArgumentException(
- "Incorrect usage of DependencyPropertyFactory in class '" + m_ownerType.Name + "'. " +
- "A DependencyPropertyFactory needs to be created as a static field inside it's owner class.");
- }
- // Get all enum values.
- const BindingFlags ALL_ENUM_VALUES = BindingFlags.Public | BindingFlags.Static;
- MemberInfo[] enumMembers = enumType.GetMembers(ALL_ENUM_VALUES);
- T[] enumValues = new T[enumMembers.Length];
- for (int i = 0; i < enumMembers.Length; i++)
- {
- enumValues[i] = (T)Enum.Parse(enumType, enumMembers[i].Name);
- }
- // Check all properties for a dependency property attribute.
- const BindingFlags ALL_PROPERTIES = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
- var matchingProperties = new Dictionary<PropertyInfo, DependencyPropertyAttribute>();
- foreach ( PropertyInfo property in m_ownerType.GetProperties( ALL_PROPERTIES ) )
- {
- object[] attribute = property.GetCustomAttributes( typeof( DependencyPropertyAttribute ), false );
- if ( attribute != null && attribute.Length == 1 )
- {
- // A correct attribute was found.
- DependencyPropertyAttribute dependency = (DependencyPropertyAttribute)attribute[ 0 ];
- // Check whether the ID corresponds to the ID required for this factory.
- if (dependency.GetId() is T)
- {
- matchingProperties.Add(property, dependency);
- }
- }
- }
- // TODO: Check whether callback attributes are applied to non-static functions.
- // Every enum value should have a matching property.
- if (enumValues.Length != matchingProperties.Count)
- {
- throw new ArgumentException(
- "Not all enum values of the template parameter have a matching property " +
- "with a DependencyPropertyAttribute set in the type '" + m_ownerType.Name + "'." );
- }
- // Create the dependency properties.
- foreach (var item in matchingProperties)
- {
- const string CONVENTION_ENABLED = "WPF dependency property conventions are enabled and not followed. ";
- if (m_enforceWpfConvention)
- {
- // Find dependency property field identifier.
- const string IDENTIFIER_SUFFIX = "Property";
- const BindingFlags IDENTIFIER_MODIFIERS = BindingFlags.Public | BindingFlags.Static;
- string identifierField = item.Key.Name + IDENTIFIER_SUFFIX;
- FieldInfo identifier = m_ownerType.GetField( identifierField, IDENTIFIER_MODIFIERS );
- if (identifier == null || (identifier.FieldType != typeof( global::System.Windows.DependencyProperty )))
- {
- throw new InvalidOperationException(
- CONVENTION_ENABLED +
- "There is no public static dependency property field identifier \"" + identifierField +
- "\" available in the class \"" + m_ownerType.Name + "\"." );
- }
- // Verify name when set.
- if (item.Value.Name != null && item.Key.Name != item.Value.Name)
- {
- throw new InvalidOperationException(
- CONVENTION_ENABLED + "The CLR property wrapper '" + item.Key.Name +
- "' doesn't match the name of the dependency property.");
- }
- }
- CreateDependencyProperty(item.Key, item.Value);
- }
- }
- private void CreateDependencyProperty(PropertyInfo property, DependencyPropertyAttribute attribute)
- {
- // Set dependency property parameters.
- string name = attribute.Name ?? property.Name;
- Type propertyType = property.PropertyType;
- // When no default value is set, and it is a value type, use the default value.
- object defaultValue = attribute.DefaultValue ??
- (propertyType.IsValueType ? Activator.CreateInstance( propertyType ) : null);
- // By default, readonly when setter is private.
- MethodInfo setMethod = property.GetSetMethod();
- bool readOnly = attribute.IsReadOnlySet() ?
- attribute.IsReadOnly() : (setMethod == null || setMethod.IsPrivate);
- T id = (T)attribute.GetId();
- // 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
- {
- const BindingFlags ALL_STATIC_METHODS =
- BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Static;
- return
- (from method in m_ownerType.GetMethods(ALL_STATIC_METHODS)
- let methodAttributes =
- method.GetCustomAttributes( typeof( TCallbackAttribute ), false )
- where methodAttributes != null && methodAttributes.Length == 1
- let changed =
- (TCallbackAttribute)methodAttributes[ 0 ]
- where changed.GetId().Equals( id )
- select Delegate.CreateDelegate( changed.CallbackType, method )).FirstOrDefault();
- }
- private void CreateDependencyProperty(
- T identifier,
- string name,
- Type propertyType,
- object defaultValue,
- PropertyChangedCallback changedCallback,
- CoerceValueCallback coerceCallback)
- {
- global::System.Windows.DependencyProperty property =
- global::System.Windows.DependencyProperty.Register(
- name,
- propertyType,
- m_ownerType,
- new PropertyMetadata(defaultValue, changedCallback, coerceCallback)
- );
- Properties.Add(identifier, property);
- }
- private void CreateReadOnlyDependencyProperty(
- T identifier,
- string name,
- Type propertyType,
- object defaultValue,
- PropertyChangedCallback changedCallback,
- CoerceValueCallback coerceCallback )
- {
- DependencyPropertyKey propertyKey =
- global::System.Windows.DependencyProperty.RegisterReadOnly(
- name,
- propertyType,
- m_ownerType,
- new PropertyMetadata( defaultValue, changedCallback, coerceCallback )
- );
- m_readOnlyProperties.Add( identifier, propertyKey );
- global::System.Windows.DependencyProperty property = propertyKey.DependencyProperty;
- 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 global::System.Windows.DependencyProperty this[T id]
- {
- get
- {
- if (!Properties.ContainsKey(id))
- {
- throw new KeyNotFoundException(
- "The dependency property with the key \"" + id.ToString() + "\" doesn't exist. " +
- "Did you forget to add a DependencyPropertyAttribute?" );
- }
- return Properties[ id ];
- }
- }
- /// <summary>
- /// Get the value of a property.
- /// </summary>
- /// <param name="o">The dependency object from which to get the value.</param>
- /// <param name="property">The property to get the value from.</param>
- /// <returns>The value from the asked property.</returns>
- public object GetValue(DependencyObject o, T property)
- {
- return o.GetValue(Properties[property]);
- }
- /// <summary>
- /// Set the value of a property, whether it is readonly or not.
- /// </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 SetValue(DependencyObject o, T property, object value)
- {
- if (m_readOnlyProperties.ContainsKey(property))
- {
- DependencyPropertyKey key = m_readOnlyProperties[ property ];
- o.SetValue( key, value );
- }
- else
- {
- o.SetValue( Properties[property], 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>
- private readonly object m_id;
- /// <summary>
- /// Create a new dependency property attribute for a specified dependency property.
- /// </summary>
- /// <param name="id">The ID of the dependency property.</param>
- protected AbstractDependencyPropertyAttribute(object id)
- {
- m_id = id;
- }
- /// <summary>
- /// Get the ID of the dependency property to which the attribute applies.
- /// </summary>
- /// <returns>The ID of the dependency property to which the attribute applies.</returns>
- public object GetId()
- {
- return m_id;
- }
- }
- /// <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 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
- {
- private readonly bool m_readOnly;
- private readonly bool m_readOnlySet = false;
- /// <summary>
- /// The name to use for the dependency property. By default this is the string representation of Id.
- /// </summary>
- public string Name
- {
- get; 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.</param>
- public DependencyPropertyAttribute(object id) : base(id)
- {
- m_readOnlySet = false;
- }
- public DependencyPropertyAttribute(object id, bool readOnly) : base(id)
- {
- m_readOnly = readOnly;
- m_readOnlySet = true;
- }
- /// <summary>
- /// Should the dependency property be a readonly dependency property or not.
- /// </summary>
- /// <returns></returns>
- public bool IsReadOnly()
- {
- return m_readOnly;
- }
- /// <summary>
- /// Has the readonly value been set?
- /// </summary>
- /// <returns></returns>
- public bool IsReadOnlySet()
- {
- return m_readOnlySet;
- }
- }
- /// <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