public static void Main() { DetectingAttributes.Go(); MatchingAttributes.Go(); ConditionalAttributeDemo.Go(); }
/// <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> /// <param name = "checkForStatic">Whether or not to check whether the factory is static.</param> protected DependencyPropertyFactory(Type ownerType, bool enforceNamingConventions, bool checkForStatic) : base(ownerType, true) { Contract.Requires(typeof(T).IsPublic || typeof(T).IsNestedPublic); Properties = new Dictionary <T, DependencyProperty>(); _enforceWpfConvention = enforceNamingConventions; // When coercion is used, all enum values should be independent flag values, different from 0. if (MatchingAttributes.Any(a => a.Key.GetAttributes <CoercionHandlerAttribute>().Length > 0)) { T none = default(T); Type enumType = typeof(T); bool correctImplementation = enumType.IsFlagsEnum() && EnumHelper <T> .GetValues().All(v => EnumHelper <T> .GetFlaggedValues(v).Count() == 1 && !v.Equals(none)); if (!correctImplementation) { string message = String.Format( "In order to use coercion, each enum value of {0} should be an individual flag value, different from 0.", enumType); throw new InvalidImplementationException(message); } } // Check whether the factory itself is defined as a static field. // TODO: Can part of this logic be moved to ReflectionHelper? if (checkForStatic) { FieldInfo[] fields = OwnerType.GetFields( BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); bool validFactory = fields .Where(field => field.FieldType == typeof(DependencyPropertyFactory <T>) && field.IsStatic) .Any(); if (!validFactory) { throw new InvalidImplementationException( "Incorrect usage of DependencyPropertyFactory in class '" + OwnerType.Name + "'. " + "A DependencyPropertyFactory needs to be created as a static field inside it's owner class."); } } // Check whether callback attributes are applied to non-static functions. They should be static! var callbackMethods = from method in OwnerType.GetMethods(ReflectionHelper.FlattenedClassMembers) from attribute in method.GetAttributes <AbstractDependencyPropertyCallbackAttribute>() select method; if (!callbackMethods.All(m => m.IsStatic)) { throw new InvalidImplementationException( "Not all dependency property callback functions are defined as static in type '" + OwnerType.Name + "'."); } // Create dependency properties. var properties = from item in MatchingAttributes where item.Key is PropertyInfo select item; foreach (var item in properties) { var attribute = (DependencyPropertyAttribute)item.Value[0]; CreateDependencyProperty(GetDependencyPropertyInfo(item.Key as PropertyInfo, attribute)); } // Create attached properties. var attachedProperties = from item in MatchingAttributes where item.Key is MethodInfo from attribute in item.Value group new { MemberInfo = item.Key, Attribute = (DependencyPropertyAttribute)attribute } by attribute.GetId(); foreach (var item in attachedProperties) { var attachedProperty = item.ToList(); bool valid = false; if (attachedProperty.Count <= 2) { MethodInfo[] methods = attachedProperty.Select(p => (MethodInfo)p.MemberInfo).ToArray(); MethodInfo getter = methods.Single(m => m.Name.StartsWith(GetPrefix)); MethodInfo setter = methods.SingleOrDefault(m => m.Name.StartsWith(SetPrefix) && m.ReturnType == typeof(void)); // Verify whether getter and setter correspond. // TODO: Do typechecking of parameters? if (setter != null && getter.Name.Substring(GetPrefix.Length) == setter.Name.Substring(SetPrefix.Length)) { // Extract settings. var attributes = attachedProperty.Select(a => a.Attribute).ToArray(); var id = (T)attachedProperty.First().Attribute.GetId(); // TODO: Allow checking whether or not the default value is set or not. var defaultValues = attributes.Where(a => a.DefaultValue != null).Select(a => a.DefaultValue).ToArray(); var readOnlySettings = attributes.Where(a => a.IsReadOnlySet()).Select(a => a.IsReadOnlySet() ? a.IsReadOnly() : new bool?()).ToArray(); if (!defaultValues.AllEqual() || !readOnlySettings.AllEqual()) { throw new InvalidImplementationException( "All set options of \"" + typeof(DependencyPropertyAttribute) + "\" should correspond with attributes with the same ID." + " Preferably only set options on one of the attributes."); } DependencyPropertyInfo info = GetAttachedDependencyPropertyInfo( getter, setter, id, defaultValues.FirstOrDefault(), readOnlySettings.FirstOrDefault()); // HACK: For collections, use a dependency property with non-matching name so the getter is called the first time, // so it can be initialized and doesn't need to be initialized through XAML. if (info.Type.ImplementsInterface(typeof(ICollection))) { info.Name = info.Name + "Internal"; _attachedCollectionsTypes.Add(info.Id, info.Type); } CreateDependencyProperty(info); valid = true; } } if (!valid) { throw new InvalidImplementationException( "Invalid usage of the attribute " + typeof(DependencyPropertyAttribute) + ". " + "To create an attached property, apply it to a correctly named get and optionally corresponding set method. "); } } }