Question

I want to display in an asp:GridView an "Images" column. The idea is to provide a thumbnails of the image with link to real size image. For some rows, this may alternatively be a PDF document. I'd like the link to be to the PDF. The PDF or image are stored in a SQL database.

Now I have error in Handler (.ashx) file:

"Invalid attempt to read when no data is present."

This is my code :

ASP:

<asp:GridView ID="GridView1" runat="server" 
            AutoGenerateColumns="False" DataKeyNames="ID"
            DataSourceID="SqlDataSource1">
   <Columns>
      <asp:BoundField DataField="assessment_id" HeaderText="assessment_id" 
                    InsertVisible="False" ReadOnly="True"
                    SortExpression="assessment_id" />
      <asp:BoundField DataField="a_mime" HeaderText="a_mime" SortExpression="a_mime" />
      <asp:TemplateField HeaderText="a_data">       
        <ItemTemplate>
        <asp:Image ID="Image1" runat="server" ImageUrl='<%# "Handler.ashx?ID=" + Eval("ID")%>'/>
        </ItemTemplate>
      </asp:TemplateField>
  </Columns>
</asp:GridView>

<asp:SqlDataSource ID="SqlDataSource1" runat="server" 
  ConnectionString="<%$ ConnectionStrings:testConnectionString %>"
  SelectCommand="SELECT [assessment_id], [a_data], [a_mime] FROM [Assessments]">    
</asp:SqlDataSource>

The Handler ASHX:

<%@ WebHandler Language="C#" Class="Handler" %>

public class Handler : IHttpHandler {

public void ProcessRequest (HttpContext context) 
{
    SqlConnection con = new SqlConnection();
    con.ConnectionString = ConfigurationManager.ConnectionStrings["testConnectionString"].ConnectionString;

    // Create SQL Command 
    SqlCommand cmd = new SqlCommand();
    cmd.CommandText = "Select a_data from Assessments where assessment_id =@ID";
    cmd.CommandType = System.Data.CommandType.Text;
    cmd.Connection = con;

    SqlParameter ImageID = new SqlParameter("@ID", System.Data.SqlDbType.Int);
    ImageID.Value = Convert.ToInt32(context.Request.QueryString["assessment_id"]);
    cmd.Parameters.Add(ImageID);
    con.Open();

    SqlDataReader dReader = cmd.ExecuteReader();
    dReader.Read();

    context.Response.BinaryWrite((byte[])dReader["a_data"]);

    dReader.Close();

    con.Close();
}

If it is possible, please help me. If it's time-consuming, please provide a link to an example or blog post.

Was it helpful?

Solution

The reason for the error about Invalid attempt to read when no data is present is because the DataReader doesn't contain any records (no image for the given ID). Consider changing your code to:

SqlDataReader dReader = cmd.ExecuteReader();

if (dReader.HasRows)
{
  while (dReader.Read())
  {
     context.Response.BinaryWrite((byte[])dReader["a_data"]);
  }

  dReader.Close();
}

A few suggestions for improvement

  • try to predict when a row won't have an image
  • when you can detect on your ASP.NET page, perform the call to the .ashx page
  • when you can detect that you need to render the link to the PDF, show a nice static pdf image and its link will be the URL.

Consider checking, where possible, that the given Assessment has the image or PDF required. Perhaps add a where clause to determine, somehow, whether this record needs to display the PDF or image scenario.

May I suggest also that you convert your SQL statements into a stored procedure, as above, and modify your SqlDataSource as such:

CREATE PROC ListAssessments
AS

SELECT [assessment_id]
     , [a_data]
     , [a_mime] 
     , CASE WHEN a_mime = 'PDF' THEN 1
       ELSE 0
       END AS IsPDF

FROM Assessments  

Now on your Gridview, you can determine whether you want to render an image or a PDF link.

<asp:TemplateField HeaderText="Image">       
  <ItemTemplate>
     <asp:PlaceHolder id="ph1" runat="server" />    
  </ItemTemplate>
</asp:TemplateField>

Setup your Gridview to have an ItemDataBound event that calls a new method that you can write.

<asp:GridView OnRowDataBound="ShowImageOrPdf" 

In your code-behind, you can then determine which webcontrol to render in that placeholder.

protected void ShowImageOrPdf(object sender, GridViewRowEventArgs e)
{
    const string LINK = "handler.ashx?Id={0}&Type={1}";
    GridView gv = (GridView)sender;

    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        string assessmentID = gv.DataKeys[e.Row.RowIndex].Value.ToString();
        bool isPDF =  (bool)e.Row.DataItem["IsPDF"];
        HyperLink h = new HyperLink();

        if (isPDF)
        {
            //render a link showing that it's a PDF.
            h.NavigateUrl = string.Format(LINK, assessmentID, "PDF");
            h.ImageUrl = "http://www.adobe.com/images/pdficon_large.gif";
            h.Text = "View PDF";
        }
        else
        {
            //render a thumbnail with a link to the image
            h.NavigateUrl = string.Format(LINK, assessmentID, "IMG");

            //have the handler create a thumbnail somehow.
            h.ImageUrl = string.Concat(h.NavigateUrl + "&Size=Small");
        } 
       //write the link back to the placeholder.
       e.Row.FindControl("ph1").Controls.Add(h);
    }
}

Then in your .ashx, you'll have to read the querystring parameters to determine what to output: an image, a thumbnail image, or the PDF document stored in the database.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top