Geeks With Blogs

News

Microsoft MVP


Moderator at CodeASP.NET


Quiz Master




free counters
Free counters
Added on January 19,2012


Follow Me @vmsdurano

A bit About Me



Disclaimer
The opinions expressed herein are my own personal opinions and does not represent the opinions of my employers. Nor does it represent the opinion of my dog, because I don’t have one.


Vinz' Blog (ProudMonkey) "Code, Beer and Music ~ my way of being a programmer"

Introduction:

This question has been asked many times at the forums (http://forums.asp.net) and definitely there are already bunch of different solutions provided. Most of the examples from the web are using DataSource controls (e.g SqlDataSource, ObjectDataSource, etc..) to implement cascading DropDownList in GridView and I can’t seem to find a formal example that demonstrate how to implement it without using DataSource controls.

Note: Before you proceed, make sure that you already know the basics of using the GridView control and how to bind it with data as I will not include the details of doing edit,update or delete scenarios for this exercise.

Scenario:

As a recap, Cascading DropDownList enables a common scenario in which the contents of one list depends on the selection of another list. In this demo we will show a list of Product orders in GridView and when the user edits the row, we will present the user with a DropDownList containing the list of available Makes of products. Once they have made their selection, we will populate the second DropDownList with the available Models for the specific Make they've chosen. And once they have chosen the Model from the second DropDownList we will then update the Price field for that corresponding Model.

ASPX Markup:

In this post will take a look at how to possibly do this. To get started let’s setup our HTML .aspx markup. For the simplicity of this demo, I just set it up like this:

<asp:GridView ID="gvProducts" runat="server" AutoGenerateColumns="false" DataKeyNames="ProductOrderID"
                    onrowcancelingedit="gvProducts_RowCancelingEdit" 
                    onrowediting="gvProducts_RowEditing"
                    onrowupdating="gvProducts_RowUpdating" 
                    onrowdatabound="gvProducts_RowDataBound">
        <Columns>
            <asp:BoundField DataField="ProductOrderID"  ReadOnly="true"/>
            <asp:TemplateField HeaderText="Make">
                <ItemTemplate>
                       <asp:Label ID="lblMake" runat="server" Text='<%# Bind("Make") %>' />
                </ItemTemplate>
                <EditItemTemplate>
                    <asp:DropDownList ID="ddlProductMake" runat="server"
                                      OnSelectedIndexChanged="ddlProductMake_SelectedIndexChanged"
                                      AutoPostBack="true">
                    </asp:DropDownList>
                </EditItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Model">
                <ItemTemplate>
                     <asp:Label ID="lblModel" runat="server" Text='<%# Bind("Model") %>' />
                </ItemTemplate>
                <EditItemTemplate>
                    <asp:DropDownList ID="ddlProductModel" runat="server"
                                      OnSelectedIndexChanged="ddlProductModel_SelectedIndexChanged"
                                      AutoPostBack="true">
                    </asp:DropDownList>
                </EditItemTemplate>
           </asp:TemplateField>
           <asp:BoundField DataField="Price" HeaderText="Price" ReadOnly="True" DataFormatString="{0:c}"/>
           <asp:BoundField DataField="Quantity" HeaderText="Quantity"/>
           <asp:CommandField ShowEditButton="True" />
        </Columns>
    </asp:GridView>

The markup above composed of 6 columns of different type of control fields. The first column is a BoundField which holds the ProductOrderID field. You may notice that we set this field as the DataKeyName in the GridView so we can easily reference the ID of the row later on. The 2nd and 3rd columns are TemplateFields that contains a Label control within an <ItemTemplate> and a DropDownList control within an <EditItemTemplate>. This means that on read-only state the Label will be displayed and on edit-state the DropDownList will be displayed to allow users to modify the selection of their choice. Note that you need to set AutoPostBack to TRUE for the DropDownList to trigger the SelectedIndexChanged event. The 4th and 5th columns are BoundFields which holds the Price and Quantity fields. You may notice that the Price field has an attribute DataFormatString="{0:c}" set to it. This will transform the value to a currency format when displayed in the browser. Finally, the last column is a CommandField with ShowEditButton enabled. This will generate the Edit link when the GridView is bounded with data.

The DataSource:

Just for the simplicity of this exercise, I didn’t use any Database and instead I’ve just created some classes that will holds some properties on it. Here are the class definitions:

    public class ProductMake
    {
        public int ProductMakeID { get; set; }
        public string Make { get; set; }
    }

    public class ProductModel
    {
        public int ProductModelID { get; set; }
        public int ProductMakeID { get; set; }
        public string Model { get; set; }
        public decimal Price { get; set; }
    }

    public class ProductOrder
    {
        public int ProductOrderID { get; set; }
        public int ProductMakeID { get; set; }
        public int ProductModelID { get; set; }
        public string Make { get; set; }
        public string Model { get; set; }
        public short Quantity { get; set; }
        public decimal Price { get; set; }
    }

Note that this is just an example to make this exercise work. You can always replace this with your DataTable , Entity objects, Custom classes, etc. that holds your actual source of data.

The ProductMake class holds two main properties. This is where we store the list of Makes of the product. The ProductModel class is where we store all the Models for each Makes. The ProductOrder class is where we store the list of orders the user has chosen. Now let’s go ahead and fill these class with some sample data. Here are the code blocks below:

        private List<ProductOrder> GetUserProductOrder() {
            List<ProductOrder> orders = new List<ProductOrder>();
            ProductOrder po = new ProductOrder();

            po.ProductOrderID = 1;
            po.ProductMakeID = 1;
            po.ProductModelID = 1;
            po.Make = "Apple";
            po.Model = "iPhone 4";
            po.Quantity = 2;
            po.Price = 499;
            orders.Add(po);

            po = new ProductOrder();
            po.ProductOrderID = 2;
            po.ProductMakeID = 2;
            po.ProductModelID = 4;
            po.Make = "Samsung";
            po.Model = "Galaxy S2";
            po.Quantity = 1;
            po.Price = 449;
            orders.Add(po);

            po = new ProductOrder();
            po.ProductOrderID = 3;
            po.ProductMakeID = 3;
            po.ProductModelID = 7;
            po.Make = "Nokia";
            po.Model = "Lumia";
            po.Quantity = 1;
            po.Price = 549;
            orders.Add(po);

            return orders;

        }

        private List<ProductMake> GetProductMakes() {

            List<ProductMake> products = new List<ProductMake>();
            ProductMake p = new ProductMake();

            p.ProductMakeID = 1;
            p.Make = "Apple";
            products.Add(p);

            p = new ProductMake();
            p.ProductMakeID = 2;
            p.Make = "Samsung";
            products.Add(p);

            p = new ProductMake();
            p.ProductMakeID = 3;
            p.Make = "Nokia";
            products.Add(p);

            return products;
        }

        private List<ProductModel> GetProductModels() {
            List<ProductModel> productModels = new List<ProductModel>();
            ProductModel pm = new ProductModel();

            pm.ProductMakeID = 1;
            pm.ProductModelID = 1;
            pm.Model = "iPhone 4";
            pm.Price = 499;
            productModels.Add(pm);

            pm = new ProductModel();
            pm.ProductMakeID = 1;
            pm.ProductModelID = 2;
            pm.Model = "iPhone 4s";
            pm.Price = 599;
            productModels.Add(pm);

            pm = new ProductModel();
            pm.ProductMakeID = 1;
            pm.ProductModelID = 3;
            pm.Model = "iPhone 5";
            pm.Price = 699;
            productModels.Add(pm);

            pm = new ProductModel();
            pm.ProductMakeID = 2;
            pm.ProductModelID = 4;
            pm.Model = "Galaxy S2";
            pm.Price = 449;
            productModels.Add(pm);

            pm = new ProductModel();
            pm.ProductMakeID = 2;
            pm.ProductModelID = 5;
            pm.Model = "Galaxy S3";
            pm.Price = 549;
            productModels.Add(pm);

            pm = new ProductModel();
            pm.ProductMakeID = 2;
            pm.ProductModelID = 6;
            pm.Model = "Galaxy Note2";
            pm.Price = 619;
            productModels.Add(pm);

            pm = new ProductModel();
            pm.ProductMakeID = 3;
            pm.ProductModelID = 7;
            pm.Model = "Nokia Lumia";
            pm.Price = 659;
            productModels.Add(pm);

            return productModels;

        }

        private List<ProductModel> GetProductModelByMake(int productMakeID) {
            var models = (from p in GetProductModels()
                          where p.ProductMakeID == productMakeID
                          select p);

            return models.ToList();
        }

The GetUserProductOrder() fetches all the list of orders. We will use this as our DataSource in the GridView later. The GetProductMakes() method gets all the available Makes which in this case we just added 3 main items on it. The GetProductModel() method gets all the available Models for each Makes. The GetProductModelByMake() method gets the specific Model item and its details based on the ProductMakeID. This method uses the LINQ syntax to query the DataSource based on the parameter that was passed in.

The Implementation:

Now looks like we already have some sample source of data to work on. Now let’s go ahead do the highlight of this exercise (which is the implementation of the cascading dropdownlist). Here are the code blocks below:

        private void BindGrid() {
            gvProducts.DataSource = GetUserProductOrder();
            gvProducts.DataBind();
        }

        protected void gvProducts_RowEditing(object sender, GridViewEditEventArgs e) {
            gvProducts.EditIndex = e.NewEditIndex;
            BindGrid();
        }

        protected void gvProducts_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e) {
            gvProducts.EditIndex = -1;
            BindGrid();
        }

        protected void gvProducts_RowDataBound(object sender, GridViewRowEventArgs e) {
            if (e.Row.RowType == DataControlRowType.DataRow) {
                if ((e.Row.RowState & DataControlRowState.Edit) > 0) {
                    DropDownList ddlMake = (DropDownList)e.Row.FindControl("ddlProductMake");
                    ddlMake.DataSource = GetProductMakes();
                    ddlMake.DataValueField = "ProductMakeID";
                    ddlMake.DataTextField = "Make";
                    ddlMake.DataBind();

                    ddlMake.SelectedValue = gvProducts.DataKeys[e.Row.RowIndex].Value.ToString();

                    DropDownList ddlModel = (DropDownList)e.Row.FindControl("ddlProductModel");
                    ddlModel.DataSource = GetProductModelByMake(Convert.ToInt32(gvProducts.DataKeys[e.Row.RowIndex].Value));
                    ddlModel.DataValueField = "ProductModelID";
                    ddlModel.DataTextField = "Model";
                    ddlModel.DataBind();

                    ddlModel.SelectedValue = GetProductModelByMake(Convert.ToInt32(gvProducts.DataKeys[e.Row.RowIndex].Value))
                                             .FirstOrDefault().ProductModelID.ToString();

                }
            }
        }

        protected void ddlProductMake_SelectedIndexChanged(object sender, EventArgs e) {
            DropDownList ddlMake = (DropDownList)sender;
            GridViewRow row = (GridViewRow)ddlMake.NamingContainer;
            if (row != null) {
                if ((row.RowState & DataControlRowState.Edit) > 0) {
                    DropDownList ddlModel = (DropDownList)row.FindControl("ddlProductModel");
                    ddlModel.DataSource = GetProductModelByMake(Convert.ToInt32(ddlMake.SelectedValue));
                    ddlModel.DataValueField = "ProductModelID";
                    ddlModel.DataTextField = "Model";
                    ddlModel.DataBind();
                }
            }
        }

        protected void ddlProductModel_SelectedIndexChanged(object sender, EventArgs e) {
            DropDownList ddlModel = (DropDownList)sender;
            GridViewRow row = (GridViewRow)ddlModel.NamingContainer;
            if (row != null) {
                if ((row.RowState & DataControlRowState.Edit) > 0) {
                    row.Cells[3].Text = string.Format("{0:C}", GetProductModels()
                                        .Where(o => o.ProductModelID == Convert.ToInt32(ddlModel.SelectedValue))
                                        .FirstOrDefault().Price);
                }
            }
        }

The gvProducts_RowDataBound event is where we bind the DropDownList with the data from our DataSource. First we check the RowType to make sure that we are only manipulating the rows of type DataRow. Please note that GridView is composed of several Row types such as Header,DataRow,EmptyDataRow,Footer,Pager and Separator. The next line in that block is the critical part of the code and that is to determine the Edit state.

Accessing the controls from within <EditItemTemplate> is a bit tricky especially if you are not really familiar on how stuff works within GridView. Equating the RowState to DataControlState.Edit isn’t really accurate and you might get an exception when doing so. The RowState property is a bitwise combination. Therefore, the RowState could indicate that you are in the Edit state and the Alternate state. Hence, you cannot do a simple equality check when you are on Edit mode. Instead you must do something like this:

if ((e.Row.RowState & DataControlRowState.Edit) > 0) {
   //do your stuff here
}

We use the bitwise “&” operator to determine if the GridView is in Edit mode and  check the result if its  greater than zero. For details about Bitwise operator see: Bitwise Operators in C#

Once we’ve manage to determine the edit-state then we can now begin accessing the controls using FindControl() method and bind it with the corresponding DataSources. If you notice, I’ve set the SelectedValue for the ddlMake and ddlModel DropDownLists so that by the time the user clicks Edit the DropDownList will be pre-selected with the previous item the user has chosen.

The ddlProductMake_SelectedIndexChanged event is where we actually do the cascading feature by populating the second DropDownList based from the value selected from the first DropDownList. But before that we need to cast the sender of the object who triggers the event to determine which DropDownList is triggered within the GridView row. We then cast the NamingContainer of the sender to a type of GridViewRow to determine the actual row the user is editing.

The ddlProductModel_SelectedIndexChanged event is where we update the Price value based on the selected Model from the second DropDownList. It basically uses LINQ syntax to query the DataSource and to get Price based on the selected value of the ddlModel.

Binding the GridView:

Finally let’s call the BindGrid() method to populate the GridView. Here’s the code block below:

        protected void Page_Load(object sender, EventArgs e) {
            if (!IsPostBack) {
                BindGrid();
            }
        }

The Output:

Here are some screenshots when running this code in the page:

gvd1

gvd2

gvd3

gvd4

gvd5

 

That’s it! I hope someone find this post useful! Winking smile

Technorati Tags: ,
Posted on Saturday, March 2, 2013 5:17 AM | Back to top


Comments on this post: GridView–Implementing Cascading DropDownList on Edit Mode

comments powered by Disqus

Copyright © Vincent Maverick Durano | Powered by: GeeksWithBlogs.net | Join free