Statistics
Visits: 105083
Page Visits: 139169
Active visitors (monthly): 5364
Feed Count: 45
Most visited page: Adding value as a technical analyst
Category : ASP.NET GridView

Handling events of nested controls within GridView

A GridView contains two dropdownlists. The first dropdownlist is a simple Yes / No. On selecting Yes, the second dropdownlist should be enabled. How do I implement this?

The GridView contains three columns: First Name, Alien DropDownList (Yes/No), Country DropDownList. The DropDownList is present in the TemplateColumn. Depending on the selection in the first DropDownList, the second DropDownList is enabled or disabled. The HTML Layout of the GridView looks like this:


<asp:Gridview ID="GridView1" runat="server" 
DataSourceID="NameXml" 
AutoGenerateColumns="false"
OnRowDataBound = "GridView1_RowDataBound" >
<Columns>
<asp:BoundField DataField="FirstName" />
<asp:TemplateField>
    <ItemTemplate>
        <asp:DropDownList ID="AlienList" runat="server" 
        AutoPostBack="true"
        OnSelectedIndexChanged="AlienList_SelectedIndexChanged" >
            <asp:ListItem Text="Yes" Value="Y"></asp:ListItem>
            <asp:ListItem Text="No" Value="N"></asp:ListItem>
        </asp:DropDownList>
    </ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
    <ItemTemplate>
        <asp:DropDownList ID="CountryList" runat="server">
            <asp:ListItem Text="" Value=""></asp:ListItem>
            <asp:ListItem Text="USA" Value="USA"></asp:ListItem>
            <asp:ListItem Text="UK" Value="UK"></asp:ListItem>
        </asp:DropDownList>
    </ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:Gridview>
<asp:XmlDataSource ID="NameXml" runat="server" DataFile="Names.xml"></asp:XmlDataSource>

There are two event handlers that need to be written. GridView1_RowDataBound and AlienList_SelectedIndexChanged. The code for GridView1_RowDataBound is below:


protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        DropDownList lstAlien = e.Row.FindControl("AlienList") as DropDownList;

        // XmlDataSourceNodeDescriptor
        // DataItem is a XmlDataSourceNodeDescriptor
        // To get attribute, use XPathBinder
        lstAlien.SelectedValue = XPathBinder.Eval(e.Row.DataItem, "@Alien").ToString();

        DropDownList lstCountry = e.Row.FindControl("CountryList") as DropDownList;
        if (lstAlien.SelectedValue == "Y")
        {
            lstCountry.Enabled = true;
            lstCountry.SelectedValue = XPathBinder.Eval(e.Row.DataItem, "@Country").ToString();
        }
        else
        {
            lstCountry.Enabled = false;
        }
    }    
}

In the above event handler, the GridViewRow is got by e.Row. To get the XmlNode attributes, use the XPathBinder. This is because we are using XmlDataSource. Each item in XmlDataSource is a XmlDataSourceNodeDescriptor. This exposes an interface that can be used by XPathBinder. The code for the other event handler (AlienList_SelectedIndexChanged) is below:



protected void AlienList_SelectedIndexChanged(object sender, EventArgs e)
{
    DropDownList lstAlien = (DropDownList)sender;
    GridViewRow row = lstAlien.NamingContainer as GridViewRow;
    DropDownList lstCountry = row.FindControl("CountryList") as DropDownList;
    if (lstAlien.SelectedValue == "Y")
    {
        lstCountry.Enabled = true;
    }
    else
    {
        lstCountry.SelectedValue = "";
        lstCountry.Enabled = false;
    }
}


The above event handler code is very similar. To get GridViewRow, use the DropDownList.NamingContainer. The NamingContainer has the parent control. Once the GridViewRow is got, it is easy to find the second DropDownList using FindControl().

Permalink | 1 Comment | Leave your comment
 
Commented by sivaL at 23-Jul-2010 12:11 PM
Wow its nice Thanks Siva

Sorted GridView order

I am using JQuery to sort my GridView. I want to save the GridView as excel. But excel does not contain the sorted order. How can I send the sorted order to the server?

This can be accomplished by having an additional TemplateField with a HiddenField. The HTML markup for Gridview will look like this:


<asp:GridView ID="gv" runat="server" DataSourceID="sql" 
AutoGenerateColumns="false">
    <Columns>
        <asp:TemplateField>
         <HeaderStyle CssClass="hidden" />
         <ItemStyle CssClass="hidden" />
          <ItemTemplate>
            <asp:HiddenField ID="hdn" runat="server" />
          </ItemTemplate>
        </asp:TemplateField> 
        <asp:BoundField DataField="au_lname" />                
    </Columns>
</asp:GridView>

The javascript for saving the sorted order into the HiddenField is shown below. It is getting all the hiddencontrols within each table cell and updating its value with the row index.


<script type="text/javascript"  >

    function setOrder() {
        var gv = document.getElementById('gv');
        for (var i = 1; i < gv.rows.length; i++) {
            var hdns = gv.rows[i].cells[0].getElementsByTagName('input');
            hdns[0].value = i;           
        }
    }
    
</script>
<style type="text/css">
 .hidden { display: none; }
</style>

In the server-side, the excel should be written in the same order as the row-index in the hidden fields. This can be done using code like this:


protected void btnSubmit_Click(object sender, EventArgs e)
{
    for (int i = 1; i <= gv.Rows.Count; i++)
    {
        GridViewRow gvr = GetSortedRow(i);
        Response.Write(gvr.Cells[1].Text);
    }
}

private GridViewRow GetSortedRow(int index)
{
    foreach (GridViewRow gvr in gv.Rows)
    {
        HiddenField hf = gvr.FindControl("hdn") as HiddenField;
        if (hf.Value == index.ToString())
            return gvr;
    }

    return null;
}

Permalink | No Comments | Leave your comment
 

Hiding Repeater Items

I have a list of items displayed in a repeater. By default, I want to show the top 10 items. On click of a linkbutton, I want to show the remaining items. How can i do this? (Forum Link)

I have created a repeater bound to a SqlDataSource. The repeater is having a list of items displayed in an unordered list (ul). In FooterTemplate, I am having two linkbuttons. One is used for showing all items, another is used for hiding all but the top 10 items. The code is:


 <asp:Repeater ID="rpt" runat="server" DataSourceID="sql"
 OnItemDataBound="rpt_ItemDataBound">
 <HeaderTemplate>
   <ul id="list">
 </HeaderTemplate>
 <ItemTemplate>
    <li id="listItem" runat="server">
        <%# Eval("au_lname") %>
    </li>
 </ItemTemplate>   
 <FooterTemplate>
    </ul>
    <a id="show" href="javascript:show();">Show All</a>
    <a id="hide" href="javascript:hide();" style="display:none;">Hide</a>
 </FooterTemplate>
 </asp:Repeater>
<asp:SqlDataSource ID="sql" runat="server" 
ConnectionString="<%$ ConnectionStrings:Pubs %>"
 SelectCommand="SELECT au_lname FROM Authors">
 </asp:SqlDataSource>


In OnItemDataBound event handler, from the 11th item onwards, I am using style="display: none;" to hide the items as follows:


protected void rpt_ItemDataBound(object sender, RepeaterItemEventArgs e)
{

    if (e.Item.ItemIndex >= 10)
    {
        HtmlGenericControl li = e.Item.FindControl("listItem") 
					as HtmlGenericControl;
        li.Attributes.Add("style", "display: none;");
    }

}

Finally, the javascript for Show and Hide buttons. In the show function, we are getting a list of all items (li) within a ul. If ItemIndex > 10, then we change the display style of the item to 'block'. This shows the list item (li). The code is as follows:


<script type="text/javascript" >

    function show() {
        var list = document.getElementById('list').getElementsByTagName("li");
        for (var i = 0; i < list.length; i++) {
            if (i >= 10) {
                list[i].style.display = 'block';
            }

        }

        document.getElementById('hide').style.display = 'inline';
        document.getElementById('show').style.display = 'none';
    }
     
    function hide() {
        var list = document.getElementById('list').getElementsByTagName("li");
        for (var i = 0; i < list.length; i++) {
            if (i >= 10) {
                list[i].style.display = 'none';
            }
        }

        document.getElementById('hide').style.display = 'none';
        document.getElementById('show').style.display = 'inline';
    }
    
    
</script>


Permalink | No Comments | Leave your comment
 

Nested GridViews Bound to ObjectDataSource

How can I display Country and City information in a nested fashion like:

India Delhi Bangalore Chennai USA New York Chicago UK London

This can be done in three steps:

Create a object that will fetch data from database and structure it as collection of countries and collection of cities for each country


// Class used by ObjectDataSource
// Fetches data from database
// Puts the data as list of countries with list of cities within
public class CityHelper
{
    private Dictionary<Country, List<City>> cities = new Dictionary<Country,List<City>>();

    public CityHelper()
    {
        SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Pubs"].ConnectionString);
        conn.Open();

        string sql = "SELECT co.CountryID, ci.CityID, co.Country, ci.City FROM Country co INNER JOIN City ci ON co.CountryID = ci.CountryID";

        SqlDataAdapter adapter = new SqlDataAdapter(sql,conn);
        DataTable table = new DataTable();
        adapter.Fill(table);

        foreach (DataRow row in table.Rows)
        {
            // Add the country, city information 
            // to the Dictionary as KeyValuePair
            Country country = new Country();
            country.CountryName = (string)row["Country"];
            country.CountryID = (int)row["CountryID"];

            City city = new City();
            city.CityName = (string)row["City"];
            city.CityID = (int)row["CityID"];

            
            if (cities.ContainsKey(country))
            {
                List<City> cityList = cities[country];
                cityList.Add(city);
                cities[country] = cityList;
            }
            else
            {
                List<City> cityList = new List<City>();
                cityList.Add(city);
                cities.Add(country, cityList);
            }
        }
    }

    public Dictionary<Country, List<City>> GetCities()
    {
        return cities;
    }
}

// Country class with CountryID, CountryName
// This class implements IEquatable interface as 
// this class is used as Dictionary Key
public class Country : IEquatable<Country>
{
    private int countryID;
    public int CountryID
    {
        get { return countryID; }
        set { countryID = value; }
    }

    private string countryName;
    public string CountryName
    {
        get { return countryName; }
        set { countryName = value; }
    }

    // The below three methods are required
    // if this class is to be used for Dictionary Key
    public bool Equals(Country c)
    {
        if (countryName == c.countryName)
            return true;
        else
            return false;
    }

    public override bool Equals(object obj)
    {
        Country c = (Country)obj;
        return Equals(c);
    }

    public override int GetHashCode()
    {
        return countryName.GetHashCode();
    }


}

// City class with CityID, CityName
public class City
{
    private int cityID;
    public int CityID
    {
        get { return cityID; }
        set { cityID = value; }
    }

    private string cityName;
    public string CityName
    {
        get { return cityName; }
        set { cityName = value; }
    }
}

Create a nested Gridview with outer GridView bound to the ObjectDataSource


<asp:GridView ID="gv" runat="server" AutoGenerateColumns="false"
 DataSourceID="odsCities" OnRowDataBound="gv_RowDataBound">
<Columns>
    <asp:TemplateField>
        <ItemTemplate>
            <asp:Label ID="lblCountry" runat="server" 
            Text='<%# Eval("Key.CountryName") %>'></asp:Label>
            <asp:GridView ID="gvCities" runat="server" 
            AutoGenerateColumns="false">
                <Columns>
                    <asp:TemplateField>
                        <ItemTemplate>
                            <%# Eval("CityName") %>
                        </ItemTemplate>
                    </asp:TemplateField>
                </Columns>
            </asp:GridView>
        </ItemTemplate>
    </asp:TemplateField>
  </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="odsCities" runat="server" 
TypeName="ForumSamples6.CityHelper" SelectMethod="GetCities" />

Handle the OnRowDataBound of the Outer GridView. In this, bind the inner GridView to the appropriate cities collection


//  Called when each row of GridView is bound to data
protected void gv_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        // Get the data as KeyValuePair of Dictionary
        KeyValuePair<Country, List<City>> kvp = 
            (KeyValuePair<Country, List<City>>)e.Row.DataItem;
        // Bind to the list of cities
        GridView gvCities = (GridView)e.Row.FindControl("gvCities");
        gvCities.DataSource = kvp.Value;
        gvCities.DataBind();
    }
}

Permalink | No Comments | Leave your comment
 

GridView as email attachment

How do I send the contents of a GridView as an email attachment?

GridView can be rendered into a string using the RenderControl() method. From the string, a buffer (of bytes) can be extracted assuming ASCII encoding. This buffer can be used as email attachment by streaming it through a MemoryStream object. The code is below:


protected void Submit_Click(object sender, EventArgs e)
{
    // Render the contents of the GridView into a string
    StringBuilder sb = new StringBuilder();
    StringWriter sw = new StringWriter(sb);
    HtmlTextWriter htw = new HtmlTextWriter(sw);
    GridView1.RenderControl(htw);

    // Get the byte buffer from the string
    byte[] buffer = new byte[sb.Length];
    ASCIIEncoding enc = new ASCIIEncoding();
    buffer = enc.GetBytes(sb.ToString());

    // Create attachment from MemoryStream
    MemoryStream ms = new MemoryStream(buffer);
    Attachment att = new Attachment(ms, "GridReport.xls");
}

Permalink | 1 Comment | Leave your comment
 
Commented by Neh at 24-Dec-2010 12:19 PM

Hey Vijay,
Your post was really helpful. Thanks for sharing!
Regards,
Neh

Nested GridViews

I have a nested GridView in my project. The top level grid will display a list of names and a link-button. On clicking the link-button, the detail (child) grid will be shown with a OK button. When the OK button is pressed, the child grid should disappear

I will use the Pubs database, Authors table. The top-level grid called AuthorGrid will display last-names. The child-grid will display first-name for the selected author

Below is the HTML markup:


<asp:GridView ID="AuthorGrid" runat="server" DataSourceID="AuthorSql" 
                AutoGenerateColumns="false" DataKeyNames="au_lname"
                 OnSelectedIndexChanged="AuthorGrid_SelectedIndexChanged">
    <Columns>
        <asp:BoundField HeaderText="Author" DataField="au_lname" />
        <asp:TemplateField>
            <ItemTemplate>
                <asp:GridView ID="DetailGrid" runat="server" 
                OnRowCommand="DetailGrid_OK" 
                AutoGenerateColumns="false">
                    <Columns>
                        <asp:BoundField HeaderText="First name" DataField="au_fname" />
                        <asp:ButtonField CommandName="OK" Text="OK" />
                    </Columns>
                </asp:GridView>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:CommandField ShowSelectButton="true" SelectText="Show" />
    </Columns>
</asp:GridView>
<asp:SqlDataSource ID="AuthorSql" runat="server"
                    ConnectionString = "<%$ ConnectionStrings:Pubs %>"
                   SelectCommand="SELECT au_lname FROM Authors"
></asp:SqlDataSource>
<asp:SqlDataSource ID="DetailSql" runat="server" DataSourceMode="DataSet"
                    ConnectionString = "<%$ ConnectionStrings:Pubs %>"
                   SelectCommand="SELECT au_fname FROM Authors
                         WHERE au_lname=@lname"
>
    <SelectParameters>
        <asp:Parameter Type="String" Name="lname" />
    </SelectParameters>
</asp:SqlDataSource>

Note that AuthorGrid is bound to AuthorSql and DetailGrid is not bound. DetailGrid will bind to DetailSql (SqlDataSource) when an author is selected in the AuthorGrid. The code for showing DetailGrid is found in the OnSelectedIndexChanged EventHandler of the AuthorGrid:

protected void AuthorGrid_SelectedIndexChanged(object sender, EventArgs e)
{
    string lname = (string)AuthorGrid.SelectedValue;
    DetailSql.SelectParameters["lname"].DefaultValue = lname;
    DataSourceSelectArguments dssa = new DataSourceSelectArguments();
    DataView ds = (DataView)DetailSql.Select(dssa);
    GridView DetailGrid = (GridView)AuthorGrid.SelectedRow
				.FindControl("DetailGrid");
    DetailGrid.DataSource = ds;
    DetailGrid.DataBind();
}

Finally, when the OK button of the DetailGrid is clicked, the DetailGrid should disappear. This can be done by binding the DetailGrid to an empty datasource as follows:

protected void DetailGrid_OK(object sender, GridViewCommandEventArgs e)
{
    if (e.CommandName == "OK")
    {
        GridView DetailGrid = (GridView)e.CommandSource;
        DetailGrid.DataBind();
    }
}

Permalink | No Comments | Leave your comment
 

Handling OnDblClick Event of GridViewRow

I have a GridView that displays a list of names. On double-clicking a name, the name needs to be set in a textbox. This needs to happen w/o any postback

The RowCreated event of GridView should add a javascript event handler for the OnDblClick event as follows: ASPX Page:


<asp:TextBox ID="txtSearch" runat="server"/>
<asp:GridView ID="gvMain" runat="server" AutoGenerateColumns="false"
	 OnRowCreated="gvMain_RowCreated">
	<Columns>
	    <asp:BoundField HeaderText="Name" DataField="Name" />
        </Columns>
</asp:GridView>

RowCreated Event Handler:

protected void gvMain_RowCreated(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        e.Row.Attributes.Add("OnDblClick", 
			    "setSearch(" + e.Row.RowIndex + ");");
    }
}

The Javascript for the SetSearch() is below:
<script language="javascript" type="text/javascript">

    function setSearch(row) {
        var tbl = document.getElementById("gvMain");
        var txt = document.getElementById("txtSearch"); 
        txt.value = tbl.rows[row+1].cells[0].innerText;
    }

</script>
Since GridView is rendered as table, the individual cell can be accessed as tbl.rows[rowIndex].cells[cellIndex].innerText

Permalink | 1 Comment | Leave your comment
 
Commented by Nisha at 29-Mar-2010 11:01 AM

Hello Vijay,
I have read your forum at "http://forums.asp.net/p/1525995/3681124.aspx" i am looking for a share-point like feature. You can click Action => Edit as Datasheet. I need to implement this feature in ASP.Net web applicaiton. Please let me know if you have any solution workarround for this. my email id is: knisha77@gmail.com. Thank you in advance for your help.