How to upload Image to Web APP (Asp.net) and hold it for processing? - asp.net

I am trying to create a web app using asp.net (trying to get some knowledge in order to work on more advanced stuff later on). The app I'm trying to make needs to have a picture uploaded to it and then do some processing to it. Now the question, Can I have the user upload this picture display it on the page and hold it somewhere temporarily for processing without making use of a database?
Tried looking through YouTube and web but couldn't find any specific documentation.

Gee, I don't see why a database would matter?
If you don't have a database, that should not effect that you can just drop in a file up loader, up-load files to say some folder, and then display the list of files that been up-loaded. And you are 100% free to THEN process those files later that been dropped into that folder, right?
So, lets drop in a file up-loader (you can choose/use one of a "gazillion" such up-loaders, or use a simple fileupload control.
Right below that control, then drop in say a grid view to display the files you up-loaded into that folder.
So, say this markup:
<asp:RadioButtonList ID="RadioButtonList1" runat="server" height="40px"
RepeatDirection="Horizontal" CssClass="rMyChoice" ClientIDMode="Static"
AutoPostBack="true" OnSelectedIndexChanged="RadioButtonList1_SelectedIndexChanged">
<asp:ListItem Selected="True">Upload Files</asp:ListItem>
<asp:ListItem>My Files</asp:ListItem>
</asp:RadioButtonList>
<br />
<div id="uploadfiles" runat="server" style="width:550px">
<ajaxToolkit:AjaxFileUpload ID="AjaxFileUpload1" runat="server"
OnUploadComplete="AjaxFileUpload1_UploadComplete"
OnClientUploadCompleteAll="AllDone" />
</div>
<div id="myfiles" runat="server" style="width: 700px">
<asp:GridView ID="GridFiles" runat="server"
AutoGenerateColumns="False" ShowHeaderWhenEmpty="true" CssClass="table table-hover" >
<Columns>
<asp:BoundField DataField="FileName" HeaderText="FileName" />
<asp:BoundField DataField="Created" HeaderText="UpLoaded" />
<asp:BoundField DataField="Size" HeaderText="Size" />
<asp:TemplateField HeaderText="Preview">
<ItemTemplate>
<asp:Image ID="Image1" runat="server" Width="120px"
ImageUrl='<%# "UpLoadFiles/" + Eval("FileName") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Download" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:Button ID="cmdDownLoad"
runat="server" Text="Download" CssClass="btn"
OnClick="cmdDownLoad_Click"
CommandArgument='<%# Eval("FileName") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<br />
</div>
I am using the ajaxtoolkit up-loader, but it kind of don't really matter here at all.
Then below the up-loader, I just dropped in a grid view to display the files after up-loading.
Heck, might as well use a tab control, or a simple radio button list, so the user can view files, or up-load some more.
So, with above, I have this code behind:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
RBShow()
End Sub
Sub LoadGrid()
Dim rstFiles As New DataTable
rstFiles.Columns.Add("FileName")
rstFiles.Columns.Add("Created", GetType(DateTime))
rstFiles.Columns.Add("Size")
Dim sFolder As String = "~/UpLoadFiles"
Dim MyFolder As New DirectoryInfo(Server.MapPath(sFolder))
Dim MyFileList() As FileInfo = MyFolder.GetFiles("*.*")
For Each OneFile As FileInfo In MyFileList
Dim OneRow As DataRow = rstFiles.NewRow
OneRow("FileName") = OneFile.Name
OneRow("Created") = OneFile.CreationTime.ToString("G")
OneRow("Size") = OneFile.Length
rstFiles.Rows.Add(OneRow)
Next
GridFiles.DataSource = rstFiles
GridFiles.DataBind()
End Sub
Protected Sub RadioButtonList1_SelectedIndexChanged(sender As Object, e As EventArgs)
RBShow()
End Sub
Sub RBShow()
' routine to hide and show a div based on radio button (this is our fake tab control)
If RadioButtonList1.SelectedIndex = 0 Then
uploadfiles.Style.Add("Display", "normal")
myfiles.Style.Add("Display", "none")
Else
LoadGrid()
uploadfiles.Style.Add("Display", "none")
myfiles.Style.Add("Display", "normal")
End If
End Sub
The file uploader has a up-load file complete event. That code is simple this:
Protected Sub AjaxFileUpload1_UploadComplete(sender As Object, e As AjaxControlToolkit.AjaxFileUploadEventArgs)
Dim sFile As String = Server.MapPath("~/UpLoadFiles/") & e.FileName
AjaxFileUpload1.SaveAs(sFile)
End Sub
So, when we run the page, we get this:
So, since you don't have a database - don't need one!
Then just load up a grid view, or listbox or whatever with a list of files from the folder you save all the files being up-loaded.
of course in above, I do have a download button.
that code looks like this:
Protected Sub cmdDownLoad_Click(sender As Object, e As EventArgs)
Dim myBut As Button = sender
Dim strFileOnly As String = myBut.CommandArgument
Dim strFile As String = ""
strFile = Server.MapPath("~/UpLoadFiles/" + strFileOnly)
Dim sMineType As String = MimeMapping.GetMimeMapping(strFileOnly)
Response.ContentType = sMineType
Response.AppendHeader("Content-Disposition", "attachment; filename=" + strFileOnly)
Response.TransmitFile(strFile)
Response.End()
End Sub
So, the files are still in that folder for "later" processing.
And even better, if you go back to the page, you can "view" the list of files already up-loaded, or up-load a few more.
So, it not at all clear why a database is required. Those files are just sitting in a folder.
You can then process those files, say when done move to another folder called MyProcessed.
And then you are free to up-load more files for processing that will reside in that MyUpLoads folder.

Related

How to refresh devexpress listbox without refreshing the page in ASP.NET using VB.NET

I'm working on a form where I have to have an upload control and a listbox to show the uploaded files. Right now the only way to show the change is to refresh the whole page. My boss wants the upload to automatically show in the listbox. I have been unable to find anything to do this after days of searching.
VB code:
Protected Sub BootstrapUploadControl1_FileUploadComplete(sender As Object, e As DevExpress.Web.FileUploadCompleteEventArgs) Handles BootstrapUploadControl1.FileUploadComplete
Dim fileName = e.UploadedFile.FileName
Dim contentType = e.UploadedFile.ContentType
Try
Using fs As Stream = e.UploadedFile.FileContent
Using br As New BinaryReader(fs)
Dim bytes As Byte() = br.ReadBytes(CType(fs.Length, Integer))
Dim constr As String = "Data Source=mgm-sql-pub101;Initial Catalog=KidVid;Integrated Security=True;"
Dim query = "INSERT INTO Attachments(RequestID,FileName,FileBytes,Description) VALUES (#RequestID, #FileName, #FileBytes, #Description)"
Using con As SqlConnection = New SqlConnection(constr)
Dim cmd As SqlCommand = New SqlCommand(query, con)
cmd.Parameters.AddWithValue("RequestID", 1)
cmd.Parameters.AddWithValue("FileName", fileName)
cmd.Parameters.AddWithValue("FileBytes", bytes)
cmd.Parameters.AddWithValue("Description", "Binary File")
Try
con.Open()
If con.State = ConnectionState.Open Then
MsgBox("It's open! Yay!")
End If
Dim obj As Object = cmd.ExecuteNonQuery()
MsgBox("Record inserted successfully. ID = " & obj.ToString())
con.Close()
If con.State = ConnectionState.Closed Then
MsgBox("It's closed! Yay!")
End If
Catch ex As Exception
Throw ex
End Try
End Using
End Using
End Using
Catch ex As Exception
Throw New Exception(ex.Message)
End Try
End Sub
HTML:
<div class="col">
<h5><i class='fas fa-paperclip' style='font-size:24px'></i>Attachments:</h5>
<div class="row mb-3">
<div class="col-5">
<dx:BootstrapUploadControl ID="BootstrapUploadControl1" runat="server" AutoPostBack="true" UploadMode="Auto" ShowProgressPanel="True" ShowUploadButton="True"></dx:BootstrapUploadControl>
<dx:ASPxButton ID="ASPxButton1" runat="server" Text="ASPxButton" AutoPostBack="true" />
</div>
</div>
<h6><i class="fas fa-file-download" style="font-size: 24px"></i> Double-click filename to download file:</h6>
<dx:BootstrapListBox ID="AttachmentsListBox" runat="server" DataSourceID="KidVidAttachmentsDataSource" AutoPostBack="true">
<Fields>
<dx:BootstrapListBoxField FieldName="FileName"></dx:BootstrapListBoxField>
</Fields>
</dx:BootstrapListBox>
<asp:SqlDataSource runat="server" ID="KidVidAttachmentsDataSource" ConnectionString="<%$ ConnectionStrings:KidVidConnectionString %>" SelectCommand="SELECT [FileName] FROM [Attachments] ORDER BY [FileName]"></asp:SqlDataSource>
So, I'm not familiar with the up-loader you are using. But, your posted code stub suggest this works "much" like the ajaxtoolkit one.
That means after "all" files been up-loaded, then no post-back occures.
As noted, you do probably want some up-loader that allows up-loading of files, and does NOT require a post-back.
However, after say 4 files are done up-loading, then it not all that bad to have a post-back trigger, and at that point in time trigger a post-back to refresh your grid of files, or listbox or whatever.
And note that one should "find" + "adopt" some up-loader for this purpose. A number "will" preview the picture file EVEN before the up-load starts. (however, I not really interested in that feature).
So, I use the ajax tool kit. It has 3 events (all do NOT post back).
Start up load stub.
One file done uploading stub. Here we save the file, add to our "file list". In my following example I use a grid view in place of a list box.
And I have some code that pulls out a first page for a image preview if this is a PDF. For images, then I just preview the image.
And a lot of the nice up-loaders also have a "hot spot" in which you can just drag + drop files.
And most are "chunked", and thus show a progress bar. And being chunked, then file size is un-limited, and you don't get a huge browser "spinner" while such large files up-load.
However, once ALL files are done, then I do fire a client side event, and I do so since I need/want to up-date my up-loaded file list. That is "why" I asked about what file up-loader you are using.
You COULD (and so could I with the aj toolkit file uploader) do a web call, get the new files, and fill out the listbox. But, I'm using a gridview, and I just do a Post-back to refresh it.
The results look like this:
So, while and after each file is up-loaded? No, I don't want a post-back.
However, once the list of files is up-loaded? Then yes, I do a post-back to update (refresh) the grid view.
So, it much depends on the file up-loader you are using.
As noted, your "code stub" suggests that your file up-loader has this ability (to trigger a client side javascript event when done).
So, what I did was drop a button on to the page, hide it (with style = display none).
And the aj fileuploader has a "all done" client side event.
this was my markup:
(not really imporant, but as a fyi:).
<div id="uploadfiles" runat="server" style="width: 550px; float: left">
<ajaxToolkit:AjaxFileUpload ID="AjaxFileUpload1" runat="server"
OnUploadComplete="AjaxFileUpload1_UploadComplete"
OnClientUploadCompleteAll="AllDone" />
</div>
<div id="myfiles" runat="server" style="float: left; width: 40%; margin-left: 30px">
<asp:GridView ID="GridFiles" runat="server"
AutoGenerateColumns="False" CssClass="table table-hover"
OnRowDataBound="GridFiles_RowDataBound">
<Columns>
<asp:BoundField DataField="FileName" HeaderText="FileName" />
<asp:BoundField DataField="UpLoadTime" HeaderText="UpLoaded" />
<asp:BoundField DataField="Size" HeaderText="Size" />
<asp:BoundField DataField="Pages" HeaderText="Pages" />
<asp:TemplateField HeaderText="Preview">
<ItemTemplate>
<asp:Image ID="Image1" runat="server" Width="120px" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Download" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<button id="cmdDownLoad" runat="server" class="btn myshadow"
onserverclick="cmdDownLoad_Click">
<span aria-hidden="true" class="glyphicon glyphicon-cloud-download"></span>
</button>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Delete" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<button id="cmdDelete" runat="server" class="btn myshadow"
onclick="if (!delok(this)) {return false};"
onserverclick="cmdDelete_ServerClick">
<span aria-hidden="true" class="glyphicon glyphicon-trash"></span>
</button>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<br />
</div>
And I have this javascript code to refresh the grid view.
<asp:Button ID="cmdLoadGrid" runat="server" Text="Button"
style="display:none" ClientIDMode="Static" OnClick="cmdLoadGrid_Click"
/>
<script>
function AllDone() {
$('#cmdLoadGrid').click()
}
So, I in fact "click" the button after everything is done, since the aj fileupload does not do a post-back, and I in fact wanted one!
And my file save after up-load looks rather simular to what you have:
this:
Protected Sub AjaxFileUpload1_UploadComplete(sender As Object,
e As AjaxControlToolkit.AjaxFileUploadEventArgs)
Dim sPath As String = Server.MapPath("~/UpLoadFiles/")
Dim sFilePath As String = sPath & e.FileName
AjaxFileUpload1.SaveAs(sFilePath)
Dim strSQL As String =
"INSERT INTO MyUploadFiles (FileName, UpLoadTime, Size, SavePath, Pages, Preview)
Values (#FileName, #UpLoadTime, #Size, #SavePath, #Pages, #Preview)"
Dim cmdSQL As New SqlCommand(strSQL)
cmdSQL.Parameters.Add("#FileName", SqlDbType.NVarChar).Value = e.FileName
cmdSQL.Parameters.Add("#UpLoadTime", SqlDbType.NVarChar).Value = DateTime.Now
cmdSQL.Parameters.Add("#Size", SqlDbType.Int).Value = e.FileSize
cmdSQL.Parameters.Add("#SavePath", SqlDbType.NVarChar).Value = sPath
Dim iBytes() As Byte
Dim PageCount As Integer = 0
Select Case Path.GetExtension(e.FileName)
Case ".pdf"
iBytes = GetPdfThumb2(sFilePath, PageCount)
Case ".zip"
iBytes = GimageF(Server.MapPath("~/Content/Pictures/zip.png"))
Case ".bmp", ".gif", ".jpeg", ".jpg", ".png"
iBytes = GimageF(sFilePath)
Case Else
' use generatic picture
iBytes = GimageF(Server.MapPath("~/Content/Pictures/generic.png"))
End Select
cmdSQL.Parameters.Add("#Pages", SqlDbType.Int).Value = PageCount
cmdSQL.Parameters.Add("#Preview", SqlDbType.Binary).Value = iBytes
MyrstPN(cmdSQL)
End Sub

How do I enable row selection in an ASP GridView without disabling EnableEventValidation?

I have an ASPX page that includes a GridView. I want to be able to select a row from the grid, and populate another section of the page based on the selected row. It works if I have EnableEventValidation="false" in the <%# Page %> line, but I have been told that I cannot use that because of a security concern. When I don't include it, selecting a grid row throws an "Invalid postback or callback argument" exception.
How can I implement row selection without disabling event validation?
Here is my code:
ASPX page:
<asp:GridView runat="server" ID="TheGrid" AutoGenerateColumns="false" DataKeyNames="id" EmptyDataText="No Data Found" AllowSorting="true">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="First Name" ReadOnly="true" SortExpression="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="Last Name" ReadOnly="true" SortExpression="LastName" />
<asp:BoundField DataField="Email" HeaderText="Email" ReadOnly="true" SortExpression="Email" />
</Columns>
</asp:GridView>
ASPX.VB code:
Protected Sub TheGrid_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles TheGrid.RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Then
e.Row.Attributes("onclick") = Page.ClientScript.GetPostBackClientHyperlink(TheGrid, "Select$" & e.Row.RowIndex)
e.Row.Attributes("style") = "cursor:pointer"
End If
End Sub
Protected Overrides Sub Render(writer As HtmlTextWriter)
ClientScript.RegisterForEventValidation("TheGrid")
MyBase.Render(writer)
End Sub
Note that when I select a row, the exception is thrown somewhere between Page_Load and Render.
Ok, lets wire up the GV two ways.
First way, drop in a plane jane button for the row click
(we will delete the button and add row click in 2nd example).
so, say this simple GV
<div id="MyGridArea" runat="server" clientidmode="static">
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID"
CssClass="table table-hover" Width="60em" GridLines="None"
ShowHeaderWhenEmpty="true">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField>
<ItemTemplate>
<asp:Button ID="cmdEdit" runat="server" Text="Edit" CssClass="btn myshadow"
OnClick="cmdEdit_Click" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
Note how we just dropped in a plane jane button.
And our code to fill out above is this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadGrid()
End If
End Sub
Sub LoadGrid()
Dim strSQL = "SELECT * FROM tblHotelsA ORDER BY HotelName"
GridView1.DataSource = Myrst(strSQL)
GridView1.DataBind()
End Sub
And now we have this:
Ok, so next part is to drop in a "div" area that allows edit of one row.
So, again, quite much plan jane controls.
And we can "hide" grid when we edit, and show edit area div.
But, I often use jQuery.UI. Really amounts to much the same code as simple hide/show, but this way jQuery.UI turns that SAME div into a nice pop up area.
So, that div area might look like this:
<div id="EditRecord" runat="server" style="float: left; display: none" clientidmode="Static">
<br />
<div style="float: left" class="iForm">
<label>HotelName</label>
<asp:TextBox ID="txtHotel" runat="server" f="HotelName" Width="280">
</asp:TextBox>
<br />
<label>First Name</label>
<asp:TextBox ID="tFN" runat="server" f="FirstName" Width="140"></asp:TextBox>
<br />
<label>Last Name</label>
<asp:TextBox ID="tLN" runat="server" f="LastName" Width="140"></asp:TextBox>
<br />
<label>City</label>
<asp:TextBox ID="tCity" runat="server" f="City" Width="140"></asp:TextBox>
<br />
<label>Province</label><asp:TextBox ID="tProvince" runat="server" f="Province" Width="75"></asp:TextBox>
</div>
etc. etc. etc.
so, now lets wire up the button above.
The button will simple:
Get current grid row
Get PK id
Load up div and display.
So, that code is this:
Protected Sub cmdEdit_Click(sender As Object, e As EventArgs)
Dim btn As Button = sender
Dim gRow As GridViewRow = btn.NamingContainer
EditRow(gRow.RowIndex)
End Sub
Sub EditRow(rowNum As Integer)
Dim intPK As Integer = GridView1.DataKeys(rowNum).Item("ID")
Dim strSQL As String = "SELECT * FROM tblHotelsA WHERE ID = " & intPK
Dim rstData As DataTable = Myrst(strSQL)
' load up our edit div
fLoader(Me.EditRecord, rstData.Rows(0))
ViewState("PK") = intPK
ScriptManager.RegisterStartupScript(Me.Page, Page.GetType, "mypopedit", "popedit()", True)
End Sub
As noted, I added a popup from jQuery.UI, but we could just use plain jane "div" and hide/show the grid and show the edit area (or like you have, have that edit area in full view).
(fLoader is a routine I built some time ago - I like everyone became VERY tired of typing code over and over to fill out text boxes etc., so for any text box etc., I use a "made up" attribute called f="Data base column name", and that routine just loops the controls on the form and fills them out. No different than hand coding simple assignments to controls, but with this code I can re-use it over and over.
So, we now see, get this:
Ok, so the only next goal is to add a row click
(and not use that edit button).
So, then all we need is a routine that will get the current row index, and call our edit row routine we have above.
So, we use row data bound, and add that click event that way.
but, do note how the above button click gets the current row. That nice short code works for repeaters, listview etc. (we used naming container).
But, if you want a row click in place of that button?
Then on row data bound, add this code:
Protected Sub GridView1_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles GridView1.RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Then
e.Row.Attributes.Add("onclick",
"__doPostBack('myrowedit'," & e.Row.RowIndex & ")")
End If
End Sub
And in our page load event, we have this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadGrid()
Else
If Request("__EVENTTARGET") = "myrowedit" Then
EditRow(Request("__EVENTARGUMENT"))
End If
End If
End Sub
And a handy dandy routine that returns a table based on SQL I used above was this:
Public Function Myrst(strSQL As String) As DataTable
Dim rstData As New DataTable
Using mycon As New SqlConnection(My.Settings.TEST4)
Using cmdSQL As New SqlCommand(strSQL, mycon)
mycon.Open()
rstData.Load(cmdSQL.ExecuteReader)
End Using
End Using
Return rstData
End Function
So, all in all, not a lot of code.
You can try the above working example here:
http://www.kallal.ca/Website11/WebForm2

asp.net get table cell text

I'm developping an asp.net Web application.
I have a table and I add rows/cells to the table through code :
<asp:Table ID="Table2" runat="server" Style="table-layout:auto;" GridLines="Both">
<asp:TableRow runat="server" Enabled="False" Font-Bold="True" HorizontalAlign="Center" VerticalAlign="Top" BackColor="#FF704C" ForeColor="Black">
<asp:TableCell runat="server">Famille</asp:TableCell>
<asp:TableCell runat="server">Editeur</asp:TableCell>
<asp:TableCell runat="server">Référence</asp:TableCell>
<asp:TableCell runat="server">Solutions</asp:TableCell>
<asp:TableCell runat="server">Nature</asp:TableCell>
<asp:TableCell runat="server">Unité</asp:TableCell>
<asp:TableCell runat="server">Usage</asp:TableCell>
<asp:TableCell runat="server">Version</asp:TableCell>
<asp:TableCell runat="server">PP net (€)</asp:TableCell>
<asp:TableCell runat="server">Quantité</asp:TableCell>
</asp:TableRow>
</asp:Table>
--
Dim tfp As New TextFieldParser(My.Settings.FilePath)
tfp.Delimiters = New String() {My.Settings.Delimiter}
tfp.TextFieldType = FieldType.Delimited
tfp.ReadLine() ' skip header
Dim Ligne As Integer = 0
While tfp.EndOfData = False
Dim fields = tfp.ReadFields()
Dim trow As New TableRow
Ligne = Ligne + 1
For i = 0 To 8
Dim tcell As New TableCell
tcell.Controls.Add(New LiteralControl(fields(i)))
trow.Cells.Add(tcell)
Next
Table2.Rows.Add(trow)
End While
Later in code, after the user click on a button, I want to get the text which is in the cell, but I always get an empty string.
MyCell = table2.rows(3).Cells(2)
? Mycell.text
""
How can I get the text which is inside the cell ?
Thank you
Ok, the main issue, is that the asp.net "table" has ONE huge big nasty issue.
Asp.net tables do NOT have viewstate nor persistence. So, in code when you add some rows, they are objects added - but NOT with viewstate.
So, your code would have to RE-RUN every time!!!!
In other words, if you run some code to add a row, you have to save that "list" or object of additions, and then on each page load - re-create, and re-add that data each time.
As a result, you are MUCH ,but I mean REALLY BEYOND WORLD poverty better off to use a control with persistence (viewstate) build in.
EVEN in that case where you want to persist the data, I STRONG suggest you persist ONLY the data table used (note how I said the data table - NOT the markup, nor the HTML markup table).
And we do not yet need to have a database to do this!!!
Of course I grasp this is just for learning, but at the end of the day? That data needs to be placed in a database. You then do the data adding, deleting, manipulation, processing against the database. If you need to show updated data as a result (say adding a new row to the database), YOU THEN RE-BIND the control on the form.
so, keep the concept of working with the data 100% separate from the control or so called UI (the web page).
Again, I 100% grasp the goal of learning, and starting out simple. The problem, is that eventually, that data WILL and NEEDS to be in a table. This will allow data base operations, or at the VERY least allow you to use code to add rows, reference a column etc.
so, I suggest you use a GridView, and THEN also create a data table (in code behind).
So, our markup now becomes this:
<asp:GridView ID="GridView1" runat="server" CssClass="table" Width="50%"
ShowHeaderWhenEmpty="true" HeaderStyle-BackColor="LightGray">
</asp:GridView>
<br />
<asp:Button ID="Button1" runat="server" Text="Add new row" CssClass="btn" />
And now our code to create the table, and also code for the add button.
We have this now:
Dim rstData As New DataTable
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
' first page load - create a table,
' send to our grid
CreateTable
LoadGrid
Session("rstData") = rstData
Else
rstData = Session("rstData")
End If
End Sub
Sub CreateTable()
rstData.Columns.Add("Famille")
rstData.Columns.Add("Editeur")
rstData.Columns.Add("Référence")
rstData.Columns.Add("Solutions")
rstData.Columns.Add("Nature")
rstData.Columns.Add("Unité", GetType(Integer))
rstData.Columns.Add("Usage")
rstData.Columns.Add("Version", GetType(Double))
rstData.Columns.Add("PP Net(€)", GetType(Double))
rstData.Columns.Add("Quantité", GetType(Double))
End Sub
Sub LoadGrid()
GridView1.DataSource = rstData
GridView1.DataBind()
End Sub
Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
' add a new row to our table
Dim MyNewRow As DataRow = rstData.NewRow
MyNewRow("Famille") = "this is test" ' example refernce by colum name
MyNewRow(1) = "My Editeur" ' example refernce by colum index
rstData.Rows.Add(MyNewRow) ' send new row to the table
LoadGrid() ' re-display grid to show this new row
End Sub
And when I run above, click the button, I now see this:
Note how we had much less markup, but of course a bit of extra code. However, if that table comes from a database, then we did not even have the code to "create" the table, since in most cases, that table and data would be in a database.
Other advantages?
Well, I can now format the grid, increase column lengths, even drop in a image control, or whatever I like.
In above, I did let the grid load the data table, and format the column widths, but you will often of course want to control the column widths, settings, and even what kind of control to use here.
In fact, if you have a boatload of custom controls on the grid you want, then often I suggest using a ListView in place of GridView (the hard part and learning curve is to learn when and what control to use - you have so many to choose from!!!).
So, I suggest the above.
You can then say I want the 2nd row, and cell like this:
rstData.Rows(1).Item("Famile")
rstData.Rows(1).Item(0)
Again, you can reference column(s) by number (index) or by name.
And say I want even MORE control, then say I have a grid like this:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table" >
<Columns>
<asp:BoundField DataField="Fighter" HeaderText="Fighter" />
<asp:BoundField DataField="Engine" HeaderText="Engine" />
<asp:BoundField DataField="Thrust" HeaderText="Thrust" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField HeaderText="Preview">
<ItemTemplate>
<asp:Image ID="Image2" runat="server" ImageUrl = '<%# Eval("ImagePath") %>' Width="140" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="View">
<ItemTemplate>
<asp:Button ID="cmdView" runat="server" Text="View" CssClass="btn"
CommandArgument = '<%# Eval("ImagePath") %>' OnClick="cmdView_Click" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Note how we now have AutoGenerateColumns = false. That's because I want to "control" each column such as width, etc. And I wanted a image control in that "table".
So, my code to load up the above is now this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadGridF()
End If
End Sub
Sub LoadGridF()
Using conn As New SqlConnection(My.Settings.TEST4)
Using cmdSQL As New SqlCommand("SELECT * FROM Fighters", conn)
conn.Open()
Dim rstData = New DataTable
rstData.Load(cmdSQL.ExecuteReader)
GridView1.DataSource = rstData
GridView1.DataBind()
End Using
End Using
End Sub
And now I get this:

How to add new line items in a APS.net webform and submit to database

Please note: I do not necessarily need working code. I just don't know how to word what I am looking for to even find an answer on the web. I guess i'm just asking for a little guidance on what kind of control I would to use to accomplish my goal.
Basically when I go to a job site I will use different amounts and types of inventory. So one line item would consist of
Item Description
Quantity Used
UsedByTech
I'm collecting these via webform text box. I would like to have a button that says "Add" and then allows me to input another line item, and so on.
Then at some point a submit button on the form would gather those line items and input them in to a MSSQL databse.
I'm currently using ASP.Net framwork and webforms.
Can somone please tell me what kind of control would allow this a may be give me some hints about what to search for?
Ok, this is actually quite easy - but we let asp.net do most of the work for us.
So, we assume we have these two tables:
so, we have a table to list out the Job Sites
And then as per above, we have a table to list out the Job items for each site.
Ok, so we display the Job Sites.
Drop in a Gridview - build it using the wizards:
Now, blow out (delete) the data source from the page - don't need it.
Remove the DataSource setting for the Gridview
And lets drop in a plane jane button into that grid.
Thus, this markup - most of it was generated for me
<div style="padding:25px">
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table" Width="40%">
<Columns>
<asp:BoundField DataField="JobSite" HeaderText="JobSite" />
<asp:BoundField DataField="JobDate" HeaderText="JobDate" DataFormatString="{0:yyyy-MM-dd}" />
<asp:BoundField DataField="Foreman" HeaderText="Foreman" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField HeaderText="View">
<ItemTemplate>
<asp:Button ID="cmdView" runat="server" Text="View Job" CssClass="btn"
Onclick="cmdView_Click"/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
And our code to load this grid is thus this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadGrid
End If
End Sub
Sub LoadGrid()
GridView1.DataSource = MyRst("SELECT * from JobSites Order by JobDate")
GridView1.DataBind()
End Sub
Ok, we now have this:
Ok, so now we need to wire up that "view" button.
So, we grab the PK row value - jump to our edit items page;
Protected Sub cmdView_Click(sender As Object, e As EventArgs)
Dim btn As Button = sender
Dim gRow As GridViewRow = btn.Parent.Parent
' get PK database ID
Dim PK As Integer = GridView1.DataKeys(gRow.RowIndex).Item("ID")
' Now jump to our job add job items page
Session("JobID") = PK
Response.Redirect("JobAddItems.aspx")
End Sub
So, now we need to build our 2nd page.
I could use a repeater - but I used DataList - to display the ONE job.
I again used the wizards - then blow out the DataSource setting and item on that page.
We then drop in a gridview for the "many" items (the job items).
I now have this:
<br />
<div style="border:solid;border-color:black;width:20%">
<asp:DataList ID="DataList1" runat="server" DataKeyField="ID">
<ItemTemplate>
JobSite:
<asp:Label ID="JobSiteLabel" runat="server" Text='<%# Eval("JobSite") %>' Font-Size="Larger" Font-Bold="true" />
<br />
JobDate:
<asp:Label ID="JobDateLabel" runat="server" Text='<%# Eval("JobDate", "{0:yyyy-MM-dddd}") %>' />
<br />
Foreman:
<asp:Label ID="ForemanLabel" runat="server" Text='<%# Eval("Foreman") %>' />
<br />
Description:
<asp:Label ID="DescriptionLabel" runat="server" Text='<%# Eval("Description") %>' />
<br />
<br />
</ItemTemplate>
</asp:DataList>
<br />
</div>
<br />
<div style="float:left">
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table borderhide" Width="30%">
<Columns>
<asp:TemplateField HeaderText="Description">
<ItemTemplate>
<asp:TextBox id="txtDesc" runat="server" Text='<%# Eval("ItemDescription") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Qty">
<ItemTemplate>
<asp:TextBox id="txtQty" runat="server" Text='<%# Eval("Qty") %>' TextMode="Number" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Used By">
<ItemTemplate>
<asp:TextBox id="txtUsedBy" runat="server" Text='<%# Eval("UsedBy") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<div style="float:right">
<asp:Button ID="cmdAdd" runat="server" Text="Add Item" CssClass="btn" style="margin-top:-20px" />
</div>
</div>
<div style="clear:both"></div>
<asp:Button ID="cmdSave" runat="server" Text="Save" CssClass="btn" />
<asp:Button ID="cmdCancel" runat="server" Text="Cancel" CssClass="btn" style="margin-left:40px"/>
</div>
Ok, and the code to load this up is this:
We load up the main record - just for display, and then the GridView of child items.
Dim rstJobItems As New DataTable
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
ViewState("JobID") = Session("JobId")
LoadData()
Else
rstJobItems = ViewState("JobItems")
End If
End Sub
Sub LoadData()
DataList1.DataSource = MyRst("SELECT * from JobSites WHERE ID = " & ViewState("JobID"))
DataList1.DataBind()
rstJobItems = MyRst("SELECT * FROM JobItems where Job_ID = " & ViewState("JobID"))
GridView1.DataSource = rstJobItems
GridView1.DataBind()
ViewState("JobItems") = rstJobItems
End Sub
Ok, so now on the first page, when we click on a row, we jump to the 2nd page, and we see/have this:
So we need to wire up the button to add a new row.
That code looks like this:
Protected Sub cmdAdd_Click(sender As Object, e As EventArgs) Handles cmdAdd.Click
' user might have done some edits
GridToTable()
' create a new row
Dim NewRow As DataRow = rstJobItems.NewRow
NewRow("Job_id") = ViewState("JobID")
NewRow("Qty") = 0
rstJobItems.Rows.Add(NewRow)
GridView1.DataSource = rstJobItems
GridView1.DataBind()
End Sub
So, say in above, I click the add row, then you will see this:
(in fact I clicked it two times).
Ok, so you are free to tab around - edit data in that grid. You can edit/change existing rows, or add a new row and simple tab around and enter data.
So, now lets wire up the Save button. That save button has to allow and deal with editing rows - and also saving edits - all in ONE shot to the database.
The code for save button is thus this:
Protected Sub cmdSave_Click(sender As Object, e As EventArgs) Handles cmdSave.Click
GridToTable()
Using conn As New SqlConnection(My.Settings.TEST4)
Using cmdSQL As New SqlCommand("SELECT * FROM JobItems WHERE ID = 0", conn)
Dim da As New SqlDataAdapter(cmdSQL)
Dim daU As New SqlCommandBuilder(da)
da.Update(rstJobItems)
End Using
End Using
' data add, and any edit now saved - return to our job site listing page
Response.Redirect("JobSites.aspx")
End Sub
So we send grid to table, and then in ONE database update, send the table back to the database.
Of course after we save, we return back to our first page with the grid now read to edit more.
So, the other routine used in above was sending the Grid (rows) back to the table.
That code is this:
Sub GridToTable()
For Each gRow As GridViewRow In GridView1.Rows
Dim OneRow As DataRow = rstJobItems.Rows(gRow.RowIndex)
OneRow("ItemDescription") = CType(gRow.FindControl("txtDesc"), TextBox).Text
OneRow("Qty") = CType(gRow.FindControl("txtQty"), TextBox).Text
OneRow("UsedBy") = CType(gRow.FindControl("txtUsedBy"), TextBox).Text
Next
End Sub
And of course the cancel button? Well, if you add some rows, or just edit the rows, and hit cancel? Well, we don't save - but just return to the previous page.
eg:
Protected Sub cmdCancel_Click(sender As Object, e As EventArgs) Handles cmdCancel.Click
Response.Redirect("JobSites.aspx")
End Sub
And last but not least, I have a "general" helper routine that simple returns a data table - and I used it several times in above. That routine kind of gives me a FoxPro or MS-Access like ability to get a data table with great ease (in place of typing that same code over and over).
That routine was this:
Public Function MyRst(strSQL As String) As DataTable
Dim rstData As New DataTable
Using conn As New SqlConnection(My.Settings.TEST4)
Using cmdSQL As New SqlCommand(strSQL, conn)
conn.Open()
rstData.Load(cmdSQL.ExecuteReader)
End Using
End Using
Return rstData
End Function
So the above is quite much start to finish. It is of course quick code, and done rather fast/quick for stack overflow. But it is full of great ideas, and as noted, I let the wizards generate most of the markup. (but then remove the data source from the GV, and also the Data source item that gets created in the page.
So in summary:
I did not write most of that markup - I let the wizards generate most of it.
Note how SMALL and relative simple the code bits were. we broke up this into bite sized parts - thus each routine is not a lot of code.
We ALSO let .net built the sql update (and insert) commands for us. this eliminated BOAT LOADS of parameters, and BOATLOADS of work to wire that up. And it also MUCH better for performance, since ado.net is smart - for rows not changed - it don't send the updates to sql server.
Note also how ONE simple update method of
da.Update(rstJobItems)
Handles BOTH edits/updates, an inserts with one command. And we probably could/should drop in a delete row button - and the update would also handle that for us too!!!
So, there is a LOT of great ideas in above, but key concepts are:
Leverage as much as you can the wizards, and built in controls.
Leverage the ability of ado.net to update a table of edits in ONE shot back to the database - and as above shows, SAME single update also works for both inserts and updates at the same time.

Server cannot set content type after HTTP headers have been sent - after error within an updatepanel

I'm getting the following error: Server cannot set content type after HTTP headers have been sent. I have looked around online, including some of the SO questions with exactly this error, but none have resolved my issue. Here is my particular scenario:
I have an asp.net gridview that is inside of an update panel (which also happens to be inside of a modalpopupextender). The gridview has a template field with an imagebutton that, when clicked, downloads a PDF. All works well until I run into an error trying to download. I handle the error inside the Gridview.RowCommand event, and the page doesn't change back on the client like I want. However, if I click the download again, the file doesn't download, and I catch the error inside ScriptManager.AsyncPostBackError event "Server cannot set content type after HTTP headers have been sent." Thanks in advance!
Here is my code:
--Default.aspx--
<asp:HiddenField ID="hdnHiddenField" runat="server" />
<cc1:ModalPopupExtender ID="hdnHiddenField_mpeModalPopup" runat="server" Enabled="True" TargetControlID="hdnHiddenField"
CancelControlID="lnkClose" PopupControlID="pnlPanel" BackgroundCssClass="modalBackground"></cc1:ModalPopupExtender>
<asp:Panel ID="pnlPanel" runat="server" BorderStyle="Double" Width="50%" Height="75%" CssClass="modalPopup" style="overflow: auto; display: none">
<div class="modalHeaderClose">
<asp:LinkButton ID="lnkClose" runat="server" Text="Close [X]"></asp:LinkButton>
</div>
<cc1:Accordion ID="accdnPolicyInfo" runat="server" SelectedIndex="0" RequireOpenedPane="false">
<Panes>
<cc1:AccordionPane ID="accpnlDocuments" runat="server">
<Header>
<div class="accordionHeader">Documents</div>
</Header>
<Content>
<asp:UpdatePanel ID="upDocuments" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<div style="text-align: center; padding: 0 10px 10px 10px;">
<asp:GridView ID="gvGridView" runat="server" AutoGenerateColumns="False" Width="100%" ShowHeaderWhenEmpty="True">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:ImageButton ID="imgDownloadPDF" runat="server"
CommandArgument='<%# Bind("ImageKey") %>' CommandName="DownloadDocument"
ImageUrl="~/images/PDF-download.png" ToolTip="Download file to your computer" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
<EmptyDataRowStyle ForeColor="Red" HorizontalAlign="Center" />
</asp:GridView>
</div>
</ContentTemplate>
</asp:UpdatePanel>
</Content>
</cc1:AccordionPane>
</Panes>
</cc1:Accordion>
</asp:Panel>
--Default.aspx.vb--
Private Sub gvPolicyDocs_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles gvPolicyDocs.RowDataBound
Dim imgDownloadPDF As ImageButton = e.Row.FindControl("imgDownloadPDF")
If imgDownloadPDF IsNot Nothing Then
ScriptManager.GetCurrent(Page).RegisterPostBackControl(imgDownloadPDF)
'I just tried changing to RegisterAsyncPostBackControl method and found that I receive the same error even on the first attempt
End If
End Sub
Protected Sub gvPolicyDocs_RowCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles gvPolicyDocs.RowCommand
Try
StreamPDF(e.CommandArgument, False)
Catch ex As Exception
ErrorHandler.HandleError(ex) 'sends out an email and returns back; ErrorHandler is an object I created
End Try
hdnHiddenField_mpeModalPopup.Show() 'otherwise modal popup disappears after return from postback
End Sub
Private Sub StreamPDF(ByVal strImageKey As String)
Dim dtImagePaths As DataTable
Dim oPDFManager As New PdfManager 'from ASPPDF library
Dim bytes() As Byte
Dim strImagePath As String = String.Empty
If String.IsNullOrEmpty(strImageKey) Then
Throw New Exception("Invalid Image Key")
End If
'Get image path from database
...
...
strImagePath = dtImagePaths.Rows(0).Item("ImagePath")
bytes = oPDFManager.OpenDocument(strImagePath).SaveToMemory()
'I checked that on the run after the error, bytes contains exactly what I want - so when I write it to a file locally, I can see the PDF
With Current
.Response.BufferOutput = False 'tried true and false
.Response.Clear()
.Response.ClearContent()
.Response.ClearHeaders()
.Response.ContentType = "application/pdf"
.Response.AddHeader("Content-Disposition", String.Format("{0}; filename={1}.pdf", IIf(bDownloadFile, "attachment", "inline"), strImageKey))
.Response.AddHeader("Content-Length", bytes.Length)
.Response.BinaryWrite(bytes)
.Response.Flush()
.Response.Close()
'Response.End() '--> See http://support.microsoft.com/kb/312629
.ApplicationInstance.CompleteRequest()
End With
End Sub
The UpdatePanel is probably the problem here.
Please see here.
Why won't Response.Write() export a CSV file to the browser?
Well, in case anyone else runs into this issue, this article helped me out completely. It basically indicates that you can't download and use the asyncpostback functionality (via UpdatePanel) on the same page. It demonstrates a workaround in which all of the download code is done on an entirely separate page, and that page is opened in a hidden iframe on the current page. On a side note, if you are also or instead trying to open the pdf in a new window, the same workaround applies but instead of adding an iframe to the current page, you can call window.open() in JavaScript.
So, I'm not sure how to fix this in VB, but I know the cause of this issue in C# as I have just had to deal with this issue myself.
The problem lies with the button itself. You need to register the button with the ScriptManager. You cannot do this at the PageLoad() event, you actually have to add an OnInit event to the button and add the registration there.
Example in C#:
ASP page code:
<asp:ImageButton ID="imgDownloadPDF" runat="server" onInit="imgDownloadPDF_Init" ... />
Code Behind:
protected void imgDownloadPDF_Init(object sender, EventArgs e)
{
ScriptManager.GetCurrent(this).RegisterPostBackControl(imgDownloadPDF);
}
I hope this helps.

Resources