Issue binding a DataTable to an ASP.Net GridView control - asp.net

I'm trying to allow my users to enter in large lists of data using an ASP.Net GridView control. The affect I'm trying to create is to make the GridView control act like a spreadsheet. The user can freely enter data and tab from column to column and row to row. The user can use a button at the bottom of the page to add rows as needed. There is also a button at the bottom of the form to save as needed.
To do this, I created a DataTable with a bunch of empty rows and bound it to a GridView. The GridView's columns are template columns that contain textboxes. So, when the page opens, it actually looks like a spread sheet. When the user hits the add rows button, I just add another ten rows to the DataTable the GridView is bound to and it works like a charm.
The issue I'm running into is reading the data that the user entered. When the user hits the paging link or the update button, I would like to update the DataTable with the data the user typed in. Here is what I have.
Private Sub UpdateDataTable()
Dim objCatRow As clsCategoriesRow = Session("gvCategoriesRow")
Dim drQuery() As DataRow = Nothing
Dim drRow As DataRow = Nothing
Dim objRow As GridViewRow = Nothing
Dim intRecNo As Integer = 0
Dim txt As TextBox = Nothing
Dim lbl As Label = Nothing
'Loop through all of the rows in the grid view control
For Each objRow In Me.gvCategories.Rows
'Get the label that contains the identity column
lbl = objRow.Cells(GridColumns.Category).FindControl("lblItemRecNo")
intRecNo = lbl.Text
'Update the datarow bound to this grid view row
'First, query the datarow from the data table
drQuery = objCatRow.Table.Select("recno = " & intRecNo)
'Make sure our query returned a row
If Not IsNothing(drQuery) AndAlso drQuery.Count > 0 Then
'Get the value from the textbox in the grid view
txt = objRow.Cells(GridColumns.Category).FindControl("txtItemCategory")
'Upadte the data row with the value the user entered
'THE VALUE IN txt.Text IS EMPTY. HOW CAN I GET THE VALUE THE USER TYPED IN?
drQuery(0)("Category") = txt.Text
'Get the value from the textbox in the grid view
txt = objRow.Cells(GridColumns.SortORder).FindControl("txtItemSortOrder")
'Upadte the data row with the value the user entered
drQuery(0)("sortorder") = txt.Text
End If
Next
End Sub
The issue is that this is not returning what the user typed in. The line
txt = objRow.Cells(GridColumns.Category).FindControl("txtItemCategory")
returns a reference to the textbox in the templated column. But, it contains the previous value, the value from the view state, not the value the user typed in.
How can I get the value the user typed into the grid?
I want to mention that I know how to add EDIT and UPDATE buttons to each row. I would like to avoid doing that way if I can. My users have huge lists of data to enter in and that approach would make the application unusable.
Thanks in advance,
Mike

The form data posted by the user is found in the Page.Request.Form.Item collection. The Page.Request.Form.AllKeys lists the "keys" associated with all of the form item values.
If Page.Request.Form.HasKeys Then
For Each key as String In Page.Request.Form.AllKeys
' step through the keys and use Page.Request.Form.Item(key) to get the data entered
Next
end If
After testing, I was able to get the data from the Request.Form data or the GridView control during page.load on postback, as long as you don't bind the control on postback, but only during the initial request ("GET").
Keep in mind, controls have to be re-created for each request to the page. The Request data posted is used by ASP.NET to repopulate form data controls only after the controls are re-created on the page and the ViewState for the controls is processed, etc.
Request.Form collection
ASP.NET Page Life Cycle

You should replace this:
txt = objRow.Cells(GridColumns.Category).FindControl("txtItemCategory")
with this
txt = objRow.Cells(GridColumns.Category).FindControl("txtItemCategory").Value

Related

how to dynamically add rows to a GridView through a DataTable without affecting any table?

I have to show a gridview in my page which doesn't concerns any table to be bound with it. Still it should store one Tax detail per row, as entered by user. Following is the image which will clarify what problem I'm facing.
The gridview shown above should not show any records at first except the footer template of two textboxes and an Add New Record Button, when you are upto add any record.
Once clicked on Add New Record button it should show a row without action column.
(I'm having quite a hard time here)Once clicked on Add/Insert Button(separate from the gridview) store the entered record somehow.
Then I have to show the record in Another concrete GridView with Edit Button.
Once I click on edit button, control should return to my dynamic gridview page. Here, my dynamic gridview will show previously entered record with action column containing a delete button.
Can you Help Me? I don't want help with the code. Just point me in the right direction(s), please.
You should try this.
private DataTable AddEmptyRow()
{
DataTable originalDataTable = GetItems();
DataTable newDataTable = originalDataTable.Clone();
string category = originalDataTable.Rows[0][2].ToString();
foreach (DataRow dRow in originalDataTable.Rows)
{
if (category != dRow[2].ToString())
{
DataRow newDataRow = newDataTable.NewRow();
newDataTable.Rows.Add(newDataRow);
category = dRow[2].ToString();
}
newDataTable.ImportRow(dRow);
}
return newDataTable;
}
You can add row by using DataRow newDataRow = newDataTable.NewRow();.

Update Label control In first record of DataList during ItemDataBound

Using Asp.net and VB.net. I have a DataList on a webpage. The datalist has a label control. I want to update the text of the label control in the first record with information obtained from the subsequent records as those subsequent records are databound. In other words, each time the datalist gets bound, I want to identify the label in the first record and then update the text of that label. I'm attempting to do this in the ItemDataBound by getting the ClientID of the label in the first record:
Dim strMealPrice As String = CType(e.Item.FindControl("lblMealPrice"), Label).ClientID
and then hold that ClientID in a hidden label outside of the datalist.
If lblhidMealHeaderID.Text = "" then
lblhidMealHeaderID.Text = strMealPrice
End if
Everything works up to this point.
Then each time the datalist ItemDataBound is fired I use findcontrol to try to update the label in the first record but I'm unsure how to format the findcontrol when using a variable for the label control's ClientID (lblhidMealHeaderID.text). But even when I hard code the ClientID of the label in the first record I can't get it to work.
Dim tempLabel As Label = DataList1.FindControl("DataList1_ctl00_lblMealPrice")
or
Dim tempLabel As Label = CType(e.Item.FindControl("DataList1_ctl00_lblMealPrice"), Label)
I get a Object reference not set to an instance of an object. when I try to write to tempLabel.
As you can see I'm grasping here. First, is this the best way to do this - is the ItemDataBound where I should be attempting this? Perhaps you can't update previous records while the DataList is "binding" subsequent records. Second, is ClientID the way to do this - I see ClientID is used mostly for javascript? Third, how do I properly format the FindControl using ClientID?
Any and all help is greatly appreciated.
In ItemDataBound use this
If e.Item.ItemIndex = 0 Then
CType(e.Item.FindControl("lblMealPrice"), Label).Text = strMealPrice
End If
Update
You can find the first label any time after binding by looping through its items.
For Each item as DataGridItem In dgGrid.Items
CType(item.FindControl("lblMealPrice"), Label).Text = strMealPrice
Next

value of variables not changing in asp.net

currently I am working on a project named online exam.
All the controls are dynamically created.
I have a webpage where I want to display the student details.
I displayed those details correctly in a table.
Now here comes the time to edit those details.
To edit a record I use the linked button named edit.
When a user clicks on that Linked button the data in that row is replaced with new textboxes.
Upto here I am OK.
Now when I click on the save changes button after making changes to the textboxes.
The old values are not replaced by the new values and the old values remains.
The code for creating textboxes in the table is as follows :
Public Sub Edit_Click(ByVal sender As Object, ByVal e As System.EventArgs)
For x As Integer = 0 To EditList.Count - 1
If sender.id.substring(4) = EditList(x).ID.Substring(4) Then
Session("PreviousRollNo") = RollNoList(x).Text
Dim txtName As New TextBox
txtName.Text = NameList(x).Text
NameList(x).Text = ""
NameList(x).Parent.Controls.Add(txtName)
txtList.Add(txtName)
Dim txtCourse As New TextBox
txtCourse.Text = CourseList(x).Text
CourseList(x).Text = ""
CourseList(x).Parent.Controls.Add(txtCourse)
txtList.Add(txtCourse)
Dim txtAdmissionDate As New TextBox
txtAdmissionDate.Text = AdmissionList(x).Text
AdmissionList(x).Text = ""
AdmissionList(x).Parent.Controls.Add(txtAdmissionDate)
txtList.Add(txtAdmissionDate)
Dim btnSaveChanges As New Button
btnSaveChanges.Text = "Save Changes"
EditList(x).Text = ""
EditList(x).Parent.Controls.Add(btnSaveChanges)
AddHandler btnSaveChanges.Click, AddressOf btnSaveChanges_Click
Session("EditButtonClicked") = True
Dim btnCancel As New Button
btnCancel.Text = "Cancel"
DeleteList(x).Text = ""
DeleteList(x).Parent.Controls.Add(btnCancel)
AddHandler btnCancel.Click, AddressOf btnCancel_Click
Session("CancelButtonClicked") = True
txtName.Focus()
Exit For
End If
Next
End Sub
The code for Save Changes button is as follows :
Public Sub btnSaveChanges_Click(ByVal sender As Object, ByVal e As System.EventArgs)
If txtList(0).Text = "" Then
Dim trError As TableRow = New TableRow
Dim tdError As TableCell = New TableCell
tdError.ColumnSpan = 7
Dim lblError As New Label
lblError.Text = "Please enter name of the student."
lblError.ForeColor = Drawing.Color.Red
tdError.Controls.Add(lblError)
trError.Controls.Add(tdError)
tbl.Controls.Add(trError)
ElseIf txtList(1).Text = "" Then
Dim trError As TableRow = New TableRow
Dim tdError As TableCell = New TableCell
tdError.ColumnSpan = 7
Dim lblError As New Label
lblError.Text = "Please enter the course."
lblError.ForeColor = Drawing.Color.Red
tdError.Controls.Add(lblError)
trError.Controls.Add(tdError)
tbl.Controls.Add(trError)
ElseIf txtList(2).Text = "" Then
Dim trError As TableRow = New TableRow
Dim tdError As TableCell = New TableCell
tdError.ColumnSpan = 7
Dim lblError As New Label
lblError.Text = "Please enter the Admission Date"
lblError.ForeColor = Drawing.Color.Red
tdError.Controls.Add(lblError)
trError.Controls.Add(tdError)
tbl.Controls.Add(trError)
Else
Dim cb As New OleDbCommandBuilder(da)
Dim editRow() As DataRow
editRow = ds.Tables("Student_Detail").Select("Roll_No = '" & Session("PreviousRollNo") & "'")
editRow(0)("Name") = txtList(0).Text
editRow(0)("Course") = txtList(1).Text
editRow(0)("Admission_Date") = txtList(2).Text
da.Update(ds, "Student_Detail")
Page.Response.Redirect("ChangeUserDetails.aspx")
End If
End Sub
I get the error sying that array is out of the bounds. on the first line of the btnSaveChanges_Click.
It means txtlist is always cleared when I click on Save Changes Button.
So I stored txtList in a Session like Session("txtList") = txtList.
and retrieved the data from that. But now I get the old values of the textbox instead of the newer ones.
Here txtList is a list (of Textbox)
Firstly, welcome to the ASP.NET WebForms Page Life Cycle. Remember its pattern with the simple mnemonic: SILVER = Start, Init, Load, Validate, Events, Render.
Secondly, HTTP is stateless. WebForms does an amazing job of hiding this fact from you using ViewState until you do something a little out of the ordinary (as you're now attempting), and it all appears to fall apart. What's really happening is that you're starting to see side-effects of how WebForms is managed, and how it's not as much like WinForms (or another stateful system) as you might think.
When you're responding to an event server-side in WebForms, it's easy to get the impression that nothing has changed. That the entire page is as you left it "last time". All the controls are there, the values you may have set programatically are still set. Magic. Not magic. What's actually happened is the entire page has been re-constructed to respond to that event. How was it re-constructed? By a combination of your page definition (markup), actions taken in control event handlers, and the form data posted back by the client.
Confusing? OK, let's consider an example. Say you've got a page with two controls on it. A textbox named txtInput and a button named btnSubmit with event handler btnSubmit_Click. When the user first requests the page, the HTML for these controls is derived from your markup (aspx page) and returned to the client. Next, the user sets a value in txtInput and clicks the submit button. The server then re-creates the page from scratch based on your markup. At this early stage of the life-cycle, the controls still have their default values. We then hit the Load stage of the life-cycle, and "if the current request is a postback, control properties are loaded with information recovered from view state and control state." In other words, by the time the life-cycle gets to Init, the control has been created from markup, but still has its default value. The Load stage then sets the value according to Postback data.
Left wondering how this applies to your scenario? You're adding your dynamic controls in response to a control event. There's two things wrong with that:
It's too late in the page life-cycle for Init to set the values based on data posted back from the client (recall SILVER, Event is after Init).
Your button click event handler is only run once, in response to the postback where the user clicked the button. But remember on each postback the page is entirely re-created. So the dynamic controls no longer exist as far as the server is concerned! You'll notice that not only are the controls not present server side when responding to the submit event, but after the page has handled it, they're no longer present client-side either.
So what's the answer? Well the "Life-Cycle Events" section of the page I linked offers a clue. It states that the PreInit event be used to (among other things) "Create or re-create dynamic controls". Why would we do it in PreInit? So it's early enough in the page life-cycle for the later events to properly handle it (like setting the values posted back from the client).
Now, I know, you want to add the controls based on the user clicking on the button. How does that fit? The trick is that you've got to manage the "state" yourself. Huh? the state? By this I mean MyDynamicControlsShouldBeShown = true / false. When the button is clicked, creating the controls in response to the button-click event handler is the right action (there's not really any choice there). But you need to store that state somehow so you know on subsequent requests to the page, whether those controls should be re-created in PreInit. One neat option would be to check for the ID of your dynamic control in Request.Form.Keys. If the control ID is present in the Keys collection, then the user is posting back a value for the control, so you should re-create it.
A side-note on the use of Session
Hopefully based on the above you've realised why putting the controls into Session didn't work. But to be clear, the controls you put into the Session object were no longer part of a page that existed (remember, the page gets completely re-created for each request. Those controls were no longer hooked up to the Page events, so didn't get their values populated between Page Init and Load. If somehow it did work, it still wouldn't be a particularly good idea, as Session is not per-request. So what would happen if a user had the same page open in multiple tabs? Strange things, that's what.

How to select the Checked rows in GridviewRow and display it in next page in VB.net

I am trying to get the rows from the gridviewrow and display it in the detail view in next page. I am using Asp.net with VB
Simply iterate the collection and check the status. You can then put the selected rows into a session object or something else to pass on to the next page.
For Each Row As GridViewRow In MyGrid.Rows
Dim SelectCheck As CheckBox = DirectCast(Row.FindControl("chkSelectForDetail"), CheckBox)
If SelectCheck.Checked
' Add your logic here to save the data or passing on to the detail page.
End If
Next

Getting information (on click) that was used to programatically generate asp controls

How may one get information that was used to programatically generate asp controls?
For example, I pulled a DataTable of user objects from the database and have organized them on a page, listing groupings such as a list of employees directly under the employer for each employer. On the page, I list each user's Username as a LinkButton. When I click one of these employees, I want to redirect the page (which is easy) and set a session variable to the selected user's UserId (which seems not so easy). How can I pull this UserId value back? These elements are not hard-coded with nice names (as they are generated in a for each loop).
Code from comment below:
Dim lnkbtnPm As New LinkButton ' is my link button. '
lnkbtnPm.Text = pmDr.Item("Username") ' where pmDr is my datarow. '
lnkbtnPm.CommandArgument = pmDr.Item("UserId")
lnkbtnPm.CommandName = "CommandNameHere"
panelToAddControlTo.Controls.Add(lnkbtnPm)
Thanks :)
I think this is what you would use the CommandName and CommandArgument properties of the LinkButton for. Assign the user id as CommandArgument and a suitable string as CommandName and hook up the Command event to an event handler:
Sub LinkButton_Command(sender As Object, e As CommandEventArgs)
' e.CommandArgument will contain the user id '
End Sub
Update
The problem is that the event handler is never attached. Use AddHandler to do that:
Dim lnkbtnPm As New LinkButton
lnkbtnPm.Text = pmDr.Item("Username") ' where pmDr is my datarow. '
lnkbtnPm.CommandArgument = pmDr.Item("UserId")
lnkbtnPm.CommandName = "CommandNameHere"
AddHandler lnkbtnPm.Command, AddressOf LinkButton_Command
panelToAddControlTo.Controls.Add(lnkbtnPm)

Resources