Click here to Skip to main content
Licence CPOL
First Posted 4 Apr 2008
Views 25,106
Bookmarked 14 times

Creating Dynamic FormView Templates

By | 4 Apr 2008 | Article
How to create dynamic FormView templates.

Introduction

Typically when I think of caching I think of reusing data that has been extracted from a datasource; however, you can cache any type of object in ASP.NET and a dynamically generated FormView Template is a great object to use caching with.

I have an application that allows administrators of the application to add fields to it at runtime. Which means the forms for those fields need to be created at runtime also. The FormView allows you to dynamically generate templates at runtime using the IBindableTemplate; however, it seems really inefficient to dynamically generate the form when nothing has changed.

Using the Code

So here is an example of a FormView readonly item template. The logic is a little complicated because my layout is based on data types and what not. The point is that you can do this with any template layout.

//            
public partial class ItemTemplate : IBindableTemplate
{
    Constants constants = new Constants();
    TextModification textModification = new TextModification();
    LinqData linqData = new LinqData();
    DataTable GetColumnInformation = new DataTable();
    TaskDataSet tds = new TaskDataSet();
    private string _tableName;

    public string tableName
    {
        get { return _tableName; }
        set 
        { 
            _tableName = value;
            //This is where the schema is for my layout
            GetColumnInformation = linqData.GetColumnsForTable(_tableName);
        }
    }

    void ITemplate.InstantiateIn(Control container)
    {
        Panel myPanel = new Panel();
        DataTable table = new DataTable();
        table = GetColumnInformation;
        Table itemTable = new Table();
        itemTable.CssClass = "formView";
        int index = 0;
        int rowIndex = 0;
        bool needNewRow = false;
        ArrayList textColumnNames = new ArrayList();
        ArrayList textColumnDisplayNames = new ArrayList();
        bool needTabPanel = false;

        //Loop through the schema to create the layout.
        foreach (DataRow col in table.Rows)
        {
            TableCell newCell = new TableCell();
            newCell.Width = Unit.Percentage(20);
            string columnName = col.ItemArray.GetValue(0).ToString();
            string colDataType = col.ItemArray.GetValue(1).ToString();
            string colDetails = col.ItemArray.GetValue(2).ToString();
            string columnDisplayName = col.ItemArray.GetValue(3).ToString();
            bool textColumn = colDetails.ToLower().Contains("text");
            TableRow firstRow;

            if (constants.showItemMode(columnName, tableName))
            {
                if ((index & 1) == 0 || needNewRow == true || 
                     textColumn || columnName == "created_by")
                {
                    firstRow = new TableRow();
                    itemTable.Rows.Add(firstRow);
                    needNewRow = false;
                    if (index != 0)
                    {
                        rowIndex++;
                    }
                    else
                    {
                        needNewRow = true;
                    }
                    index = 2;
                }
                else
                {
                    firstRow = itemTable.Rows[rowIndex];
                }
                if (textColumn)
                {
                    textColumnDisplayNames.Add(columnDisplayName);
                    textColumnNames.Add(columnName);
                    needTabPanel = true;
                }
                else
                {
                    newCell.Text = columnDisplayName;
                    firstRow.Cells.Add(newCell);
                    Label firstNameLabel = new Label();
                    if (columnName.Contains("_id") && columnName != tableName + "_id")
                    {
                        firstNameLabel.ID = columnName.Replace("_id", "_name") + "Label";
                    }
                    else
                    {
                        firstNameLabel.ID = columnName + "Label";
                    }
                    firstNameLabel.DataBinding += new EventHandler(Label_DataBinding);
                    TableCell anotherNewCell = new TableCell();
                    anotherNewCell.Controls.Add(firstNameLabel);
                    firstRow.Cells.Add(anotherNewCell);
                }
                index++;
            }
        }
        TableRow newRow = new TableRow();
        itemTable.Controls.Add(newRow);
        TableCell tableCell = new TableCell();
        tableCell.ColumnSpan = 4;
        EditButton updateButton = new EditButton();
        updateButton.ID = "defaultButton";
        tableCell.Controls.Add(updateButton);
        newRow.Cells.Add(tableCell);
        myPanel.Controls.Add(itemTable);
        myPanel.DefaultButton = "defaultButton";
        myPanel.Controls.Add(new LiteralControl("<br />"));

        //Add notes Tab Panel
        if (needTabPanel == true)
        {
            myPanel.Controls.Add(CreateTabPanelForNotes(tableName, 
                    textColumnNames, textColumnDisplayNames));
        }
        container.Controls.Add(myPanel);
    }

    public TabContainer CreateTabPanelForNotes(string tableName, 
           ArrayList textColumnNames, ArrayList textColumnDisplayNames)
    {
        TabContainer myTabContainer = new TabContainer();
        myTabContainer.ID = tableName + "TabContainer";

        for (int i = 0; i < textColumnDisplayNames.Count; i++)
        { 
            TabPanel myTabPanel = new TabPanel();
            myTabPanel.ID = textColumnNames[i].ToString() + "TabPanel";
            myTabPanel.HeaderText = textColumnDisplayNames[i].ToString(); 
            TextBox firstNameLabel = new TextBox();
            firstNameLabel.ID = textColumnNames[i].ToString() + "TextBox";
            firstNameLabel.TextMode = TextBoxMode.MultiLine;
            firstNameLabel.Rows = 25;
            firstNameLabel.Width = Unit.Percentage(100);
            firstNameLabel.ReadOnly = true;
            firstNameLabel.CssClass = "readOnlyTextBox";
            firstNameLabel.DataBinding += new EventHandler(TextBox_DataBinding);
            myTabPanel.Controls.Add(firstNameLabel);
            myTabContainer.Tabs.Add(myTabPanel);
            myTabContainer.TabIndex = 0;
        }
        return myTabContainer;
    }

    private void Label_DataBinding(Object sender, EventArgs e)
    {
        Label firstNameLabelControl = (Label)sender;
        FormView formViewContainer = (FormView)firstNameLabelControl.NamingContainer;
        string columnName = firstNameLabelControl.ID.Replace("Label", "");
        string addLineBreaksToText = "";
        addLineBreaksToText = linqData.GetDataForFormViewBoundControl(formViewContainer, 
           columnName).ToString().Replace(Environment.NewLine, "<br />");
        firstNameLabelControl.Text = addLineBreaksToText;
    }

    private void TextBox_DataBinding(Object sender, EventArgs e)
    {
        TextBox firstNameLabelControl = (TextBox)sender;
        FormView formViewContainer = null;
        if (firstNameLabelControl.NamingContainer.ToString() == 
               "System.Web.UI.WebControls.FormView")
        {
            formViewContainer = (FormView)firstNameLabelControl.NamingContainer;
        }
        else
        {
            formViewContainer = (FormView)
              firstNameLabelControl.NamingContainer.NamingContainer.NamingContainer;
        }

        string columnName = firstNameLabelControl.ID.Replace("TextBox", "");
        string addLineBreaksToText = "";
        addLineBreaksToText = linqData.GetDataForFormViewBoundControl(
                                  formViewContainer, columnName).ToString();
        firstNameLabelControl.Text = addLineBreaksToText;
    }
}

So this is how I implement the caching. My forms are based on the table names in the database so each template is cached with a reference to that table name.

protected void Page_Init(object sender, EventArgs e)
{
    string tableName = Request.QueryString["table_name"].ToLower();
    string id = Request.QueryString["id"];
    DataDataContext data = new DataDataContext();
    LinqDataSource1.TableName = tableName;

    LinqDataSource1.Where = tableName + "_id = " + id;

    FormView FormView1 = new FormView();
    FormView1.ID = "FormView1";
    FormView1.DataSourceID = "LinqDataSource1";
    FormView1.CssClass = "formView";

    if (id == "new")
    {
        InsertTemplate insertTemplate = null;
        // Try  to get it from the cache.
        try
        {
            insertTemplate = (InsertTemplate)HttpContext.Current.Cache.Get(
                 "formDefinitionInsertTemplate:" + tableName);
        }
        finally
        {
            //Check to see if it was retreived from the cache
            if (insertTemplate == null)
            {
                //If there wasn't anything in the cache go get a new template.
                insertTemplate = new InsertTemplate();
                insertTemplate.tableName = tableName;
                HttpContext.Current.Cache.Insert("formDefinitionInsertTemplate:" + 
                                                 tableName, insertTemplate);
            }
        }
        FormView1.InsertItemTemplate = insertTemplate;
        FormView1.Caption = "Add New " + textModification.TurnFirstToUpper(tableName);
        FormView1.ChangeMode(FormViewMode.Insert);
    }
    else
    {
        FormView1.ItemUpdated += new FormViewUpdatedEventHandler(FormView1_ItemUpdated);
        ItemTemplate itemTemplate = null;
        try
        {
            itemTemplate = (ItemTemplate)Cache.Get("formDefinitionItemTemplate:" + tableName);
        }
        finally
        {
            if (itemTemplate == null)
            {
                itemTemplate = new ItemTemplate();
                itemTemplate.tableName = tableName;
                Cache.Insert("formDefinitionItemTemplate:" + tableName, itemTemplate);
            }
        }
        EditTemplate editTemplate = null;
        try
        {
            editTemplate = 
              (EditTemplate)Cache.Get("formDefinitionEditTemplate:" + tableName);
        }
        finally
        {
            if (editTemplate == null)
            {
                editTemplate = new EditTemplate();
                editTemplate.tableName = tableName;
                Cache.Insert("formDefinitionEditTemplate:" + tableName, editTemplate);
            }
        }
        InsertTemplate insertTemplate = null;
        try
        {
            insertTemplate = 
              (InsertTemplate)Cache.Get("formDefinitionInsertTemplate:" + tableName);
        }
        finally
        {
            if (insertTemplate == null)
            {
                insertTemplate = new InsertTemplate();
                insertTemplate.tableName = tableName;
                Cache.Insert("formDefinitionInsertTemplate:" + tableName, insertTemplate);
            }
        }
        //Add the templates to the form
        FormView1.ItemTemplate = itemTemplate;
        FormView1.EditItemTemplate = editTemplate;
        FormView1.InsertItemTemplate = insertTemplate;
        FormView1.Caption = textModification.TurnFirstToUpper(tableName) + " Details";
    }

    //Add the form to the parent control
    myContainer.Controls.Add(FormView1);
}

So now that you have cached information, you won't need to recreate the template unless something changes. When something does change, all you need to do is remove the item from the cache using the code below:

Cache.Remove("formDefinitionItemTemplate:" + tableName);

I received a couple of questions about filling the template with data, so here is how it works. After creating the label or textbox, you add an event handler to call on databinding.

//
Label myLabel = new Label();
myLabel.DataBinding += new EventHandler(Label_DataBinding);

The code below responds when that event happens and uses the existing data to populate the control. The existing data would come from the data source that is bound to the FormView. The caveat is that the naming convention of your label must match the property name that comes from the datasource. There are other ways to accomplish this but I just use the ID of the control with the name of the control after it. Then I strip off the name of the control so I can retrieve the data.

private void Label_DataBinding(Object sender, EventArgs e)
{
    Label myLabel = (Label)sender;
    FormView formViewContainer = (FormView)myLabel.NamingContainer;
    string columnName = myLabel.ID.Replace("Label", "");
    DataRowView rowData = (DataRowView)formViewContainer.DataItem;
    string textValue = rowData[columnName].ToString();
    myLabel.Text = textValue;
}

Points of Interest

This performs as well as hardcods the values in the form as far as I can tell, and now I don't have to make changes when fields are added.

History

I added information about filling the label with content on databinding.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Ben Kotvis



United States United States

Member



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralSample Application PinmemberArthur Gladney9:48 26 Jun '08  
AnswerAdded source code for filling controls with data. PinmemberBen Kotvis4:38 4 Apr '08  
GeneralCool Concept! Question... PinmemberKevin Jensen3:44 4 Apr '08  
GeneralSource code missing Pinmember leppie 3:37 4 Apr '08  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web03 | 2.5.120528.1 | Last Updated 4 Apr 2008
Article Copyright 2008 by Ben Kotvis
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid