Monday, June 11, 2012

Dynamic Data - Custom Metadata Providers

This is really just an addition to Matt Berseth's article Dynamic Data And Custom Metadata Providers from August 24, 2008, all I wanted to do was add the same features to the Table/Class not just the Columns/Properties. So you can see the full explanation over at Matt Berseth's blog. So here are the listings:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web;
 
public class DefaultAttributesTypeDescriptionProvider : System.ComponentModel.TypeDescriptionProvider
{
    private Type Type { get; set; }
 
    public DefaultAttributesTypeDescriptionProvider(Type type)
        : this(type, TypeDescriptor.GetProvider(type))
    {
        this.Type = type;
    }
 
    public DefaultAttributesTypeDescriptionProvider(Type type, TypeDescriptionProvider parentProvider)
        : base(parentProvider)
    {
        this.Type = type;
    }
 
    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
    {
        return new DefaultAttributesTypeDescriptor(base.GetTypeDescriptor(objectType, instance), this.Type);
    }
}

Listing 1 – TypeDescriptorProvider

No change in Listing 1 from what Matt has already done just some renaming to make it a bit clearer to me what's going on.

using System;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
 
public class DefaultAttributesTypeDescriptor : CustomTypeDescriptor
{
    private Type Type { get; set; }
 
    public DefaultAttributesTypeDescriptor(ICustomTypeDescriptor parent, Type type)
        : base(parent)
    {
        this.Type = type;
    }
 
    /// <summary>
    /// Returns the collection of attributes for a given table
    /// </summary>
    /// <returns>AttributeCollection</returns>
    public override AttributeCollection GetAttributes()
    {
        AttributeCollection baseAttributes = base.GetAttributes();
 
        if (baseAttributes.OfType<DisplayNameAttribute>().FirstOrDefault() == null)
        {
            List<Attribute> extraAttributes = new List<Attribute>();
 
            // generate the display name
            String friendlyDisplayName = base.GetClassName().ToTitleFromPascal();
 
            // add it to the list
            extraAttributes.Add(new DisplayNameAttribute(friendlyDisplayName));
 
            // only create a new collection if it is necessary
            return AttributeCollection.FromExisting(baseAttributes, extraAttributes.ToArray());
        }
        else
        {
            return baseAttributes;
        }
    }
 
    /// <summary>
    /// Returns a collection of properties (columns) for the type,
    /// each with attributes for that table
    /// </summary>
    /// <returns>PropertyDescriptorCollection</returns>
    public override PropertyDescriptorCollection GetProperties()
    {
        List<PropertyDescriptor> propertyDescriptors = new List<PropertyDescriptor>();
 
        foreach (PropertyDescriptor propDescriptor in base.GetProperties())
        {
            List<Attribute> newAttributes = new List<Attribute>();
 
            // Display Name Rules ...
            // If the property doesn't already have a DisplayNameAttribute defined
            // go ahead and auto-generate one based on the property name
            if (!propDescriptor.HasAttribute<DisplayNameAttribute>())
            {
                // generate the display name
                String friendlyDisplayName = propDescriptor.Name.ToTitleFromPascal();
 
                // add it to the list
                newAttributes.Add(new DisplayNameAttribute(friendlyDisplayName));
            }
 
            // Display Format Rules ...
            // If the property doesn't already have a DisplayFormatAttribute defined
            // go ahead and auto-generate one based on the property type
            if (!propDescriptor.HasAttribute<DisplayFormatAttribute>())
            {
                // get the default format for the property type
                String displayFormat = propDescriptor.PropertyType.GetDisplayFormat();
 
                // add it to the list
                newAttributes.Add(new DisplayFormatAttribute() { DataFormatString = displayFormat });
            }
 
            propertyDescriptors.Add(new WrappedPropertyDescriptor(propDescriptor, newAttributes.ToArray()));
        }
 
        // return the descriptor collection
        return new PropertyDescriptorCollection(propertyDescriptors.ToArray(), true);
    }
 
    private class WrappedPropertyDescriptor : PropertyDescriptor
    {
        private PropertyDescriptor _wrappedPropertyDescriptor;
 
        public WrappedPropertyDescriptor(PropertyDescriptor wrappedPropertyDescriptor, Attribute[] attributes)
            : base(wrappedPropertyDescriptor, attributes)
        {
            _wrappedPropertyDescriptor = wrappedPropertyDescriptor;
        }
 
        public override bool CanResetValue(object component)
        {
            return _wrappedPropertyDescriptor.CanResetValue(component);
        }
 
        public override Type ComponentType
        {
            get { return _wrappedPropertyDescriptor.ComponentType; }
        }
 
        public override object GetValue(object component)
        {
            return _wrappedPropertyDescriptor.GetValue(component);
        }
 
        public override bool IsReadOnly
        {
            get { return _wrappedPropertyDescriptor.IsReadOnly; }
        }
 
        public override Type PropertyType
        {
            get { return _wrappedPropertyDescriptor.PropertyType; }
        }
 
        public override void ResetValue(object component)
        {
            _wrappedPropertyDescriptor.ResetValue(component);
        }
 
        public override void SetValue(object component, object value)
        {
            _wrappedPropertyDescriptor.SetValue(component, value);
        }
 
        public override bool ShouldSerializeValue(object component)
        {
            return _wrappedPropertyDescriptor.ShouldSerializeValue(component);
        }
    }
}

Listing 2 – CustomTypeDescriptor

Here all I’ve done is again some renaming and add a GetAttributes method which returns the attributes on the class/table. The section is marked in BOLD ITALIC for emphasis. What it does is check to see if the DisplayNameAttriburte is set and if not then auto generates it.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.DynamicData;
using Microsoft.Web.DynamicData;
 
public static class HelperExtansionMethods
{
    public static ColumnOrderAttribute ColumnOrdering(this MetaColumn column)
    {
        return column.Attributes.OfType<ColumnOrderAttribute>().DefaultIfEmpty(ColumnOrderAttribute.Default).First();
    }
 
    public static Boolean IsHidden(this MetaColumn column, PageTemplate currentPage)
    {
        var hideIn = column.Attributes.OfType<HideColumnInAttribute>().DefaultIfEmpty(new HideColumnInAttribute()).First() as HideColumnInAttribute;
        return hideIn.PageTemplates.Contains(currentPage);
    }
 
    /// <summary>
    /// Converts a Pascal type Name to Title
    /// i.e. MyEmployees becomes My Employees
    ///      OrderID becomes Order ID etc
    /// </summary>
    /// <param name="s">String to convert</param>
    /// <returns>Title String</returns>
    public static String ToTitleFromPascal(this String s)
    {
        // remove name space
        String s0 = Regex.Replace(s, "(.*\\.)(.*)", "$2");

        // add space before Capital letter
        String s1 = Regex.Replace(s0, "[A-Z]", " $&");
 
        // replace '_' with space
        String s2 = Regex.Replace(s1, "[_]", " ");
 
        // replace double space with single space
        String s3 = Regex.Replace(s2, "  ", " ");
 
        // remove and double capitals with inserted space
        String s4 = Regex.Replace(s3, "(?<before>[A-Z])\\s(?<after>[A-Z])", "${before}${after}");
 
        // remove and double capitals with inserted space
        String sf = Regex.Replace(s4, "^\\s", "");
 
        // force first character to upper case
        return sf.ToTitleCase();
    }
 
    public static String ToTitleCase(this String text)
    {
        StringBuilder sb = new StringBuilder();
 
        for (int i = 0; i < text.Length; i++)
        {
            if (i > 0)
            {
                if (text.Substring(i - 1, 1) == " " || 
                    text.Substring(i - 1, 1) == "\t" || 
                    text.Substring(i - 1, 1) == "/")
                    sb.Append(text.Substring(i, 1).ToString().ToUpper());
                else
                    sb.Append(text.Substring(i, 1).ToString().ToLower());
            }
            else
                sb.Append(text.Substring(i, 1).ToString().ToUpper());
        }
 
        return sb.ToString();
    }
 
    public static Boolean HasAttribute<T>(this PropertyDescriptor descriptor) where T : Attribute
    {
        Boolean value = false;
        for (int i = 0; i < descriptor.Attributes.Count && !value; i++)
        {
            value = (descriptor.Attributes[i] is T);
        }
 
        return value;
    }
 
    public static String GetDisplayFormat(this Type type)
    {
        string defaultFormat = "{0}";
 
        if (type == typeof(DateTime) || type == typeof(Nullable<DateTime>))
        {
            defaultFormat = "{0:d}";
        }
        else if (type == typeof(decimal) || type == typeof(Nullable<decimal>))
        {
            defaultFormat = "{0:c}";
        }
 
        return defaultFormat;
    }
}

 

No comments:

Post a Comment