/// <summary> /// Parse metadata from a target FrameworkElement. This will cache the metadata on the element as an attached property. /// </summary> /// <param name="element">The target FrameworkElement to pull metadata from.</param> /// <param name="forceUpdate">If set, will not pull metadata from cache.</param> /// <param name="entity">The entity used.</param> /// <param name="bindingExpression">The bindingExpression used.</param> /// <returns>Returns the metadata associated with the element. Will be null if no metadata was found.</returns> internal static ValidationMetadata ParseMetadata(FrameworkElement element, bool forceUpdate, out object entity, out BindingExpression bindingExpression) { entity = null; bindingExpression = null; if (element == null) { return(null); } if (!forceUpdate) { ValidationMetadata existingVMD = element.GetValue(ValidationMetadataProperty) as ValidationMetadata; if (existingVMD != null) { return(existingVMD); } } BindingExpression be = null; FieldInfo[] fields = element.GetType().GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy); foreach (FieldInfo field in fields) { if (field.FieldType == typeof(DependencyProperty)) { // Found a dependency property be = element.GetBindingExpression((DependencyProperty)field.GetValue(null)); if (be != null && be.ParentBinding != null && be.ParentBinding.Path != null) { // Found a BindingExpression, ensure it has valid data entity = be.DataItem != null ? be.DataItem : element.DataContext; if (entity != null) { if (be.ParentBinding.Mode == BindingMode.TwoWay) { bindingExpression = be; // A twoway binding will be automatically chosen and the rest ignored break; } // Perform an arbitrary sort on path (string), so the same dependency property is chosen consistently. // Reflection ordering is not deterministic and if we just pick the first, we could be // matched with different dependency properties depending on the run. if (bindingExpression == null || string.Compare(be.ParentBinding.Path.Path, bindingExpression.ParentBinding.Path.Path, StringComparison.Ordinal) < 0) { bindingExpression = be; } } } } } if (bindingExpression != null) { ValidationMetadata newVMD = ParseMetadata(bindingExpression.ParentBinding.Path.Path, entity); element.SetValue(ValidationMetadataProperty, newVMD); return(newVMD); } return(null); }
/// <summary> /// Sets the ValidationMetadata property for the input control /// </summary> /// <param name="inputControl">The input control to set the ValidationMetadata property on.</param> /// <param name="value">The ValidationMetadata to associate with the input control.</param> internal static void SetValidationMetadata(DependencyObject inputControl, ValidationMetadata value) { if (inputControl == null) { throw new ArgumentNullException("inputControl"); } inputControl.SetValue(ValidationMetadataProperty, value); }
/// <summary> /// Parse metadata given a binding path and entity object. /// </summary> /// <param name="bindingPath">The bindingPath is the name of the property on the entity from which to pull metadata from. This supports dot notation.</param> /// <param name="entity">The entity object from which to pull metadata from.</param> /// <returns>The validation metadata associated with the entity and binding path. This will return null if none exists.</returns> internal static ValidationMetadata ParseMetadata(string bindingPath, object entity) { if (entity != null && !String.IsNullOrEmpty(bindingPath)) { Type entityType = entity.GetType(); PropertyInfo prop = GetProperty(entityType, bindingPath); if (prop != null) { ValidationMetadata newVMD = new ValidationMetadata(); object[] attributes = prop.GetCustomAttributes(false); foreach (object propertyAttribute in attributes) { // Loop through each attribute and update the VMD as appropriate // RequiredField //RequiredAttribute reqAttribute = propertyAttribute as RequiredAttribute; //if (reqAttribute != null) //{ // newVMD.IsRequired = true; // continue; //} //// Display attribute parsing //DisplayAttribute displayAttribute = propertyAttribute as DisplayAttribute; //if (displayAttribute != null) //{ // newVMD.Description = displayAttribute.GetDescription(); // newVMD.Caption = displayAttribute.GetName(); // continue; //} } if (newVMD.Caption == null) { // If the name is not defined via the DisplayAttribute, use the property name. newVMD.Caption = prop.Name; // Caption can be set to empty string to have an empty Caption and not default // to the property name. } return(newVMD); } } return(null); }
/// <summary> /// Load meta data and update the UI. /// </summary> /// <param name="forceUpdate">If true, metadata will not be loaded from cache.</param> private void LoadMetadata(bool forceUpdate) { ValidationMetadata vmd = null; object entity = null; BindingExpression bindingExpression = null; if (!String.IsNullOrEmpty(this.PropertyPath)) { entity = this.DataContext; // Pull metadata directly from the DataContext. This isn't cached so it will be pulled every time. vmd = ValidationHelper.ParseMetadata(this.PropertyPath, entity); } else if (this.Target != null) { // Pull the metadata from the target FrameworkElement. vmd = ValidationHelper.ParseMetadata(this.Target, forceUpdate, out entity, out bindingExpression); } if (this.ValidationMetadata != vmd) { this.ValidationMetadata = vmd; // Update to the new VMD if (this.ValidationMetadata != null) { string newContent = this.ValidationMetadata.Caption; if (newContent != null && this._canContentUseMetaData) { this.SetContentInternally(newContent); } } else if (this._canContentUseMetaData) { // The Target property was reset. Since the Content property // was using the metadata, it also needs to be reset. this.SetContentInternally(null); } if (!this._isRequiredOverridden) { bool isRequired = this.ValidationMetadata == null ? false : this.ValidationMetadata.IsRequired; SetValue(Label.IsRequiredProperty, isRequired); } } }