Guest User

DependencyPropertyFactory

a guest
Oct 24th, 2010
265
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

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×