MHendren
MHendren

Reputation: 51

Dynamic ItemTemplate Within GridView with a dynamic drop down list column

I'm trying to create dynamic columns in an asp.net gridview, and within those dynamic columns I need to insert a dynamic dropdownlist. The end result should look something like this:

enter image description here

Notice the top right of this grid where it has HSE1, HSE2, etc. These columns will change meaning in the example above I may have an additional HSE8, HSE9, etc. Basically the columns are dynamic. In order to do that I've done the following, here is my asp.net markup for the gridview:

 <asp:GridView ID="gvMain" runat="server"
    AutoGenerateColumns="False"
    Width="100%" DataKeyNames="AreaLevelCostCenterFunctionID" OnPreRender="gvMain_PreRender" >
    <Columns>
        <asp:TemplateField HeaderText="JobTitle" >
            <ItemTemplate>
                <asp:HyperLink CssClass="loaderLink" ID="hlAreaLevel" runat="server" NavigateUrl='<%# "/Views/Training/AreaLevel/Details.aspx?AreaLevelID=" + 
                    Eval("AreaLevelCostCenter.AreaLevelID") %>' Text='<%# Eval( "AreaLevelCostCenter.AreaLevel.AreaLevel1" )%>' ToolTip="Area Level">
                </asp:HyperLink>
            </ItemTemplate>
            <ItemStyle HorizontalAlign="Left"></ItemStyle>
            <HeaderStyle HorizontalAlign="Left"></HeaderStyle>
        </asp:TemplateField>

        <!--HERE'S WHERE I ADD MY DYNAMIC COLUMNS -->
    </Columns>
    <PagerSettings Position="TopAndBottom" />
</asp:GridView>

In order to fill this gridview (bind it) I use the following:

private void SetGvMainGridData(IEnumerable<AreaLevel> newNameList)
{
    if (newNameList != null)
    {
        BuildGridAdditionalColumns();
        gvMain.DataSource = newNameList;
        gvMain.DataBind();
        upData.Update();
    }
}

My issue is every time I page the data or post back to filter the data the number of columns is repeated. It recreates the same columns over and over every time. So I wrote some code to remove this but I dont think it looks very good. The parameter newNameList is a list of the HSE 1, HSE 2, etc courses. Here is what BuildGridAdditionalColumns does:

private void BuildGridAdditionalColumns()
{

   using (var db = DataCenterAccess.NewConnection())
    {
        var tManager = new TrainingManager(db, CurrentUser);
         //get the courses, THE HSE 1, HSE 2, etc...
        var preDefinedCourses = tManager.GetTrainingPredefinedCourses();
        if (preDefinedCourses != null && preDefinedCourses.Any())
        {
            foreach (var course in preDefinedCourses)
            {
                // Declare the template field
                TemplateField bfield = new TemplateField();
                //Initalize the DataField value.
                bfield.ItemTemplate = new GridViewTemplate(ListItemType.Item, course.TrainingPredefinedCourse1);
                //Initialize the HeaderText field value.
                bfield.HeaderText = course.TrainingPredefinedCourse1;

                //Add the newly created bound field to the GridView.
                gvMain.Columns.Add(bfield);
            }
        }
    }
}

Notice how I did bfield.ItemTemplate = new GridViewTemplate(ListItemType.Item, course.TrainingPredefinedCourse1); I had to create an interface to support this see this stackoverflow post for more info: How to add TemplateField to a gridview in the code behind?

Basically that interface looks like this:

 public class GridViewTemplate : ITemplate
    {
        //A variable to hold the type of ListItemType.
        readonly ListItemType _templateType;
        //A variable to hold the column name.
        readonly string _columnName;
        //Constructor where we define the template type and column name.

        public GridViewTemplate(ListItemType type, string colname)
        {
            //Stores the template type.
            _templateType = type;

            //Stores the column name.
            _columnName = colname;

        }

        void ITemplate.InstantiateIn(Control container)
        {
            if (_templateType == ListItemType.Item)
            {
                //Creates a new drop down list control.
                DropDownList ddl = new DropDownList {ID = _columnName}; 
                ddl.Attributes.Add("class","chosen-select");
                ddl.Width = 70;
                //get the data to populate ddl
                var tList = Utilities.GetCourseKeys().ToList();
                var selectedValues = (from t in tList select new ListItem( t.KeyValue, t.TrainingPredefinedCourseKeyID.ToString())).ToList();
                //add empty option to items and bind
                selectedValues.Insert(0, new ListItem());
                ddl.DataSource = selectedValues;
                ddl.DataBind();
                // ddl.DataBinding += new EventHandler(tb1_DataBinding);   //Attaches the data binding event.
                container.Controls.Add(ddl); //Adds the newly created ddl to the container.
            }
        }
    }

I end up with basically two issues when I post back or refresh this data. One issue is the columns end up repeating themselves. The other issue is the binding of the data stops working (that is the x,y value becomes empty).

Upvotes: 3

Views: 2327

Answers (2)

VDWWD
VDWWD

Reputation: 35544

It works in my example whn you create the Dynamic TemplateFields in Page_Init and do the GridView DataBindind in Page_Load. I've tested it and if there is a PostBack, there are no extra columns added, all the DropDownLists still exist with all their data and their SelectedValues are shown correctly if they were changed before PostBack.

protected void Page_Init(object sender, EventArgs e)
{
    //create a loop for the dynamic fields
    for (int i = 1; i < 6; i++)
    {
        //create a new template field
        TemplateField field = new TemplateField();
        field.HeaderText = "HSE " + i;

        //create the new itemtemplate
        field.ItemTemplate = new GridViewTemplate(DataControlRowType.DataRow, "HSE_" + i);

        //add the field to the grid 
        GridView1.Columns.Add(field);
    }
}

protected void Page_Load(object sender, EventArgs e)
{
    //normal use of IsPostBack
    if (!IsPostBack)
    {
        //bind the gridview data
        GridView1.DataSource = LoadFromDataBase();
        GridView1.DataBind();
    }
}

protected void Button1_Click(object sender, EventArgs e)
{
    Button1.Text = "PostBack is done!";
}

public class GridViewTemplate : ITemplate
{
    private DataControlRowType templateType;
    private string columnName;

    public GridViewTemplate(DataControlRowType type, string colname)
    {
        templateType = type;
        columnName = colname;
    }

    public void InstantiateIn(System.Web.UI.Control container)
    {
        switch (templateType)
        {
            case DataControlRowType.DataRow:
                DropDownList list = new DropDownList();
                list.DataSource = LoadFromDataBase();
                list.DataTextField = "myText";
                list.DataValueField = "myValue";
                list.DataBind();
                container.Controls.Add(list);
                break;

            default:
                break;
        }

    }
}

The ASPX to make the example complete

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false"></asp:GridView>
<br />
<br />
<asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" />

Upvotes: 1

Ameer Ali Shar
Ameer Ali Shar

Reputation: 54

I think you should create entire grid dynamically look at below code

                    string tempnum = "txtbox";
                    TemplateField temp1 = new TemplateField();
                    temp1.HeaderStyle.Width = Unit.Pixel(101);
                    temp1.HeaderStyle.CssClass = "headerwidth";
                    temp1.HeaderText = "txt";
                    temp1.ItemTemplate = new CreateTextBox(tempnum);
                    GridView1.Columns.Add(temp1);

Upvotes: 1

Related Questions