• How to display and upload image in GridView

    Posted on July 31, 2012 by in ASP.NET, C#, Dotnet

    GridView displays the values of a data source in a table where each column represents a field and each row represents a record.  It enables you to select, sort, and edit these items. Some of these items/columns could be images.  These images might be stored in a file system or in a database. If they are stored in a file system, we set the URL of the image control to a http link.  If they are stored in a database, we might use ASPX page or custom HttpHandlers to prepare and return the image binary content. In this article, I am going to explain how to display and upload image in GridView.

    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. If user wants to add new product, he can do so by clicking on the “Add” button the bottom which would then show the footer row. User can enter all the product attributes and then click on “Insert” button to persist changes.

    I am not going to cover any basics in this article. If you are new to using grid view or if you would like know more about grid view, I would recommend you to reach following tutorials:
    Using GridView in ASP.NET & C# —PART 1
    Using GridView in ASP.NET & C# —PART 2

    What are Http Handlers?
    HTTP handlers are the .NET components that implement System.Web.IHttpHandler interface. They can act as a target for the incoming HTTP requests and can be called directly by using their file name in the URL. When a request is made to the web server for an ASP.NET resource (.aspx, .asmx, etc.), the worker process of the ASP.NET creates the right HttpHandler for the request which responds to the request.

    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"/>
    

    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 render ProductID Product Number, Product Name & Price in BoundColumns. As you would notice, we are using Image web control to render product’s thumbnail. We are setting the URL to our HTTP Handler and passing ProductID in the query string to the handler. I would explain the implementation of the Http Handler later.

    <div>
    <asp:GridView ID="gvProducts"  AutoGenerateColumns="False"
      EmptyDataText="No data available." runat="server" DataKeyNames="ProductID" 
      OnRowCommand="gvProducts_RowCommand" CssClass="mGrid">
        <Columns>
            <asp:BoundField DataField="ProductID" HeaderText="Product ID" ReadOnly="true"/>                           
            <asp:TemplateField headertext="Product Number">
                <ItemTemplate> <%#Eval("ProductNumber")%></ItemTemplate>                                 
                <FooterTemplate>
                    <asp:TextBox ID="txtNewProductNumber" runat="Server"></asp:TextBox>
                </FooterTemplate>
            </asp:TemplateField>                   
             <asp:TemplateField headertext="Product Name">
                <ItemTemplate> <%#Eval("Name")%></ItemTemplate>                   
                <FooterTemplate>
                    <asp:TextBox ID="txtNewProductName" runat="Server"></asp:TextBox>
                </FooterTemplate>
            </asp:TemplateField>     
             <asp:TemplateField headertext="Price">
                 <ItemTemplate> <%#Eval("ListPrice")%></ItemTemplate>                    
                <FooterTemplate>
                    <asp:TextBox ID="txtNewListPrice" runat="Server"></asp:TextBox>
                </FooterTemplate>
            </asp:TemplateField>                                           
            <asp:TemplateField>
                <ItemTemplate>
                    <asp:ImageButton ID="imgPhoto" runat="server"
                        ImageUrl = '<%#"ImageHttpHandler.ashx?ProductID="+Eval("ProductID")%>' />
                </ItemTemplate>
                 <FooterTemplate>
                     <asp:FileUpload ID="imgUpload" runat="server" CssClass="fileUploadStyle" />                         
                </FooterTemplate>
            </asp:TemplateField>     
    	<asp:TemplateField>       
                   <FooterTemplate>
                        <asp:LinkButton ID="btnInsert" runat="Server" Text="Insert" CommandName="Insert" UseSubmitBehavior="False" />
                   </FooterTemplate>
                </asp:TemplateField>        
        </Columns>
    </asp:GridView> 
    </div>
    <asp:LinkButton ID="btnAdd" runat="server" Text="Add" OnClick="AddNewRecord" />
    
    Name Description
    OnRowCommand= “ProductsView_RowCommand” when a button is clicked in the GridView control, ProductsView_RowCommand is called. This enables you to provide an event-handling method that performs a custom routine whenever this event occurs.
    asp:BoundField Displays the value of a field in a data source.
    BoundField -> DataField Gets or sets the name of the data field to bind to the BoundField object.
    BoundField -> HeaderText Gets or sets the text that is displayed in the header of a data control
    TemplateField Used TemplateField to display custom content for each record displayed.
    TemplateField -> ItemTemplate Use the ItemTemplate property to specify the custom content displayed for the items in a TemplateField object. Define the content by creating a template that specifies how the items are rendered.
    <%#Eval(“ProductNumber”)%> Data-binding expressions are contained within <%# and %> delimiters. At run time, the Eval method calls the Eval method of the DataBinder object. The Eval method takes the name of a data field and returns a string containing the value of that field from the current record in the data source.
    <ItemTemplate> <%#Eval(“ProductNumber”)%></ItemTemplate> Specify the custom content displayed for the templatefield items.  In our example, we are directly writing the product number text. We can also define a new control in the itemtemplate
    <FooterTemplate><asp:TextBox ID=”txtNewProductNumber” runat=”Server”/></FooterTemplate> We can define content to be displayed in the footer section of the control.  In our example, we define content that needs to be displayed when user requests to enter data for new product

    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 and will save it in the Viewstate for subsequent page refreshes. I am using ADO.NET API such as SQL Connection, SQL Command, SQL Adapters etc. to get the products from the database and to 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()
    {
        if (ViewState["Products"] != null)
            return (DataSet)ViewState["Products"];
        
        //SQL statement to fetch entries from products            
        string sql = @"Select top 5 P.* 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 “Add” button and it would create a blank row.  All we did in the button click event is to make the footer row visible. In the footer design itself, we designed it in such a way that, it looks like a blank product  record.

    protected void AddNewRecord(object sender, EventArgs e) 
    { 
         //show the footer	
         gvProducts.ShowFooter = true;
         //since it is a postback, rebind the grid
         BindData();             
    }
    

    After you fill in the data, click on the insert button to add the product to the back-end database. When user clicks on the “Insert” button, gvProducts_RowCommand event handler is called. In the event handler, we get the data from the controls and insert them in the database.

    protected void gvProducts_RowCommand(object sender, GridViewCommandEventArgs e)
    {
        // If multiple ButtonField column fields are used, use the
        // CommandName property to determine if "Insert" button was clicked. 
        if (e.CommandName.Equals("Insert"))
        {
            //fetch the values of the new product entered by the user
            TextBox txtNewProductNumber = gvProducts.FooterRow.FindControl("txtNewProductNumber") as TextBox;
            TextBox txtNewProductName = gvProducts.FooterRow.FindControl("txtNewProductName") as TextBox;
            TextBox txtNewListPrice = gvProducts.FooterRow.FindControl("txtNewListPrice") as TextBox;
            FileUpload flUp = (FileUpload)gvProducts.FooterRow.Cells[0].FindControl("imgUpload");
            if (flUp.HasFile)
            {
                //variable to store the image content
                byte[] imageBytes = new byte[flUp.PostedFile.ContentLength];
                string 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);
               
                //insert the new product into database
                InsertProduct(txtNewProductNumber.Text, txtNewProductName.Text, txtNewListPrice.Text, imageBytes, imageName);
            }
            //hide the footer
            gvProducts.ShowFooter = false;
            //clear the view state so that latest list will be retrieved from db
            ViewState["Products"] = null;
            // rebind the data
            BindData(); 
        }
    }
    
    private void InsertProduct(string productNumber,
                        string productName,
                        string listPrice, byte[] imageBytes, string imageName)
    {                       
        using (SqlConnection conn = new SqlConnection(connString))
        {
            conn.Open();
            using (SqlCommand cmd = new SqlCommand("sp_InsertProducts", conn))
            {
                cmd.CommandType = CommandType.StoredProcedure;
                //add input parameters to the SP
                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.ExecuteNonQuery();
            }               
        }
    }
    

    I have created an HttpHandler “ImageHttpHandler” which implements IHttpHandler interface. The interface requires that you implement the IsReusable property and the ProcessRequest method. When an HTTP handler is requested, ASP.NET calls the ProcessRequest method of the appropriate handler. The code that you write in the handler’s ProcessRequest method creates a response, which is sent back to the requesting browser.

    public class ImageHttpHandler : 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="*ImageHttpHandler.ashx"      
             type="ASPNETGRIDVIEW_PART2.ImageHttpHandler,ASPNETGRIDVIEW_PART2" />
    </httpHandlers>
    

    [sp_InsertProducts] Stored Procedure:
    As you would notice in the stored procedure, I am only taking some of the required attributes as inputs to simplify the example. Rest of the attributes are defaulted.

    CREATE PROCEDURE [dbo].[sp_InsertProducts]
    @ProductName VARCHAR(50),@ProductNumber NVARCHAR(25),
    @ProductPrice MONEY,@ProductPhoto VARBINARY(MAX),
    @ProductPhotoName NVARCHAR(50)
    AS
    BEGIN
    	DECLARE @ProductID INT, @ProductPhotoID INT
                    
        INSERT INTO [Production].[Product]
               ([Name],
               [ProductNumber],
               [MakeFlag],[FinishedGoodsFlag],[Color],[SafetyStockLevel],[ReorderPoint]
               ,[StandardCost],[ListPrice],[Size],[SizeUnitMeasureCode]
               ,[WeightUnitMeasureCode],[Weight],[DaysToManufacture],[ProductLine]
               ,[Class],[Style],[ProductSubcategoryID],[ProductModelID],[SellStartDate]
               ,[SellEndDate],[DiscontinuedDate] ,[rowguid],[ModifiedDate])
         
         SELECT @ProductName
               ,@ProductNumber
               ,[MakeFlag],[FinishedGoodsFlag],[Color],[SafetyStockLevel],[ReorderPoint]
               ,[StandardCost],@ProductPrice,[Size],[SizeUnitMeasureCode]
               ,[WeightUnitMeasureCode],[Weight],[DaysToManufacture],[ProductLine]
               ,[Class],[Style],[ProductSubcategoryID],[ProductModelID],[SellStartDate]
               ,[SellEndDate],[DiscontinuedDate],NEWID(),[ModifiedDate]
         FROM Production.Product WHERE ProductID = 1
         
         SET @ProductID = SCOPE_IDENTITY()
         
         INSERT INTO [Production].[ProductPhoto]
               ([ThumbNailPhoto],[ThumbnailPhotoFileName],[LargePhoto]
               ,[LargePhotoFileName],[ModifiedDate])
         VALUES
               (@ProductPhoto,@ProductPhotoName,@ProductPhoto
               ,@ProductPhotoName,GETDATE())
        
        SET @ProductPhotoID = SCOPE_IDENTITY()
        
        INSERT INTO [Production].[ProductProductPhoto]
               ([ProductID],[ProductPhotoID],[Primary],[ModifiedDate])
         VALUES
               (@ProductID,@ProductPhotoID,1,GETDATE())
    
    END
    

    Run the application and top 5 products are displayed on the page.
    upload image in gridview 1

    Click on “Add” button at the bottow and it will show the footer row at the bottom. Enter ProductNumber, ProductName and ListPrice. Click on “Browse” button and select a image for the product.
    upload image in gridview 2

    Click on “Insert” button and new product will be added to the database.
    upload image in gridview 3

    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]

    4 Responsesso far.

    1. Rakesh says:

      Awesome

    2. Soni says:

      Thank you sir for this article But…..,
      its very difficult to understand…… everything is fine but here it seems like you have created 3 tables… And i am totally confused ,which columns are under which table…??? and the datatypes ,setting primary key and foreign keys…… I need a brief explanation on this… An expert may understand.. but people like me couldn’t understand this… Neither I am an EXPERT nor a BEGINNER… just think am in middle of both ….So please post clearly about database side also….So that everyone can understand it clearly….. Its my request….

    3. gopal says:

      Thank you sir for this article… but,,
      its very difficult to understand…… everything is fine but here it seems like you have created 3 tables… And i am totally confused ,which columns are under which table…??? and the datatypes ,setting primary key and foreign keys…… I need a brief explanation on this… An expert may understand.. but people like me couldn’t understand this… Neither I am an EXPERT nor a BEGINNER… just think am in middle of both ….So please post clearly about database side also….So that everyone can understand it clearly….. Its my request….

    Leave a Reply