SHARE
TWEET

DependencyPropertyFactory

a guest Oct 24th, 2010 226 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Windows;
  6.  
  7. namespace DependencyPropertyFactory
  8. {
  9.     /// <summary>
  10.     /// A dependency property factory to simplify creating and managing dependency properties for a certain type.
  11.     /// </summary>
  12.     /// <typeparam name="T">The type used as identifier for the dependency properties.</typeparam>
  13.     /// <author>Steven Jeuris</author>
  14.     public class DependencyPropertyFactory<T>
  15.     {
  16.         private readonly Type m_ownerType;
  17.  
  18.         /// <summary>
  19.         /// A list containing the dependency properties.
  20.         /// </summary>
  21.         public Dictionary<T, DependencyProperty> Properties
  22.         {
  23.             get; private set;
  24.         }
  25.  
  26.         private readonly Dictionary<T, DependencyPropertyKey> m_readOnlyProperties
  27.                 = new Dictionary<T, DependencyPropertyKey>();
  28.  
  29.         /// <summary>
  30.         /// Create a new dependency property container for a specific type.
  31.         /// </summary>
  32.         /// <param name="ownerType">The owner type of the dependency properties.</param>
  33.         public DependencyPropertyFactory(Type ownerType)
  34.         {
  35.             Properties = new Dictionary<T, DependencyProperty>();
  36.             m_ownerType = ownerType;
  37.  
  38.             // Get all dependency property attributes.
  39.             const BindingFlags ALL_PROPERTIES = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
  40.             foreach (PropertyInfo property in m_ownerType.GetProperties(ALL_PROPERTIES))
  41.             {
  42.                 object[] attribute = property.GetCustomAttributes(typeof (DependencyPropertyAttribute), false);
  43.                 if (attribute != null && attribute.Length == 1)
  44.                 {
  45.                     DependencyPropertyAttribute dependency = (DependencyPropertyAttribute)attribute[0];
  46.  
  47.                     // Check whether the ID corresponds to the ID required for this container.
  48.                     if (!(dependency.DependencyPropertyID is T))
  49.                     {
  50.                         break;
  51.                     }
  52.  
  53.                     // Set dependency property parameters.
  54.                     string name = dependency.Name ?? dependency.DependencyPropertyID.ToString();
  55.                     Type propertyType = property.PropertyType;
  56.                     // When no default value is set, and it is a value type, use the default value.
  57.                     object defaultValue = dependency.DefaultValue ??
  58.                         (propertyType.IsValueType ? Activator.CreateInstance( propertyType ) : null);
  59.                     // By default, readonly when setter is private.
  60.                     MethodInfo setMethod = property.GetSetMethod();
  61.                     bool readOnly = dependency.ReadOnlySet ?
  62.                         dependency.ReadOnly : (setMethod == null || setMethod.IsPrivate);                                                        
  63.  
  64.                     T id = (T)dependency.DependencyPropertyID;
  65.  
  66.                     // Find changed callback.
  67.                     PropertyChangedCallback changedCallback =
  68.                         (PropertyChangedCallback)CreateCallbackDelegate<DependencyPropertyChangedAttribute>(id);
  69.                     CoerceValueCallback coerceCallback =
  70.                         (CoerceValueCallback)CreateCallbackDelegate<DependencyPropertyCoerceAttribute>(id);
  71.  
  72.                     if (readOnly)
  73.                     {
  74.                         CreateReadOnlyDependencyProperty(
  75.                                 id, name, propertyType, defaultValue,
  76.                                 changedCallback, coerceCallback
  77.                         );
  78.                     }
  79.                     else
  80.                     {
  81.                         CreateDependencyProperty(
  82.                                 id, name, propertyType, defaultValue,
  83.                                 changedCallback, coerceCallback
  84.                         );
  85.                     }
  86.                 }
  87.             }
  88.         }
  89.  
  90.         /// <summary>
  91.         /// Create a callback delegate for a dependency property.
  92.         /// </summary>
  93.         /// <typeparam name="TCallbackAttribute">The callback attribute type.</typeparam>
  94.         /// <param name="id">The ID of the dependency property.</param>
  95.         /// <returns>A delegate which can be used as a callback.</returns>
  96.         private Delegate CreateCallbackDelegate<TCallbackAttribute>(object id)
  97.                 where TCallbackAttribute : AbstractDependencyPropertyCallbackAttribute
  98.         {
  99.             return
  100.                 (from method in m_ownerType.GetMethods()
  101.                  let methodAttributes =
  102.                      method.GetCustomAttributes( typeof( TCallbackAttribute ), false )
  103.                  where methodAttributes != null && methodAttributes.Length == 1
  104.                  let changed =
  105.                      (TCallbackAttribute)methodAttributes[ 0 ]
  106.                  where changed.DependencyPropertyID.Equals( id )
  107.                  select Delegate.CreateDelegate( changed.CallbackType, method )).FirstOrDefault();            
  108.         }
  109.  
  110.         private void CreateReadOnlyDependencyProperty(
  111.             T identifier,
  112.             string name,
  113.             Type propertyType,
  114.             object defaultValue,
  115.             PropertyChangedCallback changedCallback,
  116.             CoerceValueCallback coerceCallback)
  117.         {
  118.             DependencyPropertyKey propertyKey =
  119.                 DependencyProperty.RegisterReadOnly(
  120.                     name,
  121.                     propertyType,
  122.                     m_ownerType,
  123.                     new PropertyMetadata(defaultValue, changedCallback, coerceCallback)
  124.                     );
  125.             m_readOnlyProperties.Add(identifier, propertyKey);
  126.  
  127.             DependencyProperty property = propertyKey.DependencyProperty;
  128.             Properties.Add(identifier, property);
  129.         }
  130.  
  131.         private void CreateDependencyProperty(
  132.             T identifier,
  133.             string name,
  134.             Type propertyType,
  135.             object defaultValue,
  136.             PropertyChangedCallback changedCallback,
  137.             CoerceValueCallback coerceCallback)
  138.         {
  139.             DependencyProperty property =
  140.                 DependencyProperty.Register(
  141.                     name,
  142.                     propertyType,
  143.                     m_ownerType,
  144.                     new PropertyMetadata(defaultValue, changedCallback, coerceCallback)
  145.                     );
  146.  
  147.             Properties.Add(identifier, property);
  148.         }
  149.  
  150.         /// <summary>
  151.         /// Returns the dependency property for a given ID.
  152.         /// </summary>
  153.         /// <param name="id">The ID of the dependency propert. (enum type of the class type parameter)</param>
  154.         /// <returns>The dependency property for the given ID.</returns>
  155.         public DependencyProperty this[T id]
  156.         {
  157.             get
  158.             {
  159.                 if (!Properties.ContainsKey(id))
  160.                 {
  161.                     throw new KeyNotFoundException(
  162.                         "The dependency property with the specified key doesn't exist. " +
  163.                         "Did you forget to add a DependencyPropertyAttribute?" );                    
  164.                 }
  165.  
  166.                 return Properties[ id ];
  167.             }
  168.         }
  169.  
  170.         /// <summary>
  171.         /// Set the value of a readonly property.
  172.         /// </summary>
  173.         /// <param name="o">The dependency object on which to set the value.</param>
  174.         /// <param name="property">The property to set.</param>
  175.         /// <param name="value">The new value for the property.</param>
  176.         public void SetReadOnlyProperty(DependencyObject o, T property, object value)
  177.         {
  178.             if (!m_readOnlyProperties.ContainsKey(property))
  179.             {
  180.                 throw new KeyNotFoundException(
  181.                     "The readonly dependency property with the specified key doesn't exist. " +
  182.                     "Is the DependencyPropertyAttribute set to ReadOnly?");
  183.             }
  184.  
  185.             DependencyPropertyKey key = m_readOnlyProperties[ property ];
  186.             o.SetValue( key, value );
  187.         }
  188.     }
  189.  
  190.     /// <summary>
  191.     /// When applied to a member,
  192.     /// specifies how the dependency property should be created by the DependencyPropertyContainer.
  193.     /// </summary>
  194.     /// <author>Steven Jeuris</author>
  195.     public abstract class AbstractDependencyPropertyAttribute : Attribute
  196.     {
  197.         /// <summary>
  198.         /// The ID of the dependency property.
  199.         /// </summary>
  200.         public object DependencyPropertyID
  201.         {
  202.             get;
  203.             private set;
  204.         }
  205.  
  206.         /// <summary>
  207.         /// Create a new attribute which specifies how the dependency property should be created.
  208.         /// </summary>
  209.         /// <param name="id">The ID of the dependency property.</param>
  210.         protected AbstractDependencyPropertyAttribute(object id)
  211.         {
  212.             DependencyPropertyID = id;
  213.         }
  214.     }
  215.  
  216.     /// <summary>
  217.     /// When applied to the property of a type,
  218.     /// specifies how the dependency property should be created by the DependencyPropertyContainer.
  219.     /// </summary>
  220.     /// <author>Steven Jeuris</author>
  221.     [AttributeUsage(AttributeTargets.Property, AllowMultiple=false)]
  222.     public class DependencyPropertyAttribute : AbstractDependencyPropertyAttribute
  223.     {
  224.         /// <summary>
  225.         /// The name to use for the dependency property.
  226.         /// </summary>
  227.         public string Name
  228.         {
  229.             get; set;
  230.         }
  231.  
  232.         /// <summary>
  233.         /// Set to true if ReadOnly is set.
  234.         /// </summary>
  235.         public bool ReadOnlySet
  236.         {
  237.             get; private set;
  238.         }
  239.  
  240.         /// <summary>
  241.         /// Should the dependency property be a readonly dependency property or not.
  242.         /// By default, true when the setter is private, false otherwise.
  243.         /// </summary>
  244.         public bool ReadOnly
  245.         {
  246.             get; private set;
  247.         }
  248.  
  249.         /// <summary>
  250.         /// The default value for the dependency property. Should be of the same type as the property.
  251.         /// </summary>
  252.         public object DefaultValue
  253.         {
  254.             get; set;
  255.         }
  256.  
  257.         /// <summary>
  258.         /// Create a new attribute which gives information about the dependency property to create for a given property.
  259.         /// </summary>
  260.         /// <param name="id">The ID of the dependency property. This should be an enum.</param>
  261.         public DependencyPropertyAttribute(object id) : base(id)
  262.         {
  263.         }
  264.  
  265.         /// <summary>
  266.         /// Create a new attribute which gives information about the dependency property to create for a given property.
  267.         /// </summary>
  268.         /// <param name="id">The ID of the dependency property.</param>
  269.         /// <param name="readOnly">Should the dependency property be a readonly dependency property.</param>
  270.         public DependencyPropertyAttribute(object id, bool readOnly) : this(id)
  271.         {
  272.             ReadOnly = readOnly;
  273.             ReadOnlySet = true;
  274.         }
  275.     }
  276.  
  277.     /// <summary>
  278.     /// When applied to a member,
  279.     /// specifies a callback for a dependency property.
  280.     /// </summary>
  281.     /// <author>Steven Jeuris</author>
  282.     public abstract class AbstractDependencyPropertyCallbackAttribute : AbstractDependencyPropertyAttribute
  283.     {
  284.         /// <summary>
  285.         /// The delegate type for the callback.
  286.         /// </summary>
  287.         public abstract Type CallbackType
  288.         {
  289.             get;
  290.         }
  291.  
  292.         /// <summary>
  293.         /// Create a new attribute which specifies how the dependency property should be created.
  294.         /// </summary>
  295.         /// <param name="id">The ID of the dependency property.</param>
  296.         protected AbstractDependencyPropertyCallbackAttribute(object id) : base(id)
  297.         {
  298.         }
  299.     }
  300.  
  301.     /// <summary>
  302.     /// When applied to a PropertyChangedCallback method,
  303.     /// specifies the changed callback for a dependency property.
  304.     /// </summary>
  305.     /// <author>Steven Jeuris</author>
  306.     [AttributeUsage(AttributeTargets.Method, AllowMultiple=false)]
  307.     public class DependencyPropertyChangedAttribute : AbstractDependencyPropertyCallbackAttribute
  308.     {
  309.         /// <summary>
  310.         /// Create a new attribute to assign a function as changed callback to a given dependency property ID.
  311.         /// </summary>
  312.         /// <param name="id">The ID of the dependency property.</param>
  313.         public DependencyPropertyChangedAttribute(object id) : base(id)
  314.         {
  315.         }
  316.  
  317.         /// <summary>
  318.         /// The delegate type for this callback.
  319.         /// </summary>
  320.         public override Type CallbackType
  321.         {
  322.             get { return typeof( PropertyChangedCallback ); }
  323.         }
  324.     }
  325.  
  326.     /// <summary>
  327.     /// When applied to a PropertyChangedCallback method,
  328.     /// specifies the coerce callback for a dependency property.
  329.     /// </summary>
  330.     /// <author>Steven Jeuris</author>
  331.     [AttributeUsage(AttributeTargets.Method, AllowMultiple=false)]
  332.     public class DependencyPropertyCoerceAttribute : AbstractDependencyPropertyCallbackAttribute
  333.     {
  334.         /// <summary>
  335.         /// Create a new attribute to assign a function as coerce callback to a given dependency property ID.
  336.         /// </summary>
  337.         /// <param name="id">The ID of the dependency property.</param>
  338.         public DependencyPropertyCoerceAttribute(object id) : base(id)
  339.         {
  340.         }
  341.  
  342.         /// <summary>
  343.         /// The delegate type for this callback.
  344.         /// </summary>
  345.         public override Type CallbackType
  346.         {
  347.             get { return typeof (CoerceValueCallback); }
  348.         }
  349.     }
  350. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top