Page lifecycle is causing an error with user controls within sub - asp.net

Thanks to #Angkor Wat I achived a major step towards my goal: dynamic adding of pieces of strings to a string. But I came across another thing I cannot solve.
Here is the script:
<%# Page Language="VB" AutoEventWireup="false" CodeFile="addtostring.aspx.vb" Inherits="demo_addtostring" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<p><asp:textbox id="tb" runat="server"></asp:textbox></p>
<asp:Panel ID="tbPanel" runat="server"></asp:Panel>
</div>
</form>
</body>
</html>
This is the code behind:
Partial Class demo_addtostring
Inherits System.Web.UI.Page
Public Property gesStr As String
Set(value As String)
ViewState("gesStr") = value
End Set
Get
Dim o As Object = ViewState("gesStr")
If o Is Nothing Then
Return ""
Else
Return o
End If
End Get
End Property
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
If Page.IsPostBack Then
Else
gesStr = "1;"
End If
tb.Text = gesStr
Dim iButton As New Button
iButton.Text = "add"
iButton.CommandArgument = "1;"
iButton.CommandName = "1;"
AddHandler iButton.Click, AddressOf add
tbPanel.Controls.Add(iButton)
If Page.IsPostBack Then
Else
anzeige()
End If
End Sub
Private Sub add(ByVal sender As Object, ByVal e As EventArgs)
Dim myButton As Button = DirectCast(sender, Button)
Dim addString As String = myButton.CommandArgument
gesStr += addString
tb.Text = gesStr
anzeige()
End Sub
Private Sub anzeige()
Dim gesArray As Array = Split(gesStr, ";")
For xLauf As Integer = 0 To UBound(gesArray) - 1
Dim anzeigeDiv As New System.Web.UI.HtmlControls.HtmlGenericControl("div")
Dim anzLabel As New Label
anzLabel.Text = gesArray(xLauf)
anzeigeDiv.Controls.Add(anzLabel)
Dim iButton2 As New Button
iButton2.Text = xLauf.ToString
iButton2.ID = "test" & xLauf.ToString
iButton2.CommandArgument = "1;"
iButton2.CommandName = "1;"
AddHandler iButton2.Click, AddressOf add
anzeigeDiv.Controls.Add(iButton2)
tbPanel.Controls.Add(anzeigeDiv)
Next
End Sub
End Class
Clicking on the add-button should add "1;" to gesStr - the dynamic loop-generated buttons should do the same -.- Does anyone have an idea? I would be very thankful for help...

In order for the postback to know about the event handler from the button, the button needs to be recreated prior to the point in the lifecycle where the handler is invoked. In other words, you will always need to recreate the buttons within your Page_Load.
Here is a modification to your code which works:
<form id="form1" runat="server">
<div>
<p><asp:textbox id="tb" runat="server"></asp:textbox></p>
<br />
<asp:Button runat="server" ID="btnAdd" Text="add" CommandArgument="1;" />
<asp:Panel ID="tbPanel" runat="server"></asp:Panel>
</div>
</form>
And code-behind:
Public Property gesStr As String
Get
Dim o As Object = ViewState("gesStr")
If o Is Nothing Then
Return ""
Else
Return DirectCast(o, String)
End If
End Get
Set(value As String)
ViewState("gesStr") = value
End Set
End Property
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
gesStr = "1;"
End If
tb.Text = gesStr
End Sub
Private Sub add(ByVal sender As Object, ByVal e As CommandEventArgs) Handles btnAdd.Command
Dim addString As String = e.CommandArgument
gesStr += addString
tb.Text = gesStr
CreateNewButton(tbPanel.Controls.Count, addString.Substring(0, addString.Length - 1))
End Sub
Protected Overrides Sub CreateChildControls()
MyBase.CreateChildControls()
Dim gesArray As Array = Split(gesStr, ";")
For xLauf As Integer = 0 To UBound(gesArray) - 1
CreateNewButton(xLauf, gesArray(xLauf))
Next
End Sub
Private Sub CreateNewButton(ByVal xLauf As Integer, ByVal labelText As String)
Dim anzeigeDiv As New Panel
anzeigeDiv.ID = "div" & xLauf.ToString()
Dim anzLabel As New Label
anzLabel.Text = labelText
anzeigeDiv.Controls.Add(anzLabel)
Dim iButton2 As New Button
iButton2.Text = xLauf.ToString
iButton2.ID = "test" & xLauf.ToString()
iButton2.CommandArgument = "1;"
iButton2.CommandName = "1;"
AddHandler iButton2.Command, AddressOf add
anzeigeDiv.Controls.Add(iButton2)
tbPanel.Controls.Add(anzeigeDiv)
End Sub
I've moved the add button, which will always exist, into the aspx page, so that the dynamic panel will only contain the buttons which have been added based on gesStr value.

Dynamic controls must be re-created in every postback see this article for more information.

Related

How can I use AddHandler AddressOf

I'm working on a web app using Visual Studio VB.NET. I have a web form with two panels. I have the code below:
Protected Sub WebForm2_load(sender As Object, e As EventArgs) Handles Me.Load
Dim y As Integer = 1
Protected cr(100) As Button
If con.State = ConnectionState.Open Then
con.Close()
End If
con.Open()
Dim requ As String
requ = "SELECT DISTINCT ENSEIGNEMENTS.Code_Mat From ENSEIGNANT INNER Join ENSEIGNEMENTS On ENSEIGNANT.Code_Ens = ENSEIGNEMENTS.Code_Ens where NomUser='" + un + "' and Sem='S1'"
cmd = New SqlCommand(requ, con)
Dim rd As SqlDataReader = cmd.ExecuteReader()
While rd.Read()
cr(y) = New Button
cr(y).Text = rd("Code_Mat").ToString
cr(y).ID = "btn" & y.ToString
Panel1.Controls.Add(cr(y))
AddHandler cr(y).Click, AddressOf btnc
y = y + 1
End While
End Sub
Protected Sub btnc(ByVal sender As Object, ByVal e As System.EventArgs)
Protected cr1(100) As Button
If con.State = ConnectionState.Open Then
con.Close()
End If
con.Open()
Dim btn As Button()
Dim x As Integer = 1
Dim requete As String
cdmat = CType(sender, Button).Text
'Response.Redirect("TestGroupes.aspx")
requete = "SELECT ENSEIGNEMENTS.Gr FROM ENSEIGNANT INNER JOIN ENSEIGNEMENTS ON ENSEIGNANT.Code_Ens = ENSEIGNEMENTS.Code_Ens WHERE (((ENSEIGNEMENTS.Code_Mat)='" + cdmat + "') AND ((ENSEIGNANT.[NomUser])='" + un + "') AND ((ENSEIGNEMENTS.[Sem])='S1'))"
Dim commande As New SqlCommand
commande = New SqlCommand(requete, con)
Dim reader As SqlDataReader = commande.ExecuteReader()
While reader.Read()
cr1(x) = New Button()
cr1(x).Text = reader("Gr").ToString
cr1(x).ID = "bt" & x.ToString
Panel2.Controls.Add(cr1(x))
AddHandler cr1(x).Click, AddressOf Groupe
x = x + 1
End While
End Sub
Protected Sub Groupe(sender As Object, e As EventArgs)
MsgBox("hhhhh")
End Sub
My problem is that AddHandler cr(y).Click, AdressOf btnc and WenForm2_load work well but AddHandler cr1(x).Click, AdressOf Groupe does not: when I click on a button in Panel2 nothing happens.
You always create the primary buttons (Code_Mat), but you also need to re-create the secondary buttons (Gr) when it posts back, so you'll need to save the variable(s) used to create those secondary buttons.
As a demonstration, I created a new Web Forms project named "testing" with one page named "index.aspx":
<%# Page Language="vb" AutoEventWireup="false" CodeBehind="index.aspx.vb" Inherits="testing.index" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Panel ID="Panel1" runat="server"></asp:Panel>
<asp:Panel ID="Panel2" runat="server"></asp:Panel>
</div>
<asp:Literal id="msg" runat="server">msg</asp:Literal>
</form>
</body>
</html>
Using this code-behind:
Public Class index
Inherits System.Web.UI.Page
Sub Groupe(sender As Object, e As EventArgs)
Dim bn = DirectCast(sender, Button)
msg.Text = bn.CommandArgument
End Sub
Sub CreateSecondaryButtonsHelper(codeMat As String)
Panel2.Controls.Clear()
For i = 1 To 3
Dim bn As New Button With {.Text = "Gr" & i & codeMat, .ID = "bnB" & i, .CommandArgument = "Hello from " & codeMat & i}
AddHandler bn.Click, AddressOf Groupe
Panel2.Controls.Add(bn)
Next
End Sub
Sub CreateSecondaryButtons(ByVal sender As Object, ByVal e As System.EventArgs)
Dim clickedButton = DirectCast(sender, Button)
CreateSecondaryButtonsHelper(clickedButton.Text)
Session("CodeMat") = clickedButton.Text
End Sub
Sub CreateMainButtons()
For i = 1 To 3
Dim bn As New Button With {.Text = "CodeMat" & i, .ID = "bnA" & i}
AddHandler bn.Click, AddressOf CreateSecondaryButtons
Panel1.Controls.Add(bn)
Next
If Session("CodeMat") IsNot Nothing Then
CreateSecondaryButtonsHelper(CStr(Session("CodeMat")))
End If
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
CreateMainButtons()
End Sub
End Class
It is rather dull, but I think it shows what you want to do.
N.B. You should make a new SqlConnection each time you use it and call .Dispose() on it as soon as you've finished using it. Also, using SQL parameters to pass the values instead of making up a string with them will make it more reliable and avoid SQL injection attacks.

Where is my dynamic control after I submit the form?

I created a dynamic form with a repeater.
<asp:repeater ID="fieldRepeater" OnItemDataBound="dataBound" runat="server">
<itemtemplate>
<div id="controlRow" class="row" runat="server">
<div id="testContainer" class="col-md-2" runat="server">
</div>
</div>
</itemtemplate>
</asp:repeater>
in codebehind I created the field I want to appear in it.
Protected Sub dataBound(ByVal sender As Object, e As RepeaterItemEventArgs)
dim temp as new DropDownList
dim tempLabel as new label
Dim testContainer As HtmlGenericControl = e.Item.FindControl("testContainer")
'createField
temp.ID = "testField" & e.Item.ItemIndex 'this is testField0
temp.Items.Add(New ListItem("Not Used", 0))
temp.Items.Add(New ListItem("Used", 1))
temp.CssClass = "form-control"
'createLabel
tempLabel.ID = "testFieldLabel" & e.Item.ItemIndex
tempLabel.AssociatedControlID = "testField" & e.Item.ItemIndex
tempLabel.Text = dr("controlLabel")
testContainer.Controls.Add(temp)
testContainer.Controls.Add(tempLabel)
end sub
The form works beautifully and I can even pre-populate it with data from a db, but in my submit handler my controls don't exist:
Protected Sub submit_Click(ByVal sender As Object, ByVal e As EventArgs) Handles submit.Click
Dim i As Int32 = 0
For Each r As DataRow ...
'db stuff
Dim temp As New DropDownList
temp = Me.FindControl("testField" & i.ToString)
'db stuff
i += 1
next
end sub
temp = Me.FindControl("testField" & i.ToString) is always nothing Can someone help me find out why?
My HTML output looks like:
<div id="cntMain_fieldRepeater_testContainer_0" class="col-md-2">
<label for="cntMain_fieldRepeater_testField0_0" id="cntMain_fieldRepeater_testFieldLabel0_0" style="color:#007AFF;">Activity</label>
<select name="ctl00$cntMain$fieldRepeater$ctl00$testField0" id="cntMain_fieldRepeater_testField0_0" class="form-control">
<option value="0">Not Used</option>
<option value="1">Used</option>
</select>
</div>
Dynamically created Controls need to be (re)created on every Page Load, and that includes a PostBack. So you have to move the DataBinding of the Repeater outside the !IsPostBack check.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
fieldRepeater.DataSource = loadDataHere
fieldRepeater.DataBind
End Sub
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim i As Integer = 0
For Each item As RepeaterItem In fieldRepeater.Items
Dim temp As DropDownList = CType(item.FindControl(("testField" + i.ToString)),DropDownList)
temp.BackColor = Color.Red
i = (i + 1)
Next
End Sub

Writing a passed Array via session into Label isn't working

I am trying to take an array of string (Filled from a listbox on a previous page and passed via Session) and display it in a label,this is how i got the array:
Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles CheckOut.Click
Dim x = ListBox1.GetSelectedIndices.Count
Dim ListPNames(x) As String
Dim i As Integer
i = 0
For Each item As String In ListBox1.GetSelectedIndices
ListPNames(i) = (ListBox1.SelectedItem).ToString
i = i + 1
Next
Session("SlctdPhones") = ListPNames(x)
Response.Redirect("CheckOut.aspx")
End Sub
And this is how i am trying to display it :
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim SlctdPhones() As String = CType(Session.Item("SlctdPhones"), Array)
Dim i As Integer
Label3.Text = ""
For i = 0 To SlctdPhones.Length - 1
Label3.Text += SlctdPhones(i).ToString() + Environment.NewLine
Next
End Sub
It is giving me an error :Object reference not set to an instance of an object. when it reaches the SlctdPhones.Length - 1 Line!!
i don't know how i can fix it ,also is my array code correct(Is everything being stored correctly in it?)
You declare the For loop like this:
For Each item In ...
But then never use the item variable in the body of the loop. Instead, you keep using the same SelectedItem property. You want to change that whole method to look like this:
Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles CheckOut.Click
Dim PNames As New List(Of String)()
For Each index As Integer In ListBox1.GetSelectedIndices
PNames.Add(ListBox1.Items(index).Value)
Next
Session("SlctdPhones") = PNames
Response.Redirect("CheckOut.aspx")
End Sub
With that fixed, the Page_Load can do this:
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim SlctdPhones As List(Of String) = TryCast(Session.Item("SlctdPhones"), List(Of String))
If SlctdPhones Is Nothing OrElse SlctdPhones.Length = 0 Then
'Something went wrong here!
Return
End If
Label3.Text = String.Join("<br/>", SlctdPhones.ToArray())
End Sub
But I'd really love to see you use a data control rather than stuffing <br/>s into a label. Here's markup for a ListView:
<asp:ListView ID="ListView1" runat="server">
<LayoutTemplate>
<ul>
<asp:PlaceHolder ID="itemPlaceholder" runat="server" />
</ul>
</LayoutTemplate>
<ItemTemplate>
<li><%# Container.DataItem.ToString() %></li>
</ItemTemplate>
<EmptyDataTemplate>
<p>Nothing here.</p>
</EmptyDataTemplate>
</asp:ListView>
And then Page_Load is even simpler:
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
ListView1.DataSource = Session.Item("SlctdPhones")
ListView1.DataBind()
End Sub
On display page, use Literal instead of Label
Dim SlctdPhones() As String = CType(Session.Item("SlctdPhones"), Array)
Dim result as String = string.Join("<br>", SlctdPhones) 'Instead of <br> try Environment.NewLine as well
YourLitetal = result
Hope this helps!

How to get current cell value of gridview in asp.net? strautoid retrieves nothing

Protected Sub GridView1_RowUpdating(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewUpdateEventArgs) Handles GridView1.RowUpdating
sqlqrystrng = "UPDATE temp_zone set zone_id = #zoneid, zone_name = #zonename WHERE auto_id = #autoid"
Dim strautoid As String = GridView1.Rows(e.RowIndex).Cells(1).Text
'Dim strautoid As String = Int(GridView1.SelectedRow.Cells(0).Text.ToString())
grid_view_load()
End Sub
sqlqrystrng = "UPDATE temp_zone set zone_id = #zoneid, zone_name = #zonename WHERE auto_id = #autoid"
Dim strautoid As String = GridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Text
grid_view_load()
Dim Gridview1 As New DataTable
For i As Integer = 0 To Gridview1.Rows.Count - 1
Gridview1.Rows(i).Item(0).ToString()
Next
End Sub
I used a DataTable instead of a gridview because the project I have open is a windows service not a form app but it should work the same
<%# Page Language="VB" %>
<%# Import Namespace="System.Data" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
Protected Sub Page_Load()
If Not Page.IsPostBack Then
' Create a new table.
Dim taskTable As New DataTable("TaskList")
' Create the columns.
taskTable.Columns.Add("Id", GetType(Integer))
taskTable.Columns.Add("Description", GetType(String))
taskTable.Columns.Add("IsComplete", GetType(Boolean))
'Add data to the new table.
For i = 0 To 19
Dim tableRow = taskTable.NewRow()
tableRow("Id") = i
tableRow("Description") = "Task " + i.ToString()
tableRow("IsComplete") = False
taskTable.Rows.Add(tableRow)
Next
'Persist the table in the Session object.
Session("TaskTable") = taskTable
'Bind data to the GridView control.
BindData()
End If
End Sub
Protected Sub TaskGridView_PageIndexChanging(ByVal sender As Object, ByVal e As GridViewPageEventArgs)
TaskGridView.PageIndex = e.NewPageIndex
'Bind data to the GridView control.
BindData()
End Sub
Protected Sub TaskGridView_RowEditing(ByVal sender As Object, ByVal e As GridViewEditEventArgs)
'Set the edit index.
TaskGridView.EditIndex = e.NewEditIndex
'Bind data to the GridView control.
BindData()
End Sub
Protected Sub TaskGridView_RowCancelingEdit()
'Reset the edit index.
TaskGridView.EditIndex = -1
'Bind data to the GridView control.
BindData()
End Sub
Protected Sub TaskGridView_RowUpdating(ByVal sender As Object, ByVal e As GridViewUpdateEventArgs)
'Retrieve the table from the session object.
Dim dt = CType(Session("TaskTable"), DataTable)
'Update the values.
Dim row = TaskGridView.Rows(e.RowIndex)
dt.Rows(row.DataItemIndex)("Id") = (CType((row.Cells(1).Controls(0)), TextBox)).Text
dt.Rows(row.DataItemIndex)("Description") = (CType((row.Cells(2).Controls(0)), TextBox)).Text
dt.Rows(row.DataItemIndex)("IsComplete") = (CType((row.Cells(3).Controls(0)), CheckBox)).Checked
'Reset the edit index.
TaskGridView.EditIndex = -1
'Bind data to the GridView control.
BindData()
End Sub
Private Sub BindData()
TaskGridView.DataSource = Session("TaskTable")
TaskGridView.DataBind()
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>GridView example</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="TaskGridView" runat="server"
AutoGenerateEditButton="True"
AllowPaging="true"
OnRowEditing="TaskGridView_RowEditing"
OnRowCancelingEdit="TaskGridView_RowCancelingEdit"
OnRowUpdating="TaskGridView_RowUpdating"
OnPageIndexChanging="TaskGridView_PageIndexChanging">
</asp:GridView>
</div>
</form>
</body>
</html>

viewState value is not shown after first postback

I'm new to asp.net (after programming for years in classic asp). I'm trying to build a page which adds something to a string.
My code is following:
default.aspx
<body>
<form id="form1" runat="server">
<div>
<p><asp:textbox id="tb" runat="server"></asp:textbox></p>
<asp:Panel ID="tbPanel" runat="server"></asp:Panel>
</div>
</form>
</body>
Code behind:
Partial Class demo_Default
Inherits System.Web.UI.Page
Public Property gesStr As String
Set(value As String)
ViewState("gesStr") = value
End Set
Get
Dim o As Object = ViewState("gesStr")
If o Is Nothing Then
Return ""
Else
Return o
End If
End Get
End Property
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
Dim anzeigeStr As String = ""
If Page.IsPostBack Then
Else
gesStr = "1;"
End If
tb.Text = gesStr
Dim iButton As New Button
iButton.Text = "add"
iButton.CommandArgument = "1;"
AddHandler iButton.Click, AddressOf add
tbPanel.Controls.Add(iButton)
Me.anzeige()
End Sub
Private Sub add(ByVal sender As Object, ByVal e As EventArgs)
Dim myButton As Button = DirectCast(sender, Button)
Dim addString As String = myButton.CommandArgument
gesStr += addString
End Sub
Private Sub anzeige()
Dim gesArray As Array = Split(gesStr, ";")
For xLauf As Integer = 0 To UBound(gesArray) - 1
Dim anzLabel As New Label
anzLabel.Text = "<p>" & gesArray(xLauf) & "</p>"
tbPanel.Controls.Add(anzLabel)
Next
End Sub
End Class
The problem:
Pressing the button will cause a postBack, but the result of adding won't appear until the button is pressed a second time. The desired result is that the sub displays the correct array within the loop after the first time pressing the button.
Thank you so much for any help!
You need to call the function anzeige() and bind gesStr value to the textbox control each time the button is click. See the code below:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Page.IsPostBack Then
Else
gesStr = "1;"
End If
tb.Text = gesStr
Dim iButton As New Button
iButton.Text = "add"
iButton.CommandArgument = "1;"
iButton.CommandName = "1;"
AddHandler iButton.Click, AddressOf add
tbPanel.Controls.Add(iButton)
End Sub
Private Sub add(ByVal sender As Object, ByVal e As EventArgs)
Dim myButton As Button = DirectCast(sender, Button)
Dim addString As String = myButton.CommandArgument
gesStr += addString
anzeige()
End Sub
Private Sub anzeige()
Dim gesArray As Array = Split(gesStr, ";")
For xLauf As Integer = 0 To UBound(gesArray) - 1
Dim anzLabel As New Label
anzLabel.Text = "<p>" & gesArray(xLauf) & "</p>"
tbPanel.Controls.Add(anzLabel)
Next
'Bind gesStr value to the textbox control
tb.Text = gesStr
End Sub

Resources