In a legacy ASP.NET Webforms app, I need to update a database table when the user clicks a button, depending on which checkboxes on the form are checked, using the value from a corresponding label to make the update.
It's not working; however, almost-identical code in a Winforms "test" app does work.
The FormLoad (Winforms) and Page_Load (Webforms) code works fine; there is no need to show all of that code; they both populate the form/page with pairs of label/checkbox controls.
To show how they differ one from the other, though: When the controls are dynamically created, they assign either a Tag value (Winforms) or ID (Webforms) string value (more on that below).
In the working (Winforms) code, it is done this way:
Dim lblCompanyName = New Label()
lblCompanyName.Tag = i.ToString()
lblCompanyName.Text = reader.Item(0).ToString()
Me.Controls.Add(lblCompanyName)
Dim ckbx = New CheckBox()
ckbx.Tag = i.ToString()
ckbx.Checked = True
Me.Controls.Add(ckbx)
In the nonworking (Webforms) code, it is done this way:
Dim coName = New Label()
' Must prepend with something, as controls cannot share the same ID
coName.ID = "lbl" + i.ToString()
coName.Text = categoryDT.Rows(i)(0).ToString()
formCustCatMaint.Controls.Add(coName)
Dim chk = New CheckBox()
chk.ID = "ckbx" + i.ToString()
chk.Checked = True
formCustCatMaint.Controls.Add(chk)
Besides the Form/Page load code, there are just three other blocks of code: a button click event handler, and two utility methods called by that handler.
The code in the Winforms app, which uses .NET Framework 4.5, works (updates the database table when the button is clicked); the code in the Webforms app, which uses .NET Framework 2, does not update the database table as it should - yet shows no err page, just sweetly grins and lazily rests on its laurels.
The only real difference I can see in the code between the two is that in one case (the Winforms app), the dynamically created controls' Tag property is assigned a value, whereas in the other (the Webforms site), the dynamically created controls' ID property is what is assigned. BTW, neither technology seem to have the other property available (Winforms can't seem to access an ID property for a control, and Webforms can't seem to access a Tag property for a control).
A related difference is that no two controls can have the same ID (Webforms); thus, I have to prepend "lbl" to the Label control IDs and "ckbx" to the Checkbox control IDs (each pair shares the same number, so that "lbl1" and "ckbx1" are a matched pair, "lbl42" and "ckbx42" are a matched pair, etc.).
Why does the almost-identical code in the Webforms app fail to update the database?
Here is the working (Winforms/.NET 4.5) code for those three code blocks:
Private Sub Button1_Click( sender As Object, e As EventArgs) Handles Button1.Click
Dim connStr As String = "SERVER=PLATYPUS42;DATABASE=duckbilldata;UID=schnozz;PWD=pondscum"
Dim upd8DML As String = "UPDATE CustomerCategoryLog SET Category = 'Exploding' WHERE Unit = @Unit And MemberNo = @MemberNo AND Custno = @CustNo"
Dim coName As String
Dim argVals(2) As String
Dim _Unit As String
Dim _MemberNo As String
Dim _CustNo As String
Dim curTagVal As String
For Each cntrl As Control In Me.Controls
If TypeOf cntrl Is CheckBox Then
If DirectCast(cntrl, CheckBox).Checked = True Then
curTagVal = CStr(DirectCast(cntrl, CheckBox).Tag)
coName = GetLabelTextForTag(curTagVal)
argVals = GetArgValsForCompanyName(coName)
_Unit = argVals(0)
_MemberNo = argVals(1)
_CustNo = argVals(2)
Using conn As New SqlConnection(connStr), _
cmd As New SqlCommand(upd8DML, conn)
cmd.Parameters.Add("@Unit", SqlDbType.VarChar, 50).Value = _Unit
cmd.Parameters.Add("@MemberNo", SqlDbType.VarChar, 50).Value = _MemberNo
cmd.Parameters.Add("@CustNo", SqlDbType.VarChar, 50).Value = _CustNo
conn.Open
cmd.ExecuteScalar()
End Using
End If
End If
Next
End Sub
Protected Function GetLabelTextForTag(tagVal As String) As String
Dim CoName As String = ""
For Each cntrl As Control In Me.Controls
If TypeOf cntrl Is Label Then
If DirectCast(cntrl, Label).Tag.ToString() = tagVal Then
CoName = DirectCast(cntrl, Label).Text
Exit For
End If
End If
Next
Return CoName
End Function
Protected Function GetArgValsForCompanyName(coName As String) As String()
Dim args(2) As String
Using con As New SqlConnection("SERVER=PLATYPUS42;DATABASE=duckbilldata;UID=schnozz;PWD=pondscum"),
cmd As New SqlCommand("select Unit, MemberNo, CustNo from Customers WHERE CompanyName = @CoName", con)
con.Open()
cmd.CommandType = CommandType.Text
cmd.Parameters.Add("@CoName", SqlDbType.VarChar, 50).Value = coName
Using reader As SqlDataReader = cmd.ExecuteReader
While reader.Read
args(0) = reader.Item(0).ToString()
args(1) = reader.Item(1).ToString()
args(2) = reader.Item(2).ToString()
End While
End Using
End Using
Return args
End Function
...and here is the nonworking (Webforms/.NET 2.0) code for the corresponding code blocks there:
Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim connStr As String = "SERVER=PLATYPUS42;DATABASE=duckbilldata;UID=schnozz;PWD=pondscum"
Dim upd8DML As String = "UPDATE CustomerCategoryLog SET Category = 'Exploding' WHERE Unit = @Unit And MemberNo = @MemberNo AND Custno = @CustNo"
Dim coName As String
Dim argVals(2) As String
Dim _Unit As String
Dim _MemberNo As String
Dim _CustNo As String
Dim curCheckboxIDVal As String
For Each cntrl As Control In Me.Controls
If TypeOf cntrl Is CheckBox Then
If DirectCast(cntrl, CheckBox).Checked = True Then
curCheckboxIDVal = CStr(DirectCast(cntrl, CheckBox).ID)
coName = GetLabelTextForID(curCheckboxIDVal)
argVals = GetArgValsForCompanyName(coName)
_Unit = argVals(0)
_MemberNo = argVals(1)
_CustNo = argVals(2)
Using conn As New SqlConnection(connStr), _
cmd As New SqlCommand(upd8DML, conn)
cmd.Parameters.Add("@Unit", SqlDbType.VarChar, 50).Value = _Unit
cmd.Parameters.Add("@MemberNo", SqlDbType.VarChar, 50).Value = _MemberNo
cmd.Parameters.Add("@CustNo", SqlDbType.VarChar, 50).Value = _CustNo
conn.Open
cmd.ExecuteScalar()
End Using
End If
End If
Next
End Sub
Protected Function GetLabelTextForID(ckbxIDVal As String) As String
Dim CoName As String = ""
Dim _idVal As String = ckbxIDVal
Dim numberLen As Integer = _idVal.Length - "ckbx".Length
_idVal = _idVal.Substring("ckbx".Length, numberLen)
LabelDebug.Text = _idVal
For Each cntrl As Control In Me.Controls
If TypeOf cntrl Is Label Then
If DirectCast(cntrl, Label).ID = "lbl" + _idVal Then
CoName = DirectCast(cntrl, Label).Text
Exit For
End If
End If
Next
Return CoName
End Function
Protected Function GetArgValsForCompanyName(coName As String) As String()
Dim args(2) As String
Using con As New SqlConnection("SERVER=PLATYPUS42;DATABASE=duckbilldata;UID=schnozz;PWD=pondscum"), _
cmd As New SqlCommand("select Unit, MemberNo, CustNo from Customers WHERE CompanyName = @CoName", con)
con.Open()
cmd.CommandType = CommandType.Text
cmd.Parameters.Add("@CoName", SqlDbType.VarChar, 50).Value = coName
Using reader As SqlDataReader = cmd.ExecuteReader
While reader.Read
args(0) = reader.Item(0).ToString()
args(1) = reader.Item(1).ToString()
args(2) = reader.Item(2).ToString()
End While
End Using
End Using
Return args
End Function
As you can see, the code is virtually identical, yet the results (success/failure) are diametrically opposed. Why?
UPDATE
Apparently this line is failing:
If TypeOf cntrl Is CheckBox Then
Although there are checkboxes on the page, that test fails. Why?
I know this because I added some "debug" tests inside the Button1_Click handler like so (can't step through the code):
For Each cntrl As Control In Me.Controls
Label2.Text="label 2 text from foreach"
If TypeOf cntrl Is CheckBox Then
Label2.Text="label 2 text from is checkbox"
If DirectCast(cntrl, CheckBox).Checked = True Then
Label2.Text="label 2 text from checked"
. . .
The last text written to Label2 is "label 2 text from foreach".
So, "If TypeOf cntrl Is CheckBox Then" fails.
Are the checkboxes not seen because they were created dynamically? If so, how can I get around this?
UPDATE 2
When I add this to the corresponding .aspx file:
<%@ Page Language="VB" AutoEventWireup="true" IsPostback="true" CodeFile="custmaint_categoryadmin.aspx.vb"
Inherits="pages_custmaint_categoryadmin" %>
(added the "IsPostback="true"" part)
..."IsPostback" is underscored with the red squigglies, and when I run it, I get:
Parser Error
Description: An error occurred during the parsing of a resource required to service this request. Please review the following specific parse error details and modify your source file appropriately.
Parser Error Message: Error parsing attribute 'ispostback': The 'ispostback' property is read-only and cannot be set.
Source Error:
Line 1: <%@ Page Language="VB" AutoEventWireup="true" IsPostback="true" CodeFile="custmaint_categoryadmin.aspx.vb" Inherits="pages_custmaint_categoryadmin" %>
Line 2:
Line 3: <!DOCTYPE html>
UPDATE 3
Now here is something super-bizarre, it seems to me: I dropped a checkbox onto the Webform, and ran it again. Even with the visible-at-design-time checkbox there on the form, it STILL doesn't get past the "If TypeOf cntrl Is CheckBox Then" test; the last update to Label2.Text is still "label 2 text from foreach"
How can it be that a Checkbox, especially a "concrete" rather than dynamic one, does not equate as a checkbox?!?