Cascading DropDownList on GridView

Cascading DropDownLists on GridView

Cascading DropDownLists on GridView


In this Blog, we will learn how to get the current Row of GridView inside the SelectedIndexChanged Event of a DropDownList present on the GridView itself and then find the other DropDownList and bind that.

Background

The requirement came up in one of my projects to Bind one DropDownList present on a Column based on the selection of DropDownList present on another Column. So, it was like the Cascading DropDownLists, where both the DropDownLists are present on the same GridView.

So, steps to get the solution would be like…

  1. Attach the SelectedIndexChanged Event for the first DropDownList on GridView
  2. Inside the Event, first get the GridView Row, from which the first DropDownList is selected
  3. Then from the current GridView Row, find the second DropDownList and bind it with a DataTable or something

The step which is bold is very important. Let’s go step by step.

GridView Markup

In the Markup, you can see that I have two DropDownLists Declared inside the GridView. One is for Country and another one for City. Just for demo purpose, I have hard coded the Items for Country. You can dynamically populate that using code.

Note: According to Step – 1, I have attached the OnSelectedIndexChanged Event to the Country DropDownList.
Goal: Our goal is to populate the City DropDownList for the particular row, when Country is selected in that row.

<asp:GridView ID="gvWithCascadingDropDownList" runat="server" AutoGenerateColumns="false">
	<Columns>
		<asp:BoundField DataField="RowNumber" />
		<asp:TemplateField HeaderText="Country">
			<ItemTemplate>
				<asp:DropDownList 
                        ID="ddlCountry" runat="server" 
                        AutoPostBack="true" 
                        OnSelectedIndexChanged="ddlCountry_SelectedIndexChanged">
					<asp:ListItem Text="-1">Select Country</asp:ListItem>
					<asp:ListItem Text="India">India</asp:ListItem>
					<asp:ListItem Text="Nepal">Nepal</asp:ListItem>
					<asp:ListItem Text="Bangladesh">Bangladesh</asp:ListItem>
					<asp:ListItem Text="Sri lanka">Sri lanka</asp:ListItem>
				</asp:DropDownList>
			</ItemTemplate>
		</asp:TemplateField>
		<asp:TemplateField HeaderText="City">
			<ItemTemplate>
				<asp:DropDownList ID="ddlCity" runat="server">
				</asp:DropDownList>
			</ItemTemplate>
		</asp:TemplateField>
	</Columns>
</asp:GridView>

What’s Next?

Let’s write code inside the Country Change Event and populate the City DropDownList.
So, according to the Step – 2

Inside the Event, first get the GridView Row, from which the first DropDownList is selected

For the above, we have to get the current Country DropDownList first, which we can easily get by.

// Get the Country DropDownList first.
DropDownList ddlCountry = (DropDownList)sender;

Now the important Step – 3.

Then from the current GridView Row, find the second DropDownList and bind it with a DataTable or something

In order to get the Containing Row for the DropDownList, we have to use the Control.NamingContainer Property.

The naming container for a given control is the parent control above it in the hierarchy that implements the INamingContainer interface. A server control that implements this interface creates a unique namespace for the ID property values of its child server controls. You can use the NamingContainer property of a naming container’s child control to get a reference to its parent container.

So, after getting the current GridView Row, now it is just a matter of finding the City DropDownList on that particular row and bind that with appropriate Data.

Below is the full code. I have used one Service to get the Cities Data. To use the Service, add Service Reference to your Project by right clicking on the Project in Visual Studio.

Service Reference Dialog

Service Reference Dialog


Service URL is – http://www.webservicex.net/globalweather.asmx?WSDL

protected void ddlCountry_SelectedIndexChanged(object sender, EventArgs e)
{
	// Get the Country DropDownList first.
	DropDownList ddlCountry = (DropDownList)sender;

	// Get the current GridView Row, from which the DropDownList is selected.
	GridViewRow currentRow = (GridViewRow)ddlCountry.NamingContainer;

	// Now let's find the City DropDownList on the same GridView Row.
	DropDownList ddlCity = (DropDownList)currentRow.FindControl("ddlCity");

	if (ddlCountry != null && ddlCountry.SelectedIndex > 0 && ddlCity != null)
	{
		string selectedCountry = ddlCountry.SelectedValue;

		// Get the Cities from the Service.
		string xmlCities = GetCitiesByCountry(selectedCountry);

		// Let's parse the XML into DataTable.
		XmlTextReader citiesReader = new XmlTextReader(new System.IO.StringReader(xmlCities));
		DataSet dsCities = new DataSet();
		dsCities.ReadXml(citiesReader);

		// Bind the City DropDownList.
		if (dsCities != null && dsCities.Tables.Count > 0 && dsCities.Tables[0].Rows.Count > 0)
		{
			ddlCity.DataSource = dsCities.Tables[0];
			ddlCity.DataTextField = "City";
			ddlCity.DataValueField = "City";
			ddlCity.DataBind();
		}
	}
	else if (ddlCity != null)
	{
		ddlCity.Items.Clear();
	}
}

private string GetCitiesByCountry(string selectedCountry)
{
	GlobalWeatherServiceReference.GlobalWeatherSoapClient client = new GlobalWeatherSoapClient();
	return client.GetCitiesByCountry(selectedCountry);
}

Hope You Enjoyed Reading

If you have any queries, please comment below. I will come back to you. Feel free to Like and Share the Blog in Social Sites.

Thanks for reading. 🙂

Why DropDownList SelectedValue not working inside SelectedIndexChanged Event?

Debugger Inside DropDown SelectedIndexChanged Event

Debugger Inside DropDown SelectedIndexChanged Event


In this Blog, we will explore one interesting Bug, which ASP.NET Developers encounter sometimes.

Bug

DropDownList SelectedValue inside SelectedIndexChanged Event does not give the current Selected Value, rather it gives the Default Value or first Option Value.

Walk Through

  1. Let’s declare a DropDownList first.
    <asp:DropDownList ID="ddlDropDownId" runat="server" AutoPostBack="true" 
                      OnSelectedIndexChanged="ddlDropDownId_SelectedIndexChanged">
    </asp:DropDownList>
    
  2. Now to bind the DropDownList, we will write one function like below…
    private void BindDropDownList()
    {
        // Declare a Dictionary to hold all the Options with Value and Text.
        Dictionary<string, string> options = new Dictionary<string, string>();
        options.Add("-1", "Select Option");
        options.Add("1", "Option 1");
        options.Add("2", "Option 2");
        options.Add("3", "Option 3");
        options.Add("4", "Option 4");
        options.Add("5", "Option 5");
    
        // Bind the Dictionary to the DropDownList.
        ddlDropDownId.DataSource = options;
        ddlDropDownId.DataTextField = "value";
        ddlDropDownId.DataValueField = "key";
        ddlDropDownId.DataBind();
    }
    
  3. So, when you run the Page, it would show you the DropDownList on the Page with the Options defined.
    DropDownList on Browser

    DropDownList on Browser

  4. When you select an Option from the DropDownList, it hits the SelectedIndexChanged Event in the Code Behind. If you need to get the SelectedValue, then you will write…
    protected void ddlDropDownId_SelectedIndexChanged(object sender, EventArgs e)
    {
        string value = ddlDropDownId.SelectedValue;
    }
    
  5. Now Here Comes the Main Issue

    When we put a debugger inside the Event and checked, irrespective of the selection of any Option, it always gives you the first Option Value that is “-1”. Refer the debugging Screen Shot at the Top.
    And there is no doubt on the first Option value “-1”, as we can clearly see from the Source HTML of the DropDownList. See highlighted Option in the image below.

    DropDownList Source HTML

    DropDownList Source HTML

    Any Hint Here?

    As we are getting the default value or the first value always, so the guess is that, something is causing the DropDownList to bind again when we select an Option.

    Let’s Find Out !!!

    In the Walk Through Steps, I said about the function BindDropDownList(), which binds the DropDownList. But we need to check where exactly it is getting called. As the DropDownList shows when the Page is Loaded, so it must be present inside the Page Load Event.

    protected void Page_Load(object sender, EventArgs e)
    {
        BindDropDownList();
    }
    

    Now, according to the Event Life Cycle rules, when you select an Option from DropDownList, it Posts Back and goes to Page Load Event first and then it comes to the SelectedIndexChanged Event.
    That means, it comes to the Page Load again on selection of any Option and calls the BindDropDownList(). Thus, the DropDownList is bounded again, before it goes to the SelectedIndexChanged Event. As the DropDownList is bound again, so the SelectedValue is now the first Option Value, which is “-1” (for “Select Option”).

    Fix?

    You might have got the answer. That is IsPostBack Property.

    Gets a value that indicates whether the page is being rendered for the first time or is being loaded in response to a postback.

    It tells you whether you are loading the Page for first time or is it a Post Back due to any Event. Modified code would look something like below.

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

    So, Be Careful and Share This to Save Someone’s Day !!!

    You might have played with all the codes in your Page to find out the issue, but at last you come to know that it was because you forgot to check IsPostBack Property. So, Like and Share this with your friends, so that anybody who is unable to find the problem, finally let out a sigh.

Bind DropDownList from DataTable using AJAX WebMethod

DropDownList Bound from Ajax WebMethod

DropDownList Bound from Ajax WebMethod


We will see one Example to bind one DropDownList using jQuery Ajax and C# WebMethod.

How?

Easy !!!

  • Add a WebMethod to Code Behind page
  • Call to that WebMethod from aspx Page
  • Bind the DropDownList when call succeeds

Let’s see the code

WebMethod

Here we are just creating a Dummy DataTable and converting it to XML.

/// <summary>
/// This WebMethod returns the XML for a DropDownList having value and text.
/// </summary>
/// <param name="tableName">string: Name of the Table having DropDownList items.</param>
/// <returns>string: XML containing all the items.</returns>
[System.Web.Services.WebMethod]
public static string GetDropDownItems(string tableName)
{
    // Create a dummy DataTable.
    DataTable dt = new DataTable(tableName);
    dt.Columns.Add("OptionValue");
    dt.Columns.Add("OptionText");
    dt.Rows.Add("0", "Item 0");
    dt.Rows.Add("1", "Item 1");
    dt.Rows.Add("2", "Item 2");
    dt.Rows.Add("3", "Item 3");
    dt.Rows.Add("4", "Item 4");

    // Convert the DataTable to XML.
    string result;
    using (StringWriter sw = new StringWriter())
    {
        dt.WriteXml(sw);
        result = sw.ToString();
    }

    return result;
}

JavaScript GetDropDownData function

The following function would do a jQuery Ajax call for the WebMethod and bind the Data to the DropDownList.

function GetDropDownData() {
	// Get the DropDownList.
	var ddlTestDropDownListXML = $('#ddlTestDropDownListXML');
	
	// Provide Some Table name to pass to the WebMethod as a paramter.
	var tableName = "someTableName";

	$.ajax({
		type: "POST",
		url: "BindDropDownList.aspx/GetDropDownItems",
		data: '{tableName: "' + tableName + '"}',
		contentType: "application/json; charset=utf-8",
		dataType: "json",
		success: function (response) {
			// Now find the Table from response and loop through each item (row).
			$(response.d).find(tableName).each(function () {
				// Get the OptionValue and OptionText Column values.
				var OptionValue = $(this).find('OptionValue').text();
				var OptionText = $(this).find('OptionText').text();
				
				// Create an Option for DropDownList.
				var option = $("<option>" + OptionText + "</option>");
				option.attr("value", OptionValue);

				ddlTestDropDownListXML.append(option);
			});
		},
		failure: function (response) {
			alert(response.d);
		}
	});
}

Important: We are passing one parameter tableName to the WebMethod, which will be the Dummy Table Name and after conversion to XML, it becomes parent of each Row. Refer the following Screenshot of returned XML Schema as seen inside Firebug Console.

XML returned from WebMethod

XML returned from WebMethod

We can see that someTableName is one one node containing the OptionText and OptionValue of a particular Item. These are actually Rows of the Dummy DataTable before conversion to XML.

Your Inputs !

Will be highly appreciated. Please feel free to comment. If you like the Blog, share this among your friends and colleagues.

Get File Names of a Particular File Type using LINQ

This is a simple Trick to find File Names of a particular File Type existing in a Folder.

Background

Suppose, you want to find the File Names of the CSV Files in a folder. So, you need to exclude all other files and consider the CSV Files only. This was implemented in one of my projects, where all the CSV File Names from a folder got populated in a DropDownList.

Let’s Explore

So, the main thing here is to find a particular type of File. For that, I used the following code.

DirectoryInfo di = new DirectoryInfo(folderPath);

// Get only the CSV files.
List csvFiles = di.GetFiles("*.csv")
                  .Where(file => file.Name.EndsWith(".csv"))
                  .Select(file => file.Name).ToList();

Here I have used DirectoryInfo.GetFiles Method (String).

Returns a file list from the current directory matching the given search pattern.

So, di.GetFiles("*.csv") would give us a list of all the CSV Files in that folder.

  • Here *.csv is the SearchPattern and * means any string before .csv.
  • Now, Where Clause is used to make sure that File Extension Ends With .csv and nothing else.

Then we are selecting the File Names by Select clause like Select(file => file.Name).

Note

You can apply this Trick to find any File Type. You just need to change the SearchPattern accordingly. If you wish to find pdf files, then it would be di.GetFiles("*.pdf").

Full Code

I have used another Where condition here as per the requirements to exclude the CSV Files, having _something in their File Names.

///
/// This function Populates CSV File Names on a DropDownList.
///
private void PopulateCSVFilesDropDownList()
{
    try
    {
        string folderPath = GetFolderPath();

        if (!string.IsNullOrEmpty(folderPath))
        {
            if (Directory.Exists(folderPath))
            {
                DirectoryInfo di = new DirectoryInfo(folderPath);

                // Get only the CSV files excluding the ones having
                // "_something" appended to them.
                List csvFiles = di.GetFiles("*.csv")
                                  .Where(file => 
                                                 file.Name.EndsWith(".csv") &&
                                                 !file.Name.Contains("_something"))
                                  .Select(file => file.Name).ToList();

                // Bind the DropDown and add one default option at the top.
                ddlCSVFiles.DataSource = csvFiles;
                ddlCSVFiles.DataBind();
                ddlCSVFiles.Items.Insert(0, new ListItem("Please select a file", "-1"));
            }
            else
            {
                // DropDownList is hided and Error message is displayed.
                ddlCSVFiles.Visible = false;
                lblErrorMessage.Visible = true;
                lblErrorMessage.Text = "Folder Does not Exist.";
            }
        }
    }
    catch (Exception ex)
    {
        // Exception is displayed on a Label.
        lblErrorMessage.Visible = true;
        lblErrorMessage.Text = ex.Message;
    }
}

///
/// This function returns the Folder Path from web.config,
/// which contains different type of Files.
///
/// string: Folder Path
private string GetFolderPath()
{
    // For Example - D:\Projects\SomeProject\SomeFolder
    return (ConfigurationManager.AppSettings != null &&
            ConfigurationManager.AppSettings["FolderPath"] != null) ?
            ConfigurationManager.AppSettings["FolderPath"].ToString() :
            string.Empty;
}

Update

27 Feb 2014 – Added an extra condition in LINQ to check if File Name Ends with .csv only and nothing else.