Advertisement
Guest User

Untitled

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