I have a Gridview with these parameters:
<asp:GridView runat="server" ID="ItemGrid" CssClass="Grid"
AutoGenerateColumns="false"
AutoGenerateDeleteButton="true" OnRowDeleting="RowDeleting"
AutoGenerateEditButton="true" onRowEditing="RowEdit"
OnRowCancelingEdit="CancelRowEdit" onRowUpdating="RowUpdating"
DataKeyNames="Item_ID">
<Columns>
<asp:BoundField HeaderText="Item" DataField="Item"/>
<asp:BoundField HeaderText="Family" DataField="Family"/>
<asp:BoundField HeaderText="Structure" DataField="Structure"/>
<asp:BoundField HeaderText="Updated" ReadOnly="true" DataFormatString="{0:d}" DataField="Updated"/>
</Columns>
</asp:GridView>
On updating it calls:
protected void RowUpdating(object sender, GridViewUpdateEventArgs e){
int Item_ID = (int)this.ItemGrid.DataKeys[e.RowIndex][0];
//Problem is something right here:
string Item = ((TextBox)ItemGrid.Rows[e.RowIndex].Cells[1].Controls[0]).Text;
string Family = ((TextBox)ItemGrid.Rows[e.RowIndex].Cells[2].Controls[0]).Text;
string Structure = ((TextBox)ItemGrid.Rows[e.RowIndex].Cells[3].Controls[0]).Text;
ItemTableAdapter taItem = new ItemTableAdapter();
taItem.UpdateItem(Item, Family, Structure, DateTime.Now, Item_ID);
//just a <asp:Label> for seeing some output.
Alert.Text= string.Format("Item:{0}Family:{1}Structure:{2}",Item,Family,Structure);
this.ItemGrid.EditIndex = -1;
dataBind();
}
It generates the Update/Edit/Delete buttons, my Delete function is working exactly how I want and the 'Edit' button generates editable TextBoxes as it should.
My problem is in the updating part, the strings Item, Family, Structure are getting the old values, not the new values I put in the generated text boxes.
If I hard code in values they are updated to the database and the DateTime.Now is always updating correctly in the database so the update query is working.
I've been looking at this/reading forums testing things for a couple days now. I'm sure I'm just missing something simple that I have overlooked.
Thanks for any help.
Edit:
It has been answered but for those who were curious this is my dataBind();
protected void dataBind()
{
ItemTableAdapter taItem = new ItemTableAdapter();
this.ItemGrid.DataSource = taItem.GetActive();
this.ItemGrid.DataBind();
}
Are you re-binding your GridView on postback by mistake? You should only fetch the data from the database on initial load:
if (!IsPostBack)
{
Gridview1.Datasource = BLL.SomeClass.Load(SomeRecordId);
GridView1.DataBind();
}
RowUpdating and RowUpdated fire at different times. See if that isn't your problem.
Try using the following method to get your new values:
//string Item = ((TextBox)ItemGrid.Rows[e.RowIndex].Cells[1].Controls[0]).Text;
//string Family = ((TextBox)ItemGrid.Rows[e.RowIndex].Cells[2].Controls[0]).Text;
//string Structure = ((TextBox)ItemGrid.Rows[e.RowIndex].Cells[3].Controls[0]).Text;
string Item = e.NewValues["Item"].ToString();
string Family = e.NewValues["Family"].ToString();
string Structure = e.NewValues["Structure"].ToString();
Related
I've Asset Codes which I'm converting into BASE64 like this:
private string GenerateBarcode(string BarCode)
{
string generatebarcode = BarCode;
GeneratedBarcode barcode = BarcodeWriter.CreateBarcode(generatebarcode, BarcodeWriterEncoding.Code128);
barcode.ResizeTo(400, 120);
barcode.SetMargins(10);
barcode.ChangeBarCodeColor(Color.Black);
Image MyBarCodeImage = barcode.Image;
var myArray = (byte[])new ImageConverter().ConvertTo(MyBarCodeImage, typeof(byte[]));
string temp_inBase64 = Convert.ToBase64String(myArray);
return temp_inBase64;
}
And after this, I'm saving this BASE64 into my database.
Now, getting this into my Dataset, showing in tabular form.
At the RDLC level, I've tried:
Drop an Image report control from toolbox.
Right click on the image and choose image properties
Set the image source to database
Set the MIME type to a suitable value, for example image/bmp.
Set use this field to the image value which you have,
For example =Fields!Code.Value. The parameter type should be Text.
But it's not showing anything in the RDLC Report Viewer.
I've found many solutions on the internet but none of them seems works to me. Is there something that I missed?
I use Visual Studio 2019 Community, .NET Framework 4.7.2, Microsoft.ReportViewer.WebForms 15.0.0.0
Thanks.
I suggest that you consider storing the data in the table as raw binary, and then convert to base64 as required.
the bonus points is that the RDLC can and will accpet a raw binay data source for a image.
So, example with raw binary, gridview, and RDLC report example.
So, say I have this gridView, and I need to display that binary picture:
<asp:GridView ID="GridView1"
runat="server" AutoGenerateColumns="False" DataKeyNames="ID"
CssClass="table" Width="50%" OnRowDataBound="GridView1_RowDataBound"
>
<Columns>
<asp:BoundField DataField="Fighter" HeaderText="Fighter" />
<asp:BoundField DataField="Engine" HeaderText="Engine" />
<asp:BoundField DataField="Thrust" HeaderText="Thrust (lbs)" />
<asp:BoundField DataField="FirstFlight" HeaderText="First Flight"
DataFormatString ="{0:MM dd yyyy}" ItemStyle-Width="100" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField ControlStyle-Width="132">
<ItemTemplate>
<asp:Image ID="Image1" runat="server"/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
And my code to load - along with the "binary" convert in the row data bound event.
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadGrid();
}
void LoadGrid()
{
string strSQL =
#"SELECT ID, Fighter,Engine, Thrust, FirstFlight, Description, MyImage
FROM Fighters ORDER BY Thrust";
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
conn.Open();
DataTable rstData = new DataTable();
rstData.Load(cmdSQL.ExecuteReader());
GridView1.DataSource = rstData;
GridView1.DataBind();
}
}
}
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DataRowView gData = e.Row.DataItem as DataRowView;
Image MyImage = e.Row.FindControl("Image1") as Image;
MyImage.ImageUrl =
#"data:image/jpeg;base64," + Convert.ToBase64String((byte[])gData["MyImage"]);
}
}
And now I see/get this:
So, in above, I CONVERT on the fly the raw binary data in the SQL table to base64 string.
And creating a RDLC report, this also works
Then I can indeed bind the image control directly to the image column, and the column and it works
eg:
and then when I run this, I get this:
however, if you stroring the picture as a base64 string?
You only need to "exclude" the first of the string with the mine type, and only include everything after the "," in the base64 string should work.
eg this:
=Convert.FromBase64String(CStr(Parameters!Base64.Value).Substring(22))
You have to change the 22 based on the type of image.
So, in my case, I'm using/testing with a jpeg.
thus this:
data:image/jpeg;base64,/
So, my expression to chop all off up to the "," is thus this:
So this works for me:
=Convert.FromBase64String(CStr(Fields!MyImage2.Value).Substring(23))
So, you CAN use a base64 string. It just that you are NOT to include the first part (mine type info) in that string.
If you don't want to change this in the report viewer, then I suppose you could add/make/have/create a expression in the sql to only return all characters after 23 chars.
If you are using webforms, you can use the asp Image control
<asp:Image ID="ImageObject" runat="server" Width="400"/>
And from server side do something like this
public void SetImageData(byte[] inArray)
{
string strBase64Logo = Convert.ToBase64String(inArray);
if (!string.IsNullOrWhiteSpace(strBase64Logo))
{
ImageObject.Visible = true;
ImageObject.ImageUrl = "data:Image/png;base64," + strBase64Logo;
}
else
{
ImageObject.Visible = false;
}
}
Save (and load) your image info as byte[] and let the control know that you will use an image from Base64 as data:Image/png;base64.
Just a heads-up: if you are storing your images as base64 in database, it will grow a lot really fast, and there will be network overhead when retrieving multiple images from database and when sending them to the page. I had a project with exactly this scenario and we found out that it was slow to load multiple images at the same time, as the queries returned a lot of data just from the images.
I have a .ascx user control on an .aspx page. When the user clicks a button, information is gathered and stored in the database. Then a Gridview is databinded and in the Gridview is a dropdownlist. This dropdownlist's Items are created dynamically based on the users previous input, now in the database. This is all good and the Gridview is displayed with the dropdownlist with the dynamically created Items.
The problem is on the postback for this dropdownlist. I obviously have to recreate the Gridview with the dynamically created Items in the dropdownlist. This does not work. The postback happens and calls the Page_Init and then the Page_InitComplete. This has the databind on the Gridview which calls the SectionGV_OnRowDataBound method. The dropdownlists are recreated. But the SectionDD_OnSelectedIndexChanged method is never hit and then the dropdownlist just reverts back to its original value. I can not change the dropdownlist's selected value.
.ASCX
<asp:GridView ID="MyGV" runat="server" AutoGenerateColumns="False" DataSourceID="MyDS" Width="100%" OnRowDataBound="MyGV_OnRowDataBound">
<Columns>
<asp:TemplateField >
<ItemTemplate>
<asp:DropDownList runat="server" ID="SectionDD" AppendDataBoundItems="True" AutoPostBack="True" OnSelectedIndexChanged="SectionDD_OnSelectedIndexChanged" >
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Code Behind of .ASCX
protected void Page_Init(object sender,EventArgs e)
{
this.Page.InitComplete += Page_InitComplete;
}
private void Page_InitComplete(object sender, EventArgs e)
{
MyGV.DataBind();
}
protected void SectionDD_OnSelectedIndexChanged(object sender, EventArgs e)
{
}
protected void MyGV_OnRowDataBound(object sender, GridViewRowEventArgs e)
{
using (EntitiesModel dbContext = new EntitiesModel())
{
// get array[][] array from database
if (e.Row.RowType == DataControlRowType.DataRow)
{
DropDownList sectionDd = (DropDownList)e.Row.FindControl("SectionDD");
sectionDd.Items.Clear();
if (array.Length == 3)
{
if(array[0][2].ToDecimal() > 0) sectionDd.Items.Add(new ListItem(array[0][0], array[0][1]));
if(array[1][2].ToDecimal() > 0) sectionDd.Items.Add(new ListItem(array[1][0], array[1][1]));
if(array[2][2].ToDecimal() > 0) sectionDd.Items.Add(new ListItem(array[2][0], array[2][1]));
}
else if (array.Length == 2)
{
if (array[0][2].ToDecimal() > 0) sectionDd.Items.Add(new ListItem(array[0][0], array[0][1]));
if (array[1][2].ToDecimal() > 0) sectionDd.Items.Add(new ListItem(array[1][0], array[1][1]));
}
else if (array.Length == 1)
{
if (array[0][2].ToDecimal() > 0) sectionDd.Items.Add(new ListItem(array[0][0], array[0][1]));
}
}
sectionDd.DataBind();
}
}
}
}
private void SaveToDB()
{
//save information to database
NOTE
This is one of many ways I have tried to solve this issue. The reason I have saved the information in the database is just a temporary fix. I just want to solve the issue outlined above and then I will add a ViewState solution.
First up, no, you do not have to re-create or load the grid again, or the dropdown list in the post back.
The user can type into the grid - change values, select drop downs. At that point you can loop all the grid rows and get/grab the dropdown values selected.
You CAN of course fire/trigger a event for the dropdown, but it not at all clear if you really need that event here.
If your grid is getting messed up on post-backs?, then it means you not limiting the load up in the FIRST page load - after that, it should not matter.
Say we have this grid markup:
<asp:GridView ID="MyGrid" runat="server" CssClass="table table-hover"
DataKeyNames="ID" AutoGenerateColumns="false" OnRowDataBound="MyGrid_RowDataBound" >
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="Last Name" />
<asp:BoundField DataField="HotelName" HeaderText="Hotel Name" />
<asp:TemplateField HeaderText="City">
<ItemTemplate>
<asp:DropDownList ID="DropDownList1" runat="server"
DataTextField="City"
DataValueField="City"
>
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Province" HeaderText="Province" />
</Columns>
</asp:GridView>
And of course non templated fields (such as the first few boundField - they appear in the .Cells collection. but for templated columns, you use findcontrol.
However, in EVERY and NEAR ALL web pages, as a general rule, you ONLY load + mess + create the grid ONE time, and you ONLY do this on the first page load - END OF STORY! You don't follow this rule, then you are in a world of hurt big time.
Ok, so lets load up the grid. Since we have a dropdown, then we have to fill that out on item data bound.
So, our code will look like this:
public DataTable rstCity = new DataTable();
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack == false)
{
LoadGrid();
}
}
public void LoadGrid()
{
using (SqlCommand cmdSQL = new SqlCommand("SELECT City from City Order by City",
new SqlConnection(Properties.Settings.Default.TEST4)))
{
// locd city for drop down list
cmdSQL.Connection.Open();
rstCity.Load(cmdSQL.ExecuteReader());
// now load grid
cmdSQL.CommandText = "SELECT * from tblHotels ORDER BY HotelName";
DataTable rst = new DataTable();
rst.Load(cmdSQL.ExecuteReader());
MyGrid.DataSource = rst;
MyGrid.DataBind();
}
}
Note how I did scope the city table to the class level (no reason to load it over and over for each row - and after the first page load, it will go out of scope - we don't care - it will live during the first page load and the data binding.
Ok, now, lets do the item data bind for the grid.
We have this:
protected void MyGrid_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DropDownList MyDrop = (DropDownList)e.Row.FindControl("DropDownList1");
MyDrop.DataSource = rstCity;
MyDrop.DataBind();
// get City from current data row source - set the drop to data row City
MyDrop.SelectedValue = ((DataRowView)e.Row.DataItem)["City"].ToString();
}
}
Ok, that's it. (note how the "dataitem" exists ONLY during data binding - a handy tip, since then you have use of the FULL data row - including PK values, and columns that you don't even include in the grid markup. But, once data binding is over, then DataItem can NOT be used - it only EVER exists during the bind process. But this information is VERY valuable, since you have full use of the Actual data SOURCE you used to bind. In above, I needed the City from that Row to set the drop list.
Ok, the output now looks like this:
Ok, at this point since we ONLY bind on first page load. The view state of that grid is 100% automatic handled by asp.net for you at this point in time.
You can drop other buttons on this form - post backs should NOT matter. The BIG lesson here is that gridview does persist (at least it will if we don't re-load it each time on post-back - you should NOT have to re-load).
Ok, next issue:
In most cases, I don't see the need for the dropdown list event in the grid?
So, you can as a general rule select and change any row combo. Once done, then you can get values of each row by looping the data grid rows - including that of dropdown selected.
However, Lets wire up the dropdown list event anyway.
tip of the day:
Since we can't select the dropdown and use the property sheet?
Then in markup do this:
You can in the markup type in OnSelectedIndexChanged=
WHEN you hit the "=" sign, note VERY close how intel-sense pops up a event create option:
So, click on CreateNewEvent - it "seems" like nothing occurs, but in fact if you flip over to code behind, you have a nice event stub created.
So lets put our code in that event - grab the row - show the value just slected.
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
// user changed the combo box
DropDownList MyDrop = (DropDownList)sender;
GridViewRow gRow = (GridViewRow)MyDrop.Parent.Parent;
Response.Write("<h2>Row index is = " + gRow.RowIndex.ToString() + "</h2>");
Response.Write("<h2>Hotel Name is = " + gRow.Cells[2].Text + "</h2>");
Response.Write("<h2>City from Drop selected = " + MyDrop.SelectedValue + "</h2>");
}
Of course we set auto post back = true in the drop markup also - right?
And note how we pick up the "sender", and then use .Parent.Parent to get the grid row that we are operating on. The first parent is some cell or some such.
I actually built recursive function to get that value, but for here we just used .parent.Parent (often with a extra markup, then you need to go op one more .parent).
Anyway, now say I select the last drop down in above, and change it.
I get this output:
In summary:
Only EVER load the grid - first page load - (you check IsPostBack).
You can have all kinds of additional post backs - the grid should survive and just be fine - no need to re-load at all. (GridView has built in viewstate).
Now, after I have fun, change the drop down on many rows?
I can loop the gridview - and any changes to that grid will persist.
In fact, if you make the columns text boxes (in templated fields), then you can tab around and edit almost like Excel, and again the values will persist for you, and they survive post backs. (and then you can send the whole grid of changes back to the database in one update command - I can show how to do this, but this post already has lots of good stuff anyway.
Now, having said the above, having done the above?
Well now that we have this working, then one can go back to building a custom user control - but if done right, it also should behave correctly, and should also survive post-backs.
I need to retrieve the email address from my grid, so any one can help me to retrieve the email value alone from grid..
CODE
<asp:BoundField ItemStyle-Width="150px" DataField="Email" HeaderText="Email">
<HeaderStyle HorizontalAlign="Left" />
</asp:BoundField>
Check this snippet please.
string[] emails="";
for (int i=0; i<Gridview.Rows.Count;i++)
{
emails[i] = Gridview.Rows[i].Cells[2].Text;
}
Your emails array contains all the e-mails of your Gridview rows.
If you want a specific email there is another approach, like this:
If the command of the linkbutton is UPDATE then the following will suffice:
protected void GridView_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
string email = GridView.Rows[e.RowIndex].Cells[2].Text;
}
You can add this in generic list
List<string> email = new List<string>();
for (int i=0; i<Gridview.Rows.Count;i++)
{
email.Add(Gridview.Rows[i].Cells[2].Text);
}
I have a grid view on my page and I want to allow the user to add / remove items to it. How it works, and the add functionality does work albeit probably not great, is that a user selects a product from a drop down list, then clicks Add.
This creates a new row in grid with the select Product text and ID. The GridView markup is:
<asp:GridView runat="server" ID="grdSelectedProducts" BorderWidth="1px" CellPadding="3" CellSpacing="2" AutoGenerateColumns="False" OnRowDataBound="grdSelectedProducts_OnRowDataBound" ShowHeaderWhenEmpty="True" DataKeyNames="ProductId"
OnRowCommand="grdSelectedProducts_RowCommand" OnRowDeleted="grdSelectedProducts_RowDeleted" OnRowDeleting="grdSelectedProducts_RowDeleting">
<Columns>
<asp:BoundField DataField="Product" HeaderText="Product" ReadOnly="False" />
<asp:TemplateField>
<ItemTemplate>
<asp:LinkButton runat="server" ID="linkDelete" runat="server" CommandName="Delete" CommandArgument="<%# Container.DataItemIndex %>">Remove</asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="ProductId" HeaderText="ProductId" ReadOnly="False" Visible="False" />
</Columns>
</asp:GridView>
The data in the grid is bound but there is no connection to a database, as it's just the information from the drop down.
The linkDelete is where I want to allow the user to remove an item, so my question is, how can I actually delete the row? I've got the RowCommand event up which I've tried to remove it, but it just doesn't seem to work correctly as the table either 1) gets rebound with the same data as before or 2) all rows are removed due to the binding of a new DataTable. I'm missing something obvious, but this type of grid is new to me.
Any example I've seen here or on Google has been around gridviews which are databound from SQL, or some other database, which seems to make the rebinding easier. However my version doesn't use that so binding again doesn't seem to work correctly.
protected void grdSelectedProducts_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Delete")
{
if (!string.IsNullOrEmpty(e.CommandArgument.ToString()))
{
int rowIndex = Convert.ToInt32(e.CommandArgument);
grdSelectedProducts.DeleteRow(rowIndex);
CountryDataTable = (DataTable)grdSelectedProducts.DataSource;
DataTable table = CreateDataTable(false, string.Empty, string.Empty);
grdSelectedProducts.DataSource = table;
grdSelectedProducts.DataBind();
}
}
}
private DataTable CreateDataTable(bool isAddingValue, string selectedProduct, string selectedId)
{
// if isAddingValue is FALSE then it isn't from a button click to add a Product, it is just
// a call to create the datatable
DataTable dataTable = ProductDataTable;
if (!dataTable.Columns.Contains("Product"))
{
dataTable.Columns.Add("Product");
dataTable.Columns.Add("ProductId");
}
if (isAddingValue)
{
// Get the data from ViewState
DataRow dataRow;
dataRow = dataTable.NewRow();
dataRow["Product"] = selectedProduct;
dataRow["ProductId"] = selectedId;
dataTable.Rows.Add(dataRow);
}
else
{
grdSelectedProducts.DataSource = null;
grdSelectedProducts.DataSource = ProductDataTable;
grdSelectedProducts.DataBind();
}
// Save the data back to ViewState
ProductDataTable = dataTable;
return dataTable;
}
private DataTable ProductDataTable
{
get {return ViewState["ProductDataTable"] as DataTable ?? new DataTable(); }
set { ViewState["ProductDataTable"] = value; }
}
I do have the RowDeleting & RowDeleted events but I don't have any code in it.
First thing you should note is you are deleting the row from grid not from the data table
Then you again bind the data table which may not give you any change, What you should do is instead of deleting from grid delete it from datatable
Rewrite it like
if (e.CommandName == "Delete")
{
if (!string.IsNullOrEmpty(e.CommandArgument.ToString()))
{
int rowIndex = Convert.ToInt32(e.CommandArgument);
DataTable table = CreateDataTable(false, string.Empty, string.Empty);
table.Rows.RemoveAt(rowIndex)
grdSelectedProducts.DataSource = table;
grdSelectedProducts.DataBind();
}
}
If you need it to remove it from DB , DOnt forget to do that , And also note that the above code each time when u fetch the table it will give u everything from DB
so you can make it global , or you can just remove from DB
try this code
if (e.CommandName == "Delete")
{
if (!string.IsNullOrEmpty(e.CommandArgument.ToString()))
{
int rowIndex = Convert.ToInt32(e.CommandArgument);
grdSelectedProducts.DeleteRow(rowIndex);
SqlCommand cmd = new SqlCommand ("Delete from table where id='"+ rowIndex )+"'",ConnectionObject);
cmd.ExecutenonQuery();
CountryDataTable = (DataTable)grdSelectedProducts.DataSource;
DataTable table = CreateDataTable(false, string.Empty, string.Empty);
grdSelectedProducts.DataSource = table;
grdSelectedProducts.DataBind();
}
}
I have the following GridView:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="SysInvoiceID" DataSourceID="SqlDataSource1">
<Columns>
<asp:BoundField DataField="SysInvoiceID" HeaderText="SysInvoiceID" ReadOnly="True" SortExpression="SysInvoiceID" />
<asp:BoundField DataField="BillMonth" HeaderText="BillMonth" SortExpression="BillMonth" />
<asp:BoundField DataField="InvoiceDate" HeaderText="InvoiceDate" ReadOnly="True" SortExpression="InvoiceDate" />
<asp:BoundField DataField="InvoiceNumber" HeaderText="InvoiceNumber" SortExpression="InvoiceNumber" />
<asp:BoundField DataField="Net" HeaderText="Net" SortExpression="Net" />
<asp:BoundField DataField="VAT" HeaderText="VAT" SortExpression="VAT" />
<asp:BoundField DataField="Gross" HeaderText="Gross" SortExpression="Gross" />
<asp:ButtonField CommandName="ViewInvoice" HeaderText=" " ShowHeader="True" Text="View" />
</Columns>
</asp:GridView>
Here is the code behind the page:
public partial class PagingTest01 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
void GridView1_RowCommand(Object sender, GridViewCommandEventArgs e)
{
// If multiple buttons are used in a GridView control, use the
// CommandName property to determine which button was clicked.
if (e.CommandName == "ViewInvoice")
{
// Convert the row index stored in the CommandArgument
// property to an Integer.
int index = Convert.ToInt32(e.CommandArgument);
// Retrieve the row that contains the button clicked
// by the user from the Rows collection.
GridViewRow row = GridView1.Rows[index];
// Now you have access to the gridviewrow.
ViewButton_Click(row);
}
}
protected void ViewButton_Click(GridViewRow row)
{
byte[] FileImage = GetImageData(0,row);
if (FileImage != null)
{
base.Response.Clear();
base.Response.Buffer = true;
base.Response.ContentType = "Application/x-pdf";
base.Response.ContentEncoding = Encoding.Default;
string attachment = string.Format("attachment;filename=\"Invoice_{0}.pdf\"", "Customer1");
base.Response.AddHeader("content-disposition", attachment);
base.Response.BinaryWrite(FileImage);
base.Response.Flush();
base.Response.Close();
base.Response.End();
}
}
public byte[] GetImageData(int sysInvoiceID, GridViewRow row)
{
string strUserID = CommonCode.GetCurrentUserID().ToString();
string strCustomerID = CommonCode.GetCurrentCustomerID().ToString();
byte[] numArray;
string strConnectionString = "Data Source=TESTSERV;Initial Catalog=DB_Invoices;Persist Security Info=True";
SqlConnection connection = new SqlConnection(strConnectionString);
SqlCommand command = new SqlCommand("select FileImage from DB_Invoices.dbo.Bills WHERE (FileType = 'PDF' AND SysInvoiceID = #ID)", connection);
command.Parameters.AddWithValue("#ID", GridView1.Rows[row].Cells[0].Text);
SqlDataAdapter da = new SqlDataAdapter(command);
DataSet ds = new DataSet();
try
{
connection.Open();
da.Fill(ds);
DataRow item = ds.Tables[0].Rows[0];
byte[] item1 = (byte[])item["FileImage"];
ds.Tables.Clear();
numArray = item1;
}
catch (Exception ex)
{
throw ex;
}
finally
{
connection.Close();
}
return numArray;
}
}
So basically I have a GridView with a lot of rows, each one with a 'View' Buttonfield next to it. When 'View' is clicked, I attempted to make use of GridView1_RowCommand which should hopefully grab the row clicked before passing it onto ViewButton_Click. This will then call GetImageData and pass the row number onto this line:
command.Parameters.AddWithValue("#ID", GridView1.Rows[row].Cells[0].Text);
Cell 0 is the SysInvoiceID column, so if the correct row is passed, #ID will be assigned a SysInvoiceID.
'Row' however doesn't seem to be a valid argument, though I can't think why not... Unless I have to explicitly convert it into a int? Any help would be really appreciated! Thanks.
I have just commented this as side-note but maybe it's your issue because you mention that "it doesn't seem to be a valid argument, though I can't think why not... Unless I have to explicitly convert it into a int".
If ID is an int you should use int.Parse(celltext), othwerwise the database gets the wrong type since AddWithValue needs to infer the type from the value.
So use:
command.Parameters.AddWithValue("#ID", int.Parse(GridView1.Rows[row].Cells[0].Text));
Apart from that, you haven't added the event handler GridView1_RowCommand.
<asp:GridView ID="GridView1" OnRowCommand="GridView1_RowCommand" runat="server" AutoGenerateColumns="False" DataKeyNames="SysInvoiceID" DataSourceID="SqlDataSource1">
....
and you are also not setting the CommandArgument to the index of the row. I would use a different approach anyway if you need the row-index. Use a templatefield and a control, for example a Button. Then use it's NamingContainer property to get the reference to the 'GridViewRow`, here is an example: Get Row Index on Asp.net Rowcommand event
use this:
int index = ((GridViewRow)((WebControl)sender)).RowIndex;
in place of
int index = Convert.ToInt32(e.CommandArgument);