• How to update Image in GridView ASP.NET Control

    Posted on August 21, 2012 by in ASP.NET, C#, Dotnet

    In this article I am going to describe how to update image in GridView with the new image uploaded by user.

    If you are interested in just saving image to database, take a look at below article.
    How to save image to database in ASP.NET 

    If you are interested in resizing the image before saving it to database, take a look at below article.
    How to resize and save image to database in ASP.NET 

    If you are new to using HttpHandlers, I would recommend you to read below article.
    Display Images from database in ASP.NET GridView using HttpHandler 

    Database Connection
    Added following entry in web.config under connectionStrings element.

    <add name="Sql" connectionString="Data Source=(local); Initial Catalog=AdventureWorks; User=testuser; Password=testuser;" providerName="System.Data.SqlClient"/>

    Overview:
    When page is initially loaded, we retrieve some (say top 5) products from Production.Product table and bind them to the grid. We display some product attributes such as Product ID, Product Number, Product Name, List Price and product’s thumbnail photo. If user wants to update any product attribute, he can do so by clicking on the “Edit” button the bottom which would change the mode of the GridView to Edit. User can change product attributes and then click on “Update” button to persist changes.

    Page Design:
    I have created a new ASP.NET web page.  We don’t want a fancy grid, so just applying some basic formatting.  We are not going to allow user to update ProductID, so it is being rendered in BoundColumn. Rest of the attributes (ProductName, ProductNumber, ListPrice & Thumbnail photo) are rendered in Template columns as the content is custom made. If GridView is in ViewMode, these attributes are rendered as specified in ItemTemplate. If GridView is in EditMode,   it will follow the instructions in EditItemTemplate.

    For example, if you look at thumbnail photo column,  in the ItemTemplate we are using ImageButton to render the photo. You would notice that the URL of the image is pointing to a httphandler. I will discuss about this later. If you look at the EditItemTemplate, you would notice that we are adding FileUpload control to the page, so as to allow user to select a new image to update the existing image.

    <asp:GridView ID="gvProducts" AutoGenerateColumns="False"  CssClass="GridViewStyle" runat="server" DataKeyNames="ProductID" OnRowEditing="gvProducts_RowEditing"
    OnRowCancelingEdit="gvProducts_RowCancelingEdit" OnRowUpdating="gvProducts_RowUpdating">   
    <RowStyle CssClass="RowStyle" />                               
    <SelectedRowStyle CssClass="SelectedRowStyle" />    
    <HeaderStyle CssClass="HeaderStyle" />              
    <AlternatingRowStyle CssClass="AltRowStyle" />    
    <Columns>
        <asp:BoundField DataField="ProductID" HeaderText="Product ID" ReadOnly="true"/>            
       <asp:TemplateField headertext="Product Number">
                <ItemTemplate> <%#Eval("ProductNumber")%></ItemTemplate>                                                
                <EditItemTemplate>
                  <asp:TextBox id="txtProductNumber" text='<%#Eval("ProductNumber")%>' runat="server"/>                                                                         
                </EditItemTemplate>   
            </asp:TemplateField>                   
             <asp:TemplateField headertext="Product Name">
                <ItemTemplate> <%#Eval("Name")%></ItemTemplate>    
                 <EditItemTemplate>
                      <asp:TextBox id="txtProductName" text='<%#Eval("Name")%>' runat="server"/>                                             
                </EditItemTemplate>                             
            </asp:TemplateField>     
             <asp:TemplateField headertext="Price">
                 <ItemTemplate> <%#Eval("ListPrice")%></ItemTemplate>                    
                <EditItemTemplate>
                      <asp:TextBox id="txtListPrice" text='<%#Eval("ListPrice")%>'
                               runat="server"/>                                                                         
                </EditItemTemplate>               
            </asp:TemplateField>      
            <asp:TemplateField>
                <ItemTemplate>
                    <asp:ImageButton ID="imgPhoto" runat="server" 
                             ImageUrl = '<%#"ImageHttpHandler.ashx?ProductID="+Eval("ProductID")%>' />
                </ItemTemplate>
                 <EditItemTemplate>
                     <asp:FileUpload ID="imgUpload" runat="server" CssClass="fileUploadStyle" />   
                     <asp:HiddenField ID="hdProductPhoto" runat="server" Value='<%#Eval("ProductPhotoID")%>' />                     
                </EditItemTemplate>
            </asp:TemplateField>                
            <asp:TemplateField>
               <ItemTemplate>
                   <asp:LinkButton ID="btnedit" runat="server" CommandName="Edit" Text="Edit"/>			       
               </ItemTemplate>
               <EditItemTemplate>
                   <asp:LinkButton ID="btnupdate" runat="server" CommandName="Update" Text="Update" />
                   <asp:LinkButton ID="btncancel" runat="server" CommandName="Cancel" Text="Cancel"/>			      
               </EditItemTemplate>              
            </asp:TemplateField>
    </Columns>  
    </asp:GridView>
    

    Source Code:
    When page is first requested, Page_Load event is one of the events that get fired on the server side. If the load is not because of any web control event, we fetch products from database using ADO.NET API such as SQL Connection, SQL Command, SQL Adapters etc.  and bind to gridview.

    private string connString;
    protected void Page_Load(object sender, EventArgs e)
    {
        //fetch the connection string from web.config
        connString = ConfigurationManager.ConnectionStrings["Sql"].ConnectionString;
    
        if (!IsPostBack)
            BindData();
    }
    
    private void BindData()
    {
        //Bind the grid view
        gvProducts.DataSource = RetrieveProducts();
        gvProducts.DataBind();
    }
    private DataSet RetrieveProducts()
    {        
        //SQL statement to fetch entries from products            
        string sql = @"Select top 10 P.*, PP.ProductPhotoID from Production.Product P
            inner join Production.ProductProductPhoto PPP on P.ProductID = PPP.ProductID
            INNER JOIN Production.ProductPhoto PP on pp.ProductPhotoID = PPP.ProductPhotoID
            where ThumbnailPhotoFileName != 'no_image_available_small.gif' order by P.ProductID	desc";
    
        DataSet dsProducts = new DataSet();
        //Open SQL Connection
        using (SqlConnection conn = new SqlConnection(connString))
        {
            conn.Open();
            //Initialize command object
            using (SqlCommand cmd = new SqlCommand(sql, conn))
            {
                SqlDataAdapter adapter = new SqlDataAdapter(cmd);
                //Fill the result set
                adapter.Fill(dsProducts);
            }
        }
        return dsProducts;
    }
    

    Click on “Edit” and the grid would go into edit mode.  If you look at event handling function, you would notice that we are setting edit index of the grid to the row you trying to edit. Since it is a post back, we have to re-bind the grid control.

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

    When the end user clicks the Browse button, a file dialog will open up.  User can navigate through the local file system and can select any product image file. He can also change any product attributes if he would want to.  Clicking on the “Update” button would invoke gvProducts_RowUpdating event handler.  In the event handler,  we initially would get reference to the gridview row he was trying to update. By invoking FindControl on the row object, we will get access to the controls defined in the EditItemTemplate. These are the controls that hold user changes.  In case of ProductName, ProductNumber & ListPrice it is pretty straight forward as they are just texboxes.  In case of fileUpload, we need to add more logic.  We initially check if the upload control has any file. A variable to store the content of the image is declared and initialized to a length of the content. The content from the file upload input stream is stored into the variable. If you look at the page design you also notice that we are storing productPhotoID in a hidden field.  We need this primary key in order to update product photo. Finally we pass all these attributes to UpdateProduct method which will update the database.

    protected void gvProducts_RowUpdating(Object sender,  GridViewUpdateEventArgs e)
    {
        // Get the product id of the selected product    
        string ID = gvProducts.DataKeys[e.RowIndex].Value.ToString();
    
        // Get the GridViewRow object that represents the row being edited
        // from the Rows collection of the GridView control.            
        GridViewRow row = gvProducts.Rows[e.RowIndex];
    
        // Get the controls that contain the updated values. In this
        // example, the updated values are contained in the TextBox 
        // controls declared in the edit item templates of each TemplateField 
        // column fields in the GridView control.
        TextBox txtProductNumber = (TextBox)row.FindControl("txtProductNumber");
        TextBox txtProductName = (TextBox)row.FindControl("txtProductName");
        TextBox txtListPrice = (TextBox)row.FindControl("txtListPrice");
        HiddenField hdProductPhoto = (HiddenField)row.FindControl("hdProductPhoto");
        FileUpload flUp = (FileUpload)row.FindControl("imgUpload");
    
        //variable to store the image content
        byte[] imageBytes = null;
        string imageName = String.Empty;
        if (flUp.HasFile)
        {
            //variable to store the image content
            imageBytes = new byte[flUp.PostedFile.ContentLength];
            imageName = flUp.FileName;
            HttpPostedFile uploadImage = flUp.PostedFile;
            //read the image stream from the post and store it in imageBytes
            uploadImage.InputStream.Read(imageBytes, 0, (int)flUp.PostedFile.ContentLength);                
        }
    
        //update the existing product in database
        UpdateProduct(ID, txtProductNumber.Text, txtProductName.Text, txtListPrice.Text, hdProductPhoto.Value, imageBytes, imageName);
      
        //set gridview back to normal mode
        gvProducts.EditIndex = -1;
    
        //rebind gridview with latest data
        BindData();
    }
    

    In the UpdateProduct function, we have started by fetching connection string the configuration file. Once we have the connection string, we make a connection the database. We prepare a command object initialized with the connection and the name of the stored procedure to be executed. All the attributes are added to the command parameter object and ExecuteNonQuery method of command object is invoked which will then execute the stored procedure.

    private void UpdateProduct(string productID, string productNumber, string productName, string listPrice, string productPhotoID, byte[] imageBytes, string imageName)
    {
        using (SqlConnection conn = new SqlConnection(connString))
        {
            conn.Open();
            using (SqlCommand cmd = new SqlCommand("sp_UpdateProduct", conn))
            {
                cmd.CommandType = CommandType.StoredProcedure;
                //add input parameters to the SP
                cmd.Parameters.AddWithValue("@ProductID", productID);
                cmd.Parameters.AddWithValue("@ProductName", productName);
                cmd.Parameters.AddWithValue("@ProductNumber", productNumber);
                cmd.Parameters.AddWithValue("@ProductPrice", listPrice);
                cmd.Parameters.AddWithValue("@ProductPhoto", imageBytes);
                cmd.Parameters.AddWithValue("@ProductPhotoName", imageName);
                cmd.Parameters.AddWithValue("@ProductPhotoID", productPhotoID);
    
                cmd.ExecuteNonQuery();
            }
        }
    }
    

    [sp_UpdateProduct]
    In the stored procedure, we are simply updating product attributes in the Production.Product table. Only if user uploads a new image, we are updating Production.ProductPhoto table.

    CREATE PROCEDURE [dbo].[sp_UpdateProduct]
    @ProductID INT,
    @ProductName VARCHAR(50),
    @ProductNumber NVARCHAR(25),
    @ProductPrice MONEY,
    @ProductPhoto VARBINARY(MAX) = NULL,
    @ProductPhotoName NVARCHAR(50) = NULL,
    @ProductPhotoID INT
    AS
    BEGIN
    	
    	UPDATE [Production].[Product]
    	SET Name = @ProductName,
    		ProductNumber = @ProductNumber,
    		ListPrice = @ProductPrice
    	WHERE ProductID = 	@ProductID	
    	
    	IF(ISNULL(@ProductPhotoName,'') <> '')
    	BEGIN
    		UPDATE [Production].[ProductPhoto]
    		SET ThumbNailPhoto = @ProductPhoto,
    			ThumbnailPhotoFileName = @ProductPhotoName,
    			LargePhoto = @ProductPhoto,
    			LargePhotoFileName = @ProductPhotoName
    		WHERE ProductPhotoID = 	@ProductPhotoID	
    	END                    
    END
    

    I wanted to discuss this httphandler at the end, as this is not the main focus of this article. We are using httphandler to fetch the product photo from the database. In the pagedesign, you would notice that url of the image is pointing to ProductImageHttpHandler http handler and passing productID in the querystring. In the processrequest method, we fetch ProductID from the query string. We fetch the photo from the database based on the productID in the form of byte data using ADO.NET API and we simply write it to the response output stream by invoking BinaryWrite method.

    public class ProductImageHttpHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            //get the productid from the query string
            int productID = Convert.ToInt32(context.Request.QueryString["ProductID"]);
    
            if (productID > 0)
            {
                //fetch the product photo details
                DataTable dtProductPhotoDetails = RetrieveProductImage(productID);
    
                //store photo data and file name
                byte[] imageContent = (byte[])dtProductPhotoDetails.Rows[0][0];
                string fileName = dtProductPhotoDetails.Rows[0][1].ToString();
    
                context.Response.BinaryWrite(imageContent);
                context.Response.End();
            }
        }
    
        private DataTable RetrieveProductImage(int ProductID)
        {
            //fetch the connection string from web.config
            string connString = ConfigurationManager.ConnectionStrings["Sql"].ConnectionString;
    
            //SQL statement to fetch thumbnail photo
            string sql = @"Select ThumbNailPhoto, ThumbnailPhotoFileName 
                        from Production.ProductPhoto PP
                        inner join Production.ProductProductPhoto PPP on PP.ProductPhotoID = PPP.ProductPhotoID 
                        where ProductID = " + ProductID;
    
            DataTable dtProductsPhoto = new DataTable();
            //Open SQL Connection
            using (SqlConnection conn = new SqlConnection(connString))
            {
                conn.Open();
                //Initialize command object
                using (SqlCommand cmd = new SqlCommand(sql, conn))
                {
                    SqlDataAdapter adapter = new SqlDataAdapter(cmd);
                    //Fill the result set
                    adapter.Fill(dtProductsPhoto);
                }
            }
            return dtProductsPhoto;
        }
    
        public bool IsReusable
        {
            get{
                return false;
            }
    
        }
    }
    

    We must now register the handler in Web.config file.  The verb part is just what you want it to respond to (GET, POST, etc.). Path is the file or file extension you want it to respond to and Type is – ClassName, Assembly. The way you register to the handler depends upon the IIS version you are running and its mode (classic or Integrated mode).

    <httpHandlers>	
      <add verb="*" path="*ProductImageHttpHandler.ashx"      
          type="ASPNETGRIDVIEW_PART2.ProductImageHttpHandler,ASPNETGRIDVIEW_PART2"/>
    </httpHandlers>
    

    Run the application and 5 products from Production.Product table are displayed in the GridView.update Image in GridView 1

    Click on the “Edit” button on the first row and gridview would enter into edit mode. update Image in GridView 2

    Click on “Browse” button and file dialog will open up. Select a new image from the local file system. Change other product attributes as shown below:
    update Image in GridView 3

    click on “Update” button. Changes will be persisted to database and gridview is refreshed with new data.update Image in GridView 4

    Be Sociable, Share!
      Post Tagged with , , ,

    Written by

    Software architect with over 10 years of proven experience in designing & developing n-tier and web based software applications, for Finance, Telecommunication, Manufacturing, Internet and other Commercial industries. He believes that success depends on one's ability to integrate multiple technologies to solve a simple as well as complicated problem.

    View all articles by

    Email : [email protected]

    3 Responsesso far.

    1. sibasis says:

      Good Morning Gopal Sir,
      Thanks a lot for this useful article. But where the class ProductImageHttpHandler : IHttpHandler will be placed.kept?? Should I add a class and I will give name ProductImageHttpHandler and implement IhttpHandler? and then in body, I will keep rest?

      • Vanamali says:

        Yes, create a new class ProductImageHttpHandler as described in the article. Keep it in the same root directory as your webpage. then you could add reference to the class in web.config as explained.

    2. vj says:

      Very good tutorial…simple and easy to understand..Thank you.

    Leave a Reply