Monday, 26 October 2009

Conditional UIHint

Long time no posts Open-mouthed but I’m posting again should have some DDv2 and ASP.Net 4.0 samples soon Open-mouthed

I just had need of a conditional UIHint for a project I was working and so here it is.

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class ConditionalUIHintAttribute : Attribute
{
    public String UIHint { get; set; }
    public PageTemplate PageTemplates { get; private set; }

    public ConditionalUIHintAttribute() { }

    public ConditionalUIHintAttribute(String uiHint, PageTemplate pageTemplates)
    {
        UIHint = uiHint;
        PageTemplates = pageTemplates;
    }
}

Listing 1 – the  conditional UIHint attribute

We will need and IAutoColumnGenerator to generate the columns for us and here that is in Listing 2 I’ve simplified it and all the extra code extension methods are in the sample at the end of the article.

/// <summary>
/// Implements the IAutoFieldGenerator interface and 
/// supports advanced scenarios such as declarative 
/// column ordering, workaround for attribute 
/// localization issues.
/// </summary>
public class AdvancedFieldGenerator : IAutoFieldGenerator
{

    private MetaTable _table;
    private bool _multiItemMode;
    protected Page _page;

    /// <summary>
    /// Creates a new AdvancedFieldGenerator.
    /// </summary>
    /// <param name="table">The table this class generates fields for.</param>
    /// <param name="multiItemMode"><value>true</value> to indicate a 
    /// multi-item control such as GridView, <value>false</value> for 
    /// a single-item control such as DetailsView.
    /// </param>
    public AdvancedFieldGenerator(MetaTable table, bool multiItemMode, Page page)
    {
        if (table == null)
            throw new ArgumentNullException("table");

        _table = table;
        _multiItemMode = multiItemMode;
        _page = page;

    }

    public ICollection GenerateFields(Control control)
    {
        var template = _page.GetPageTemplate();
        var fields = from c in _table.GetVisibleColumns(_multiItemMode, template)
                     select new DynamicField()
                                  {
                                      DataField = c.Name,
                                      HeaderText = c.DisplayName,
                                      UIHint = c.ConditionalUIHint(template)
                                  };

        return fields.ToList();
    }
}

Listing 2 – IAutoColumnGenerator

The neat thing here is the ConditionalUIHint extension method that returns the ConditionalUIHint (see it in Listing 5) if the PageTemplate matches otherwise the standard UIHint is returned, here I’ve created a different Boolean field template called AltBoolean that is used only in Edit pages; you could also specify:

[ConditionalUIHint("AltBoolean", PageTemplate.Edit | PageTemplate.Insert)]

which would use the condition field template in both Edit and Insert mode, but in the sample metadata you will get a different field template for Edit and Insert pages to show off the feature.

[MetadataType(typeof(ProductMetadata))]
partial class Product
{
    partial class ProductMetadata
    {
        public Object ProductID { get; set; } 
        public Object ProductName { get; set; } 
        public Object SupplierID { get; set; } 
        public Object CategoryID { get; set; } 
        public Object QuantityPerUnit { get; set; } 
        public Object UnitPrice { get; set; } 
        public Object UnitsInStock { get; set; } 
        public Object UnitsOnOrder { get; set; } 
        public Object ReorderLevel { get; set; } 

        [ConditionalUIHint("AltBoolean", PageTemplate.Edit)]
        public Object Discontinued { get; set; } 
        // Entity Set 
        public Object Order_Details { get; set; } 
        // Entity Ref 
        public Object Category { get; set; } 
        // Entity Ref 
        public Object Supplier { get; set; } 
    }
}

Listing 4 – sample metadata

To use this in a page you would need to:

public partial class Details : System.Web.UI.Page
{
    protected MetaTable table;

    protected void Page_Init(object sender, EventArgs e)
    {
        DynamicDataManager1.RegisterControl(DetailsView1);
        table = DetailsDataSource.GetTable();
        DetailsView1.RowsGenerator = new AdvancedFieldGenerator(table, true, this);
    }

Listing 3 – Page_Init Details page

move the table assignment to the Page_Init and add the

DetailsView1.RowGenerator = new AdvancedFieldGenerator(table, true, Page);

to the Page_Init of each page you want to use it with. I’ve added this to every page in the sample.

Below are the extension methods attributes required to use the above code

/// <summary>
/// Conditionals the UI hint.
/// </summary>
/// <param name="column">The column.</param>
/// <param name="currentPage">The current page.</param>
/// <returns></returns>
public static String ConditionalUIHint(this MetaColumn column, PageTemplate currentPage)
{
    var conditionalUIHint = column.GetAttribute<ConditionalUIHintAttribute>();
    if (conditionalUIHint != null && conditionalUIHint.AlternateUIHint(currentPage))
        return conditionalUIHint.UIHint;
    else
        return column.UIHint;
}

/// <summary>
/// Alternates the UI hint.
/// </summary>
/// <param name="cUIHint">The c UI hint.</param>
/// <param name="currentPage">The current page.</param>
/// <returns></returns>
public static Boolean AlternateUIHint(this ConditionalUIHintAttribute cUIHint, PageTemplate currentPage)
{
    return (cUIHint.PageTemplates & currentPage) == currentPage;
}

Listing 5 – Extension methods

Updated: For ASP.Net 4.0 if MetaColumn.UIHint (see A Great Buried Sample in Dynamic Data Preview 4 and now ASP.Net 4.0 Beta 2) was virtual we could have overridden it to achieve the same thing without  having to add the AdvancedFieldGenerator to each page.

Download

All other extension methods are provided in the sample.

Have fun Open-mouthed

For the next post I think we may be able to use a FieldTemplateFatory like the EnumFieldTemplateFactory from the old Futures sample. And maybe something extra for ASP.Net 4.0 using the MetaModel classes Dont tell anyone

6 comments:

Team NO.1 said...

thanks. very useful

Roberto Vanoli said...

Hello Steve, tankyou for your valuable posts.

Pls consider my case (a sort of conditional UHINT): I'm creating a DynamicMultiField, subclass of DynamicField.

I want to use it in GridView, in this way:

(where MyValueField is the value to display and MyUIHintField the UIHint to be used)

In InitializeCell() I'm tryint to do something like this: control.UIHint = databounditem[MyUIHintField];

but I'm unable to access it. What i'm missing?

thankyou for any response
R.V.

Steve said...

Always welcome

Steve :)

Debra Paponette said...

hey, i tried your code in DD EF 4 and it is working fine but i don't want the value to store in the database as the selected option but rather the code it references. Not sure if i am using the right thing here. Basically i have a column called doc type which has String enum eg- BL means Bill of Lading. I want to show the used Bill of Lading in a drop down box but store as BL in the database. I also want the gridview to show Bill of lading. So basically it works like a foreignkey template but it isn't. Any help will be greatly appreciated

Debra Paponette said...

hey, i tried your code in DD EF 4 and it is working fine but i don't want the value to store in the database as the selected option but rather the code it references. Not sure if i am using the right thing here. Basically i have a column called doc type which has String enum eg- BL means Bill of Lading. I want to show the used Bill of Lading in a drop down box but store as BL in the database. I also want the gridview to show Bill of lading. So basically it works like a foreignkey template but it isn't. Any help will be greatly appreciated

Steve said...

Hi Debra, not sure what you mean conditional UIHint is about changing the UI depending on Page Template. so I don't think it's what you want

Steve