Wednesday, 8 July 2009

Dynamic Data Custom Field Template – Values List

Here I have created a simple FieldTemplate for a recent project to allow you to give the user a predefined set of text values to choose from without a FK field relationship. An example of this could be a field with optional values of ‘Y’, ‘N’, ‘N/A’ or ‘Y’, ‘N’, ‘Other’ etc.

Requirements

  • Custom Attribute
  • Custom FieldTemplate

I have cover attributes many time on this blog so I’ll just post the code here:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class ValuesListAttribute : Attribute
{
    public ValuesListAttribute() { }

    /// <summary>
    /// Construvtor take a list of values
    /// to show in the ListControl
    /// </summary>
    /// <param name="values">
    /// A ',' or ';' seperated list of values in the order
    /// that they should appear in the list control.
    /// </param>
    public ValuesListAttribute(String values)
    {
        var items = values.Split(new char[] { ',', ';' });
        Values = (from item in items
                  select new ListItem()
                  {
                      Text = item,
                      Value = item
                  }).ToArray();
    }

    public ListItem[] Values { get; set; }
}

Listing 1 – ValuesListAttribute

[MetadataType(typeof(Value.Metadata))]
partial class Value
{
    internal class Metadata
    {
        public object Id { get; set; }
        [UIHint("ValuesList")]
        [ValuesList("Y,N,N/A")]
        public object TestR { get; set; }
        [UIHint("ValuesList")]
        [ValuesList("Y,N,Other")]
        public object Test { get; set; }
    }
}

Listing 2 – sample metadata

So now to the FieldTemplate we don’t need to have two versions ValuesList and ValuesList_Edit as the data type we will be using this on is always text so the ValuesList version will automatically use the standard Text FieldTemplate.

<%@ Control 
    Language="C#" 
    CodeBehind="ValuesList_Edit.ascx.cs" 
    Inherits="DD_ValuesListFieldTemplate.ValuesList_EditField" %>

<asp:DropDownList 
    ID="DropDownList1" 
    runat="server" 
    CssClass="droplist">
</asp:DropDownList>

<asp:RequiredFieldValidator 
    runat="server" 
    ID="RequiredFieldValidator1" 
    CssClass="droplist" 
    ControlToValidate="DropDownList1" 
    Display="Dynamic" 
    Enabled="false" />

Listing 3 – ValuesList_Edit.ascx

public partial class ValuesList_EditField : System.Web.DynamicData.FieldTemplateUserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {
        SetUpValidator(RequiredFieldValidator1);

        if (DropDownList1.Items.Count == 0)
        {
            // add [Not Set] in insert mode as the field would
            // otherwise just take on the first value in the list
            // if the user clicks save without changing it.
            if (!Column.IsRequired || Mode == DataBoundControlMode.Insert)
            {
                DropDownList1.Items.Add(new ListItem("[Not Set]", ""));
            }

            // fill the dropdown list with the 
            // values in the ValuesList attribute
            PopulateListControl(DropDownList1);
        }
    }

    /// <summary>
    /// Populates a list control with all the values from a parent table.
    /// </summary>
    /// <param name="listControl">The list control to populate.</param>
    protected new void PopulateListControl(ListControl listControl)
    {
        var uiHint = Column.GetAttribute<UIHintAttribute>();
        if (uiHint == null || uiHint.ControlParameters.Count == 0 || !uiHint.ControlParameters.Keys.Contains("ValueList"))
            throw new InvalidOperationException(String.Format(
                "ValuesList for FieldTemplate {0} field is missing from UIHint",
                Column.Name));

        var valuesList = ValuesListAttribute(uiHint.ControlParameters["ValueList"].ToString());
        if (valuesList.Count() == 0)
            throw new InvalidOperationException(String.Format(
                "ValuesList for FieldTemplate {0} field, is empty",
                Column.Name));

        listControl.Items.AddRange(valuesList);
    }

    public ListItem[] ValuesListAttribute(String values)
    {
        var items = values.Split(new char[] { ',', ';' });
        var listItems = (from item in items
                         select new ListItem()
                         {
                             Text = item,
                             Value = item
                         }).ToArray();
        return listItems;
    }

    // added page init to hookup the event handler
    protected override void OnDataBinding(EventArgs e)
    {
        if (Mode == DataBoundControlMode.Edit)
        {
            ListItem item = DropDownList1.Items.FindByValue(FieldValueString);
            if (item != null)
                DropDownList1.SelectedValue = FieldValueString;
        }

        if (Mode == DataBoundControlMode.Insert)
            DropDownList1.SelectedIndex = 0;

        base.OnDataBinding(e);
    }

    /// <summary>
    /// Provides dictionary access to all data in the current row.
    /// </summary>
    /// <param name="dictionary">The dictionary that contains all the new values.</param>
    protected override void ExtractValues(IOrderedDictionary dictionary)
    {
        // If it's an empty string, change it to null
        string val = DropDownList1.SelectedValue;
        if (val == String.Empty)
            val = null;

        dictionary[Column.Name] = ConvertEditedValue(val);
    }

    /// <summary>
    /// Gets the data control that handles the data field in a field template.
    /// </summary>
    /// <value></value>
    /// <returns>
    /// A data control that handles the data field in a field template.
    /// </returns>
    public override Control DataControl
    {
        get
        {
            return DropDownList1;
        }
    }
}

Listing 4 – ListValues_Edit.ascx.cs FieldTemplate

The FieldTemplate has a RequiredFieldValidator on it in case the field is required but as the user will be choosing from a list non of the other valuators make sense. Also note in the extract values method the value is replaced with null if an empty string is selected (the default in insert so no value will be added if the user does not explicitly select one.

In PopulateListControl we have replaced the base class FieldTemplateUserControl’s PopulateListControl with our own that will use the values passed into via the ValuesListAttribute (which is required for the FieldTemplate to function).

So there you have it a simple custom FieldTemplate for those situations where the database either uses a field with the values ‘Y’ and ‘N’ instead of a bit field or where you just want the user to choose from a simple list of possible cases.

Download

Friday, 12 June 2009

Removed Digsby

Hi I’ve remove the Digsby client after a couple of users complained of browser hijacking (browser opening lots of tabs/pages) when exiting my blog. So unless I can find a safer solution you’ll just have to leave comments of e-mail me.

smile_sad if anyone has a better safer solution just drop me a line and I’ll look into it.

Wednesday, 3 June 2009

A Great Buried Sample in Dynamic Data Preview 4 – Dynamic Data Futures

I just thought I’d mention this find ad David Ebbo mentioned overriding MetaTable to get at the table.GetScaffoldColumns() method, and then I found the Dynamic Data Futures sample inside of Preview 4 (which had a billing of being the Futures sample updated to work with Preview 4) and in side the it I found the CustomMetaClasses folder:

The CustomMetaClasses folder

Figure 1 – The CustomMetaClasses folder

This of course give you the sample you need to do this your self my plan is to use this sample to make a MetaTable Class that I can pass in a Lambda function to do all my column generation without ever having to add IAutoFieldGenerator to the page (which in .Net 4.0 will not work on the Details, Edit and Insert pages as they are to be based on FormView to accomodate EntityTemplates).

public class Global : System.Web.HttpApplication
{
    private static MetaModel s_defaultModel = new CustomMetaModel();
    public static MetaModel DefaultModel
    {
        get
        {
            return s_defaultModel;
        }
    }

    public static void RegisterRoutes(RouteCollection routes)
    {
        DefaultModel.RegisterContext(typeof(NorthwindDataContext), new ContextConfiguration()
        {
            ScaffoldAllTables = true,
            MetadataProviderFactory = (type => new InMemoryMetadataTypeDescriptionProvider(type,
new AssociatedMetadataTypeTypeDescriptionProvider(type))) });

Listing 1 – Adding a DefaultModel based on CustomMetaModel from the CustomMetaClasses folder

Listing 1 shows the addition of the CustomMetaModel to the Global.asax allowing you to override the Meta Classes in one place this is cool.

Thanks again ASP.Net Team HappyWizard

Tuesday, 2 June 2009

HTML Editor FieldTemplate for Dynamic Data Using AJAX Control Toolkit Editor

I decided to create an HTML Edit FieldTemplate after the AJAX Control Toolkit HTML Editor was released and a post (Edit.aspx page hanging on text entry) on the ASP.Net Dynamic Data Forum was posted.

For this we will need two FieldTemplates:

  1. Html.ascx
  2. Html_Edit.ascx

The first of the two Html.ascx will display the HTML rendered correctly and the second will use the AJAX Control Toolkit HTML Editor.

The Normal FieldTemplate

<%@ Control 
    Language="C#" 
    Inherits="DD_HTMLEditor.Html_Field" 
    Codebehind="Html.ascx.cs" %>

<asp:Literal 
    runat="server" 
    ID="Literal1" 
    Text="<%# FieldValueEditString %>" />

Listing 1 – Html.ascx

using System;
using System.Web.UI;

namespace DD_HTMLEditor
{
    public partial class Html_Field : System.Web.DynamicData.FieldTemplateUserControl
    {
        public override Control DataControl
        {
            get
            {
                return Literal1;
            }
        }
    }
}

Listing 2 – Html.ascx.cs

As you can see this is a direct copy of the Text.ascx FieldTemplate the only change is to Literal1 in the ascx file instead of FieldValueString we are using FieldValueEditString.

Description Field Showing HTML

Figure 1 – Description Field Showing HTML

The Edit FieldTemplate

<%@ Control 
    Language="C#" 
    Inherits="DD_HTMLEditor.Html_EditField" 
    Codebehind="Html_Edit.ascx.cs" %>
    
<%@ Register 
    Assembly="AjaxControlToolkit" 
    Namespace="AjaxControlToolkit.HTMLEditor"
    TagPrefix="cc1" %>

<cc1:Editor 
    ID="Editor1" 
    Content='<%# FieldValueEditString %>'
    runat="server" />
    
<asp:RequiredFieldValidator 
    runat="server" 
    ID="RequiredFieldValidator1" 
    CssClass="droplist" 
    ControlToValidate="Editor1" 
    Display="Dynamic" 
    Enabled="false" />
    
<asp:RegularExpressionValidator 
    runat="server" 
    ID="RegularExpressionValidator1" 
    CssClass="droplist" 
    ControlToValidate="Editor1" 
    Display="Dynamic" 
    Enabled="false" />
    
<asp:DynamicValidator 
    runat="server" 
    ID="DynamicValidator1" 
    CssClass="droplist" 
    ControlToValidate="Editor1" 
    Display="Dynamic" />

Listing 3 – Html_Edit.ascx file

using System;
using System.Collections.Specialized;
using System.Web.UI;

namespace DD_HTMLEditor
{
    public partial class Html_EditField : System.Web.DynamicData.FieldTemplateUserControl
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            SetUpValidator(RequiredFieldValidator1);
            RequiredFieldValidator1.Text = "*";
            SetUpValidator(RegularExpressionValidator1);
            RegularExpressionValidator1.Text = "*";
            SetUpValidator(DynamicValidator1);
            DynamicValidator1.Text = "*";
        }

        protected override void ExtractValues(IOrderedDictionary dictionary)
        {
            dictionary[Column.Name] = ConvertEditedValue(Editor1.Content);
        }

        public override Control DataControl
        {
            get
            {
                return Editor1;
            }
        }
    }
}

Listing 4 – Html_Edit.ascx.cs file

Here again it’s just the Text_Edit FieldTemplate with a few minor changes (shown in BOLD ITALIC). First we added the AJAC Control Toolkit HTML Editor control to replace the TextBox control and added a reference to the FieldTemplate.

Note:

You could also add a reference for the AJAX Control Toolkit to the web.config

<pages>
    <controls>
        <add tagPrefix="ajaxToolkit"
            namespace="AjaxControlToolkit"
            assembly="AjaxControlToolkit"/>
    </controls>
</pages>

Description Field in Edit mode

Figure 2 – Description Field in Edit mode

Next Steps

Next we need to add some of the configurability of the HTML Editor via a custom Attribute.
using System;
using System.Linq;
using System.Web.DynamicData;
using AjaxControlToolkit.HTMLEditor;

namespace DD_HTMLEditor
{
    /// <summary>
    /// Attribute to identify which column to use as a 
    /// parent column for the child column to depend upon
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class HtmlEditorAttribute : Attribute
    {
        /// <summary>
        /// Default Contructor
        /// </summary>
        public HtmlEditorAttribute()
        {
        }

        /// <summary>
        /// Active editing panel (Design, Html, Preview) on control loaded 
        /// </summary>
        public AjaxControlToolkit.HTMLEditor.ActiveModeType ActiveMode { get; set; }

        /// <summary>
        /// If true, editing panel is focused and cursor is set 
        /// inside it ("Design" or "HTML text") on initial load 
        /// or editing panel change
        /// </summary>
        public Boolean AutoFocus { get; set; }

        /// <summary>
        /// A css class override used to define a custom look 
        /// and feel for the HTMLEditor. See the HTMLEditor 
        /// Theming section for more details
        /// </summary>
        public String CssClass { get; set; }

        /// <summary>
        /// Sets the path of additional CSS file used for 
        /// HTMLEditor's content rendering in "Design" panel. 
        /// If not set, the default CSS file is used which is 
        /// embedded as a WebResource and is a 
        /// part of the Toolkit assembly
        /// </summary>
        public String DesignPanelCssPath { get; set; }

        /// <summary>
        /// Sets the path of CSS file used for HTMLEditor's 
        /// content rendering in "Design" and "Preview" panels. 
        /// If not set, the default CSS file is used which is 
        /// embedded as a WebResource and is a part of the 
        /// Toolkit assembly
        /// </summary>
        public String DocumentCssPath  { get; set; }

        /// <summary>
        /// A css class override used to define a custom look for the 
        /// HTMLEditor's "HTML text" mode panel. See the HTMLEditor 
        /// Theming section for more details
        /// </summary>
        public String HtmlPanelCssClass  { get; set; }

        /// <summary>
        /// If true, Tab key navigation is suppressed inside 
        /// HTMLEditor control 
        /// </summary>
        public Boolean IgnoreTab  { get; set; }

        /// <summary>
        /// If true, HTMLEditor's content is cleaned up on 
        /// initial load. MS Word specific tags are removed 
        /// </summary>
        public Boolean InitialCleanUp  { get; set; }

        /// <summary>
        /// If true, JavaScript code is suppressed 
        /// in HTMLEditor's content 
        /// </summary>
        public Boolean NoScript  { get; set; }

        /// <summary>
        ///  If true, all Unicode characters in 
        ///  HTML content are replaced with &#code;
        /// </summary>
        public Boolean NoUnicode { get; set; }

        /// <summary>
        /// The client-side script that executes after 
        /// active mode (editing panel) changed
        /// </summary>
        public String OnClientActiveModeChanged { get; set; }

        /// <summary>
        /// The client-side script that executes before 
        /// active mode (editing panel) changed
        /// </summary>
        public String OnClientBeforeActiveModeChanged { get; set; }

        /// <summary>
        /// If true, no white spaces inserted on Tab key 
        /// press in Design mode. Default Tab key navigation 
        /// is processing in this case 
        /// </summary>
        public Boolean SuppressTabInDesignMode { get; set; }

        /// <summary>
        /// Sets the height of the body of the HTMLEditor 
        /// </summary>
        public int Height { get; set; }

        /// <summary>
        /// Sets the width of the body of the HTMLEditor 
        /// </summary>
        public int Width { get; set; }
    }

    public static partial class HelperExtansionMethods
    {
        /// <summary>
        /// Get the attribute or a default instance of the attribute
        /// if the Column attribute do not contain the attribute
        /// </summary>
        /// <typeparam name="T">
        /// Attribute type
        /// </typeparam>
        /// <param name="table">
        /// Column to search for the attribute on.
        /// </param>
        /// <returns>
        /// The found attribute or a default 
        /// instance of the attribute of type T
        /// </returns>
        public static T GetAttributeOrDefault<T>(this MetaColumn column) where T : Attribute, new()
        {
            return column.Attributes.OfType<T>().DefaultIfEmpty(new T()).FirstOrDefault();
        }
    }
}

Listing 5 – HtmlEditorAttribute

var htmlAttr = Column.GetAttributeOrDefault<HtmlEditorAttribute>();
if (htmlAttr.ActiveMode != ActiveModeType.Design)
    Editor1.ActiveMode = htmlAttr.ActiveMode;
if (htmlAttr.AutoFocus)
    Editor1.AutoFocus = true;
if (htmlAttr.CssClass != null)
    Editor1.CssClass = htmlAttr.CssClass;
if (htmlAttr.DesignPanelCssPath != null)
    Editor1.DesignPanelCssPath = htmlAttr.DesignPanelCssPath;
if (htmlAttr.DocumentCssPath != null)
    Editor1.DocumentCssPath = htmlAttr.DocumentCssPath;
if (htmlAttr.Height > 0)
    Editor1.Height = htmlAttr.Height;
if (htmlAttr.HtmlPanelCssClass != null)
    Editor1.HtmlPanelCssClass = htmlAttr.HtmlPanelCssClass;
if (htmlAttr.IgnoreTab)
    Editor1.IgnoreTab = true;
if (htmlAttr.InitialCleanUp)
    Editor1.InitialCleanUp = true;
if (htmlAttr.NoScript)
    Editor1.NoScript = true;
if (htmlAttr.NoUnicode)
    Editor1.NoUnicode = true;
if (htmlAttr.OnClientActiveModeChanged != null)
    Editor1.OnClientActiveModeChanged = htmlAttr.OnClientActiveModeChanged;
if (htmlAttr.OnClientBeforeActiveModeChanged != null)
    Editor1.OnClientBeforeActiveModeChanged = htmlAttr.OnClientBeforeActiveModeChanged;
if (htmlAttr.SuppressTabInDesignMode)
    Editor1.SuppressTabInDesignMode = true;
if (htmlAttr.Width > 0)
    Editor1.Width = htmlAttr.Width;

Listing 6 – Attribute setting for Html_Edit.ascx

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using AjaxControlToolkit.HTMLEditor;

namespace DD_HTMLEditor.Models
{
    [MetadataType(typeof(CategoryMD))]
    public partial class Category 
    {
        public partial class CategoryMD
        {
            public object CategoryID {get;set;}
            public object CategoryName {get;set;}

            [UIHint("Html")]
            [HtmlEditor(
                ActiveMode=ActiveModeType.Preview,
                Width=200,
                Height=100
                )]
            public object Description {get;set;}
            public object Picture {get;set;}
            public object Products {get;set;}
        }
    }
}

Listing 7 – Metadata

Above we have the attribute Listing 5 and the code Listing 6 to set the properties of the Html Editor in the Html_Edit.ascx FieldTemplate’s Page_Load event, and finally Listing 7 sample metadata.

Attribute from the Metadata applied

Figure 3 – Attribute from the Metadata applied

Not Rocket Science but a cool Html Editor (Thanks AJAX Toolkit Guys).

download

Sunday, 31 May 2009

Sorting Filters in Dynamic Data (DD v1.0)

This is just a small sample of how to sort your filters in Dynamic Data using the ColumnOrderAttribute from the Dynamic Data Futures project. I was surprised at how easy it was and that I’d not thought of it before.

using System;
using System.Linq;
using System.Web.DynamicData;

namespace DynamicData.CascadeExtensions
{
    /// <summary>
    /// Allows to specify the ordering of columns. 
/// Columns are will be sorted in increasing order based
/// on the Order value. Columns without this attribute
/// have a default Order of 0. Negative values are
/// allowed and can be used to place a column before all other columns. /// </summary> [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field,
Inherited = true,
AllowMultiple = false)] public class ColumnOrderAttribute : Attribute, IComparable { public static ColumnOrderAttribute Default = new ColumnOrderAttribute(0); public ColumnOrderAttribute(int order) { Order = order; } /// <summary> /// The ordering of a column. Can be negative. /// </summary> public int Order { get; private set; } public int ListOrder { get; set; } public int CompareTo(object obj) { return Order - ((ColumnOrderAttribute)obj).Order; } } public static partial class HelperExtansionMethods { public static ColumnOrderAttribute GetColumnOrdering(this MetaColumn column) { return column.Attributes.OfType<ColumnOrderAttribute>()
.DefaultIfEmpty(ColumnOrderAttribute.Default).First(); } } }

Listing 1 - Column Order attribute

So Listing 1 is just a direct rip off of the ColumnOrderAttribute in the Futures project. and then the code to order the filters based on the ColumnOrderAttribute.

protected void Page_Load(object sender, EventArgs e)
{
    table = GridDataSource.GetTable();
    Title = table.DisplayName;

    InsertHyperLink.NavigateUrl = table.GetActionPath(PageAction.Insert);

    // Disable various options if the table is readonly
    if (table.IsReadOnly)
    {
        GridView1.Columns[0].Visible = false;
        InsertHyperLink.Visible = false;
    }

    // set the filter order
    var filters = from c in table.Columns
                  where c is MetaForeignKeyColumn
                  orderby c.GetColumnOrdering()
                  select c;
    FilterRepeater.DataSource = filters;
    FilterRepeater.DataBind();
}

Listing 2 – modified Page_Load event handler

Here in Listing 2 you can see the code highlighted in bold italic is setting the filter sort order simples.

Note: the only caveat is that the filters are generated twice smile_sad

That caveat in mind I decided to do this instead:

public class SortedFilterRepeater : FilterRepeater
{
    protected override IEnumerable<MetaColumn> GetFilteredColumns()
    {
        // sort the filters by their filter order as specified in FilterAttribute.
        // Table.Columns.Where(c => IsFilterableColumn(c))
// .OrderBy(column => FilterOrdering(column));
var filteredColumns = from c in Table.Columns where IsFilterableColumn(c) orderby FilterOrdering(c) select c; return filteredColumns; } protected bool IsFilterableColumn(MetaColumn column) { // don't filter custom properties by default if (column.IsCustomProperty) return false; // always filter FK columns and bools if (column is MetaForeignKeyColumn) return true; if (column.ColumnType == typeof(bool)) return true; return false; } private ColumnOrderAttribute FilterOrdering(MetaColumn column) { return column.Attributes
.OfType<ColumnOrderAttribute>()
.DefaultIfEmpty(ColumnOrderAttribute.Default)
.First(); } }

Listing 3 – SortedFilterRepeater

Updated: Listing 3 is just a ripp-off on the AdvancedFilterRepeater in the Dynamic Data Futures sample.

I implemented this using by adding a mapping to the web.config as in Listing 4.

<pages>
    <tagMapping>
        <add tagType="System.Web.DynamicData.FilterRepeater"
mappedTagType="DynamicData.CascadeExtensions.SortedFilterRepeater" /> </tagMapping> </pages>

Listing 4 – Adding mapping to web.config

The advantage of the above is:

  1. It only generates the filters once
  2. This will work with my CascadingFilters

So I think this addendum wraps this up as we have sorting of filters in Dynamic Data Preview 4.

Saturday, 30 May 2009

Move Command Link Column to End Column – Dynamic Data

Whilst answering this question Move Edit, Details, Delete (col 0) to the Rightmost column of table on the Dynamic Data Forum I found this had been answered before (Move GridView command column or dynamically append columns) and decided to post it to my blog in full for easy access.

The post suggest using IAutoFieldGenerator to add the command column to the end like this:

public ICollection GenerateFields(Control control)
{
    // Get all of table's columns, take only the ones that should be automatically included in a fields collection,
    // sort the result by the ColumnOrderAttribute, and for each column create a DynamicField
    var fields = from column in _table.Columns
                 where IncludeField(column)
                 orderby ColumnOrdering(column), column.Name
                 select new DynamicField()
                 {
                     DataField = column.Name,
                     HeaderText = column.DisplayName
                 };

    List<DynamicField> flds = fields.ToList();
    if (_table.PrimaryKeyColumns.Count > 0)
    {
        // get the first primary key field                
        DynamicField ctrl = new DynamicField();
        ctrl.HeaderText = "Commands";
        ctrl.DataField = _table.PrimaryKeyColumns[0].Name;
        ctrl.UIHint = "GridCommand";
        flds.Add(ctrl);
    }

    return flds;
}

Listing 1 – GenerateFields method of the IAutoFieldGenerator

The main change to my usual IAutoFieldGenerator is shown in Bold Italic this works by only adding the GridCommand FieldTemplate if the table a=has a Primary Key.

<asp:HyperLink 
    ID="EditHyperLink" 
    runat="server" 
    NavigateUrl='<%# Table.GetActionPath(PageAction.Edit, Row) %>' 
    Text="Edit" />
&nbsp;
<asp:LinkButton 
    ID="DeleteLinkButton" 
    runat="server" 
    CommandName="Delete" 
    CausesValidation="false" 
    Text="Delete" 
    OnClientClick='return confirm("Are you sure you want to delete this item?");' />
&nbsp;
<asp:HyperLink 
    ID="DetailsHyperLink" 
    runat="server" 
    NavigateUrl='<%# Table.GetActionPath(PageAction.Details, Row) %>' 
    Text="Details" />

Listing 2 – GridCommand FieldTemplate

In Listing 2 we see the GridCommand FieldTemplate layed out neatly (you may need to tidy it so that it is similar to the section it is copied form in the List page. >&nbsp;<)

Download

The download contains the full IAutoFieldGenerator and GridCommand FieldTemplate plus Column sort capability.

Wednesday, 20 May 2009

Password FieldTemplates for Dynamic Data (UPDATED)

See this thread on the Dynamic Data Forum where I suggested you could use [DataType(DataType.Password)] attribute to mark the field as a password field and alternative would be to create a custom FieldTemplate and use [UIHint("Password")] to set it.

This just means the extra logic I implement for the password is only used on password fields and not all Text fields.

<%@ Control 
    Language="C#" 
    CodeBehind="Password.ascx.cs" 
    Inherits="DD_EF4_PasswordField.PasswordField" %>

<asp:Literal runat="server" ID="Literal1" />

Listing 1 – Password.ascx

using System;
using System.Web.UI;

namespace DD_EF4_PasswordField
{
    public partial class PasswordField : System.Web.DynamicData.FieldTemplateUserControl
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            // add a string of '*' the max length of the field
            var length = Column.MaxLength > 20 ? 20 : Column.MaxLength;
            Literal1.Text = new String('*', length);
        }

        public override Control DataControl
        {
            get
            {
                return Literal1;
            }
        }
    }
}

Listing 2 – Password.ascx.cs

Listing 1 & 2 represent the Password (Normal) FieldTemplate.

This is FieldTemplate just displays a list of '*' the max length of the field instead of the actual text.

<%@ Control 
    Language="C#" 
    CodeBehind="Password_Edit.ascx.cs" 
    Inherits="DD_EF4_PasswordField.Password_EditField" %>

<asp:TextBox 
    ID="TextBox1" 
    runat="server" 
    TextMode="Password"
    CssClass="DDTextBox" >
</asp:TextBox>

<asp:RequiredFieldValidator 
    runat="server" 
    ID="RequiredFieldValidator1"
    CssClass="DDControl" 
    ControlToValidate="TextBox1" 
    Display="Dynamic" 
    Enabled="false" />
<asp:RegularExpressionValidator 
    runat="server" 
    ID="RegularExpressionValidator1" 
    CssClass="DDControl" 
    ControlToValidate="TextBox1" 
    Display="Dynamic" 
    Enabled="false" />
<asp:DynamicValidator 
    runat="server" 
    ID="DynamicValidator1" 
    CssClass="DDControl" 
    ControlToValidate="TextBox1" 
    Display="Dynamic" />

Listing 3 – Password_Edit.ascx

using System;
using System.Collections.Specialized;
using System.Web.DynamicData;
using System.Web.UI;

namespace DD_EF4_PasswordField
{
    public partial class Password_EditField : FieldTemplateUserControl
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (Column.MaxLength < 20)
            {
                TextBox1.Columns = Column.MaxLength;
            }
            TextBox1.ToolTip = Column.Description;

            SetUpValidator(RequiredFieldValidator1);
            SetUpValidator(RegularExpressionValidator1);
            SetUpValidator(DynamicValidator1);
        }

        protected override void OnDataBinding(EventArgs e)
        {
            base.OnDataBinding(e);
            TextBox1.MaxLength = Math.Max(FieldValueEditString.Length, Column.MaxLength);
        }

        protected override void ExtractValues(IOrderedDictionary dictionary)
        {
            if (TextBox1.Text.Length > 0)
                dictionary[Column.Name] = ConvertEditedValue(TextBox1.Text);
        }

        public override Control DataControl
        {
            get
            {
                return TextBox1;
            }
        }
    }
}

Listing 4 – Password_Edit.ascx.cs

Listings 3 & 4 are for the Edit version Password_Edit FieldTemplate.

Updated: Listing 4 has been updated with the line if (TextBox1.Text.Length > 0) so that in edit mode if you do not enter a value it will not return an empty string.

Here the TextBox’s TextMode is set to Password so that any password you type in is hidden.

Note: The TextBox does not show any text in the TextBox.Text property when in Password mode this is normal behaviour.