PagedDataSource not showing data in subsequent pages - asp.net

It's probably something small, but I haven't been figure out why the following isn't working completely. The first time the user submits the search, the first page of search results show up properly. However, when the user tries to navigate by using the Previous and Next Page buttons, the repeater doesn't show the appropriate data from the DataTable.
search.aspx
<asp:Repeater ID="rptSearchResults" runat="server" OnItemDataBound="rptSearchResults_ItemDataBound">
<ItemTemplate>
<div>
<asp:Literal ID="ltlCustName" runat="server" />
</div>
</ItemTemplate>
</asp:Repeater>
search.aspx.vb
Public dtCustomers As DataTable = New DataTable()
Sub Page_Load(ByVal Sender As Object, ByVal E As EventArgs) Handles Me.Load
' Check to see if this is the first time postback-ing to itself.
blnFirstPostBack = (Request.Form("firstpb") = "1")
' When the page first loads, show the search form with search options.
If Not Page.IsPostBack Then
Session("StoredDT") = Nothing
ShowSearchForm()
' Once the form is submitted, show the search results.
Else
Response.Write("DEBUG: Get Data")
If blnFirstPostBack Then
GatherFormData()
dtCustomers = BuildDT()
Session("StoredDT") = dtCustomers
BindData()
ElseIf Not Session("StoredDT") Is Nothing Then
dtCustomers = Session("StoredDT")
End If
End If
End Sub
Sub BindData()
Response.Write("DEBUG: Bind Data")
' At this point, I've checked that the dtCustomers is showing the proper data (e.g., grabbing from session when appropriate).
Dim objPDS As PagedDataSource = New PagedDataSource()
objPDS.DataSource = dtCustomers.DefaultView
objPDS.AllowPaging = True
objPDS.PageSize = intNumPerPage
' I've checked that Me.ViewState("_CurrentPage") gets updated to the correct page.
objPDS.CurrentPageIndex = Me.ViewState("_CurrentPage")
rptSearchResults.DataSource = objPDS
' I'm not sure if the error is here but although the dtCustomers is the proper data, the PagedDataSource and the binding here doesn't seem to play nice.
Call rptSearchResults.DataBind()
End Sub
' Subroutine called when the previous page button is pressed.
Sub GoToPrevPage()
Response.Write("DEBUG: Prev Page")
' Set viewstate variable to the previous page.
Me.ViewState("_CurrentPage") -= 1
BindData()
End Sub
' Subroutine called when the next page button is pressed.
Sub GoToNextPage()
Response.Write("DEBUG: Next Page")
' Set viewstate variable to the next page.
Me.ViewState("_CurrentPage") += 1
BindData()
End Sub
Protected Sub rptSearchResults_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles rptSearchResults.ItemDataBound
Dim item As RepeaterItem = e.Item
Dim strCustName As String = ""
If (item.ItemType = ListItemType.Item) Or (item.ItemType = ListItemType.AlternatingItem) Then
strCustName = e.Item.DataItem("CustName")
Dim ltlCustName As Literal = CType(e.Item.FindControl("ltlCustName"), Literal)
ltlCustName.Text = strCustName
End If
End Sub
Sample Page 1 (shows proper data):
DEBUG: Get Data
DEBUG: Bind Data
Laurence Clinton
John Doe
Sean King
Jane Smith
Sample Page 2 (does not show proper data but shows enough spaces for the missing data):
DEBUG: Get Data
DEBUG: Next Page
DEBUG: Bind Data
[no name showing here 5]
[no name showing here 6]
[no name showing here 7]
[no name showing here 8]
Please excuse the abbreviated code but the actual code is massive so the simplification is to make it easier to get to the heart of the issue.
Let me know if any of that was unclear or if more code is required. Thanks in advance!
Update 2/19/2013:
So after fiddling with the code a bit, I think the error happens in the repeater subroutine. The original literal inside of the repeater works now. For some reason I don't think it worked before but it works fine now. The problem is when we go a step further using a custom control inside of the repeater subroutine. It seems that the information doesn't get passed into the control. The control gets called properly because supporting HTML found only within the control gets outputted properly but the customer information that we try to pass into the control doesn't get inside. Here is the modified code:
search.aspx
<asp:Repeater ID="rptSearchResults" runat="server" OnItemDataBound="rptSearchResults_ItemDataBound">
<ItemTemplate>
<div>
<asp:Literal ID="ltlCustName" runat="server" />
<Acme:CustInfo ID="AcmeCustInfo" runat="server" />
</div>
</ItemTemplate>
</asp:Repeater>
search.aspx.vb
Protected Sub rptSearchResults_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles rptSearchResults.ItemDataBound
Dim item As RepeaterItem = e.Item
Dim strCustName As String = ""
If (item.ItemType = ListItemType.Item) Or (item.ItemType = ListItemType.AlternatingItem) Then
strCustName = e.Item.DataItem("CustName")
' This literal properly shows the customer name on subsequent pages so the customer name reaches this point properly.
Dim ltlCustName As Literal = CType(e.Item.FindControl("ltlCustName"), Literal)
ltlCustName.Text = strCustName
' This control gets called properly but strCustName doesn't
' get passed into the control. The control works fine for the
' first page but subsequent pages do not work. Also, the
' control works fine when PagedDataSource is not used.
Dim AcmeCustInfo As CustInfo = CType(e.Item.FindControl("AcmeCustInfo"), CustInfo)
AcmeCustInfo.CustName = strCustName
End If
End Sub
Per Request from Ann
Here is the code for CustInfo. Needless to say, a lot of the fluff has been stripped out to focus on the issue but if anything is missing that will be useful, please let me know and I'll update the example accordingly.
custinfo.ascx
<%# Control Language="VB" AutoEventWireup="false" CodeFile="CustInfo.ascx.vb" Inherits="CustInfo" %>
<div style="border: 1px solid black;">
<asp:Literal ID="ltlCustName" runat="server" />
</div>
custinfo.ascx.vb
Imports System
Imports System.Web
Imports System.Web.Security
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Web.UI.HtmlControls
Imports System.Configuration
Imports System.Data
Imports System.Data.SqlClient
Imports System.IO
Imports System.Web.HttpUtility
Partial Class CustInfo
Inherits System.Web.UI.UserControl
Private _CustName As String = ""
Public Property CustName() As String
Get
Return _CustName
End Get
Set(value As String)
_CustName = value
End Set
End Property
Public Sub Page_Load(ByVal Sender As Object, ByVal E As EventArgs) Handles Me.Load
Dim ltlCustName As Literal = CType(FindControl("ltlCustName"), Literal)
ltlCustName.Text = "<b>Name: " & CustName & "</b>"
End Sub
End Class

I'm not a PagedDataSource expert, but I've used user controls extensively.
You're setting the value of the literal in your CustInfo control in the Load subroutine. If I'm not mistaken, that's going to get called when the CustInfo control is created. That would happen before OnItemDataBound is called, so -- I think -- the Load subroutine will happen and the value of the literal be set (to an empty string) before you assign the value of the CustName property. To fix this, try setting the value of the literal later, such as in the PreRender subroutine.
It looks like you're binding on every postback, so the following shouldn't be an issue, but if you aren't, here are some thing that might affect you:
ItemDataBound only runs if you re-bind. If you have ViewState turned on for your repeater, the controls will be recreated on postback, and ItemCreated would run, but ItemDataBound wouldn't. So you'd never re-set your CustName property. And this would matter because ...
... your CustName property has a variable as a backing store: it isn't preserved in ViewState. So when the user control is recreated, the CustName property you previously set won't be there anymore: your literal's contents will be set to the empty string that is the default value of the CustName property.
Those two together would give exactly the behavior you've described. But since it looks like you do bind on every postback (eventually) that may not be the issue. I'm including it for completeness.

Related

How to htmlencode body but not heading of gridview

I have a GridView. I want the content of the cells, the actual detail data, to be html-encoded. But I want to keep the raw HTML in the heading.
That is, the heading in this case is the name of a language and the ISO code, for example "English (EN)". I want the name and code on separate lines, so I'm specifying the heading as "English<br/>(EN)".
But for the content, I want to see any HTML. If it says "<p>foobar</p>" I want the p-tags to display.
If I say "htmlencode=false", then the heading doesn't get encoded, but neither does the data.
Is there any way to say, html-encode the data, but not the heading?
If it matters, I'm building the columns in code rather than with tags in the ASP file, depending on which languages I find in my data. So here's how I'm creating the column:
For Each row As DataRow In ds.Tables(0).Rows
Dim iso2 = row("language")
Dim name = row("name")
... other code ...
Dim head = String.Format("{0}<br/>({1})", name, iso2)
gvSnippets.Columns.Add(New BoundField With {.HeaderText = head, .DataField = iso2, .HtmlEncode = False})
... other code ...
End For
(My first draft did not set HtmlEncode.)
Curious observation: In my first couple of test runs, the data did not include any HTML or entities, and the HTML in the heading was NOT encoded, I got the line break and not "<br/>". Then I did a test where there were entities in the data and the entities got html-encoded ... and so did the header. So like, ASP is apparently saying that by default, if the data has no HTML but the heading does, then don't HTML-encode the heading. But if the data has HTML, then HTML-encode both the data and the heading. So like, it's deciding dynamically whether to html-encode the heading or not.
In reply to #fnostro, here's the markup for the GridView:
<asp:GridView ID="gvSnippets" runat="server" AutoGenerateColumns="False" SkinID="skin3" EmptyDataText="No records found" Visible="false">
</asp:GridView>
There is no <Columns> markup. I build the columns completely in code. I haven't tested if the same behavior occurs in what I assume is the more normal case where you specify columns with markup.
Add these lines of code to the beginning of your loop
row("language") = HttpUtility.HtmlEncode(dr("language"))
row("name") = HttpUtility.HtmlEncode(dr("name"))
Based on my reading of the information provided in your post I have the following suggestions:
You should isolate data formatting from raw data.
Your DataTable is the data source for the GridView. The DataTable should contain only raw data, completely unformatted and unadulterated.
The DataTable gets bound to the Gridview by setting the GridView DataSource and making a call to DataBind(). Calling gvSnippets.Databind() will trigger all the databinding events associated with Gridviews which will allow you to:
Manage simple formatting and binding in the GridView Columns in the aspx markup.
Handle more complicated formatting in the code behind for specific GridView events like the RowDataBound event.
Regarding the GridView Header: HeaderText appears once at the top of the Gridview and is stored internally in GridView.HeaderRow, not on a per row basis even though every bound field has a HeaderText property.
Example excerpt gvSnippets Markup per your update:
<asp:GridView ID="gvSnippets" runat="server"
AutoGenerateColumns="False" SkinID="skin3"
EmptyDataText="No records found" Visible="false">
</asp:GridView>
Then you can do things like this in the Code Behind:
Public Class __JunkWebForm1
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
' Fill Dataset first, then:
gvSnippets.DataSource = ds.Tables(0)
' Add Columns...
gvSnippets.Columns.Add(...)
gvSnippets.Columns.Add(...)
gvSnippets.Columns.Add(...)
. . .
' This will trigger data binding events
gvSnippets.DataBind();
End Sub
Private Property HeaderTextLanguage As String
Private Property HeaderTextLanguageIso As String
Dim NeedHeaderText As Boolean = False
Private Sub gvSnippets_DataBinding(sender As Object, e As EventArgs) Handles gvSnippets.DataBinding
NeedHeaderText = True
End Sub
Private Sub gvSnippets_DataBound(sender As Object, e As EventArgs) Handles gvSnippets.DataBound
Dim this As GridView = sender
this.Columns(0).HeaderText = String.Format("{0}<br>({1})", HeaderTextLanguage, HeaderTextLanguageIso)
End Sub
Private Sub gvSnippets_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles gvSnippets.RowDataBound
Dim this As GridView = sender
Dim drv As DataRowView = e.Row.DataItem
If e.Row.RowType = DataControlRowType.DataRow Then
If NeedHeaderText Then
HeaderTextLanguage = drv("language")
HeaderTextLanguageIso = drv("iso")
NeedHeaderText = False
End If
e.Row.Cells(0).Text = Server.HtmlEncode(drv("Somefieldnameofyours"))
End If
End Sub
End Class
Addendum Dealing with Dynamic cells
For a GridView defined as:
<asp:GridView ID="gvSnippets" runat="server"
AutoGenerateColumns="False">
</asp:GridView>
I don't know how you're populating your ds but I'm using a SqlDataSource to fill a table in the Code Behind. Note there is no GridView DataSourceID above:
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
SelectCommand="select top 10 user_id, user_name, user_logon, 'English' as language, 'en' as iso from tbl_users"
ConnectionString='<%$ ConnectionStrings:MyConnectionString %>'>
</asp:SqlDataSource>
Then in the Code Behind I can do this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
GetDataSetAndPopulate(gvSnippets)
End Sub
Private Sub GetDataSetAndPopulate(gv As GridView)
Dim view As DataView = SqlDataSource1.Select(DataSourceSelectArguments.Empty)
Dim table As DataTable = view.ToTable()
Dim ds As DataSet = New DataSet()
ds.Tables.Add(table)
For Each dc As DataColumn In ds.Tables(0).Columns
Dim field As New BoundField
field.DataField = dc.ColumnName
field.HeaderText = dc.ColumnName
gv.Columns.Add(field)
Next
gv.DataSource = ds
gv.DataBind()
End Sub
And now the Gridview is populated dynamically and you can still handle all the formatting during the DataBinding events as needed.
Here's a solution I came up with that works in my particular case.
I am building the data to populate the GridView in code. I create a DataTable, add columns to it, and then populate those columns. Like:
dim dslang=GetListOfLanguages()
dim dtgv=new DataTable()
for each row as DataRow in dslang.tables(0).rows
dim language_name=row("name")
dim language_code=row("code")
dtgv.columns.add(String.format("{0}<br/>({1})",language_name, language_code)
end for
... bunch of other stuff ...
... inside a loop that reads the data ...
dim outrow=dtgv.NewRow()
... get data for a specific language ...
outrow(language_code)=text
... later ...
my_gridview.datasource=dtgv
my_gridview.databind()
So my solution:
When creating the GridView, set HtmlEncode=false. This makes the header display correctly.
When populating the data, say
outrow(language_code)=HttpUtility.HtmlEncode(text)
So I add the data to the datatable that will ultimately be used as the datasource for the gridview already encoded, so I don't need to rely on the Gridview encoding it.
I consider this a mediocre solution, as it only works because I am populating GridView from a DataTable that I build with code. If I was populating the GridView directly from a DataSource specified in markup, or from a DataSet created by a database query, there'd be no convenient place to put the HttpUtility.HtmlEncode call.

VB.NET Obj Ref not set in datetime

I've seen similar questions through extensive 'google-ing' but none have been able to sort this issue out, the error I'm getting from VS13 is below:
An exception of type 'System.NullReferenceException' occurred in
my.dll but was not handled in user code. Additional information:
Object reference not set to an instance of an object.
Simply put I want a default date value to populate within the calendar textbox so the user doesn't have to choose a date, or if they forget etc it'll put the system time in.
I've tried converting toString etc and I get the same error.
Any help would be gratefully received.
Thanks in advance.
Code behind:
Public Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
Dim SrtDateTxt As New TextBox
SrtDateTxt = ListView1.FindControl("StartDateTextBox")
SrtDateTxt.Text = DateTime.Now
End If
End Sub
And the aspx that the above code behind relates to:
<asp:TextBox ID="StartDateTextBox" runat="server" Text='<%# Bind("StartDate") %>' Width="150px"/>
<act:CalendarExtender ID="CalendarExtender1" runat="server" TargetControlID="StartDateTextBox" Format="yyyy-MM-dd"></act:CalendarExtender>
First i want to suggest to change Option Strict to "On" in your project's settings. That won't you prevent from compiler errors but from nasty runtime errors. Fixing the compiler errors will teach you much about the types in the .NET framework.
Then for example this will not compile anymore:
SrtDateTxt.Text = DateTime.Now
You need to assign a string instead of a Date:
SrtDateTxt.Text = DateTime.Now.ToString() ' or ToShortDateString()
Next, this will not prevent you from a NullRefernceException:
Dim SrtDateTxt As New TextBox
If you assign another value to this variable one line afterwards:
SrtDateTxt = ListView1.FindControl("StartDateTextBox")
So i assume that SrtDateTxt is Nothing because there is no control with that ID in the ListView but in one of it's ListViewItems(which are different NamingContainers).
Therefore you get the exception at the next line when you try to access a property
SrtDateTxt.Text = DateTime.Now ' at .Text
A ListView is meant to be used for multiple items. So you need to use the correct ListViewItem.
For Each item As ListViewItem In ListView1.Items
Dim StartDateTextBox = DirectCast(item.FindControl("StartDateTextBox"), TextBox)
' ... '
Next
This loops all items, i don't know if that is what you want to do.

Filling the dropdown from sql bug

Good afternoon people have been trying to fill a dropdown using a sql command, until so good, when I click on the dropdown it shows all the items, but when I try to click on an item in the dropdown it always returns the first item in the dropdown .... follows the codes, what i want to do is get the selected value and item from the dropdown and save it on a label for future use.
I appreciate all the support possible,
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
utilizador.Text = Me.Context.User.Identity.Name
If (Not Page.IsPostBack) Then
escolhePerfil()
End If
'DropDownPerfil.DataBind()
lbperfil2.Text = DropDownPerfil.SelectedItem.Text
lbnome.Text = DropDownPerfil.SelectedValue
End Sub
Function escolhePerfil() As Boolean
Dim connstring As String = "Data Source=10.2.24.17;Persist Security Info=True;User ID=sa;Password=Pr0dUn1C0$qL;database=ePrimavera"
Dim SQLData As New System.Data.SqlClient.SqlConnection(connstring)
Dim cmdSelect As New System.Data.SqlClient.SqlCommand("SELECT u.WindowsUser,u.Email ,g.Description, u.Login FROM [ePrimavera].[dbo].[PLT_Users] as u,[ePrimavera].[dbo].[PLT_UserGroups] as ug, [ePrimavera].[dbo].[PLT_Groups] as g where u.ID = ug.UserID And ug.GroupID = g.ID and u.WindowsUser like 'bancounico\" & utilizador.Text & "'", SQLData)
SQLData.Open()
Dim dtrReader As System.Data.SqlClient.SqlDataReader = cmdSelect.ExecuteReader()
If dtrReader.HasRows Then
DropDownPerfil.DataValueField = "Login"
DropDownPerfil.DataTextField = "Description"
DropDownPerfil.DataSource = dtrReader
DropDownPerfil.DataBind()
End If
SQLData.Close()
Return True
End Function
.aspx
<asp:DropDownList ID="DropDownPerfil" runat="server"
Height="16px" Width="202px" CssClass="DropBorderColor">
</asp:DropDownList>
try the following code:
Protected Sub DropDownPerfil_SelectedIndexChanged(sender As Object, e As EventArgs)
lbperfil2.Text = DropDownPerfil.SelectedItem.Text
lbnome.Text = DropDownPerfil.SelectedValue
End Sub
The problem is that you are trying to get the value "too early". The value is not valid in the Page_Load, since the control's OnLoad event fired after the Page's OnLoad (Page_Load) event.
The way, what the others wrote the correct, since the Event handlig section (inclueding control's onchanged event) will process after the Load section.
For more details check the offical ASP.NET life cycle site in the MSDN
The likely reason is that some (if not all) the values you are assigning to the DropDownList for Login are occur more than once.
When you then select an item in the DDL, if the value of that item occurs more than once, the selected index will highlight the first instance. To test this, comment out DropDownPerfil.DataValueField = "Login"
I am sure upon selection, it will highlight the correct item you intended on selecting.

asp.net: Attributes.Add is to save - and how do i read them?

I'm adding in asp.net (vb) an attribute to an image-button:
imgButton.Attributes.Add("myAttr", "true")
This is working fine.
Now i want to read the attribute, but it does not work:
If imgButton.Attributes("myAttr") = "true" Then
..doSomething..
How do I get this thing working?
Edit
I have an asp.net repeater.
In this repeater i have in each itemtemplate two image buttons.
If I'm clicking on an imagebutton, the other imagebutton in this row changes it's URL.
I want that this URL is saved, after clicking on another row's imagebutton.
So I'm adding in the repeater event
ItemCommand
Dim imgButton As ImageButton
If e.CommandName = "imgBtn1" Then
imgButton = CType(e.Item.FindControl("imgBtn1"), ImageButton)
imgButton.ImageUrl = "myURL"
imgButton.Attributes.Add("myAttr1", "true")
ElseIf e.CommandName = "imgBtn2" Then
imgButton = CType(e.Item.FindControl("imgBtn2"), ImageButton)
imgButton.ImageUrl = "myURL"
imgButton.Attributes.Add("myAttr2", "true")
End If
In the Page Load event I'm adding:
If Page.IsPostBack Then
Dim countRepeaterItems As Integer = myRepeater.Items.Count
Dim imgButton As ImageButton
For i As Integer = 0 To countRepeaterItems - 1
imgButton = CType(myRepeater.Items(i).FindControl("imgBtn1"), ImageButton)
If imgButton.Attributes.Item("myAttr1") = "true" Then
imgButton.ImageUrl = "myURL"
End If
imgButton = CType(myRepeater.Items(i).FindControl("imgBtn2"), ImageButton)
If imgButton.Attributes.Item("myAttr2") = "true" Then
imgButton.ImageUrl = "myURL"
End If
Next
End If
While debugging, it still skips everything, because all Attributes are empty (but actually they are not)!
Looks like for VB it should be:
If imgButton.Attributes.Item("myAttr") = "true" Then
EDIT: original answer was for C#:
Should be square brackets on the reads:
If imgButton.Attributes["myAttr"] = "true" Then
http://msdn.microsoft.com/en-us/library/kkeesb2c.aspx#Y0
I guess! May be you have (turn off the viewstate) set EnableViewState=False to the imgButton.
Take a look at this sample:
Markup:
<form id="form1" runat="server">
<asp:Button ID="Button1" runat="server" Text="Button" />
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
</form>
Code:
Protected Sub Button1_Click(sender As Object, e As System.EventArgs) Handles Button1.Click
If Label1.Attributes("myAttr") = "true" Then
Label1.Attributes.Add("myAttr", "false")
Label1.Text = "false is set"
Else
Label1.Attributes.Add("myAttr", "true")
Label1.Text = "true is set"
End If
End Sub
Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
Label1.Attributes.Add("myAttr", "true")
End If
End Sub
EDIT:
May be typo in ItemCommand event code.
imgButton.ImageUrl = "myURL"
imgButton.Attributes.Add("myAttr1", "true") '<----- Correction
Addressing the issue of not finding the attributes set in your page load.
I am not entirely clear on what you are trying to accomplish, but it seems likely that you are running into a problem with the order that events execute. On a post back, the page load event executes, then the event handler for the control causing the post back. If your image button causes the post back, then the attribute is not being set until after the page load event has already completed. However, assuming you are using viewstate, your attribute should be persisted and detected on the following post back.
If this is what is happening to you, then one possible way around it would be to move the code from your page load event to the page pre render event. Pre render occurs after the control's post back events have been handled, and it is one of the last points where you can hook in and make changes to your content before it is committed to viewstate.

ASP.NET bug in event linking of 2nd level+ dynamic controls. Test case included

The Repeater control in the following test case contains two runat="server" DIVs. Each one of them gets a TextBox appened to them through the ItemCreated event of the Repeater. Both of the have AutoPostBack=True, and TextChanged event wired to txt_TextChanged. However, only the TextBox from the first level properly points to the event on the postBack of the page. The second level TextBox also causes the postBack to occur, but its value does not persist in the VIEWSTATE as well as the event does not fire.
Here's a direct link to the test case in a .zip file (uploaded to my personal website), as well as all the code needed. The project is built in VS2010 using .NET Framework 4, but the problem also exists in 1.1, 2, and 3.5.
Anybody has any ideas on what is wrong and how to make this work?
ASPX
<asp:Repeater ID="rep" runat="server">
<ItemTemplate>
<!-- first level works -->
<div id="divOutside" runat="server">
<!-- second level doesn't work -->
<div id="divInside" runat="server"></div>
</div>
</ItemTemplate>
</asp:Repeater>
Code-Behind
Public Class WebForm1
Inherits System.Web.UI.Page
Private Sub WebForm1_Init(sender As Object, e As System.EventArgs) Handles Me.Init
If Not IsPostBack Then
Dim Table As New DataTable()
Table.Columns.Add("Column1")
Dim Row As DataRow = Table.NewRow()
Row("Column1") = ""
Table.Rows.Add(Row)
rep.DataSource = Table
rep.DataBind()
End If
End Sub
Private Sub repTest_ItemCreated(sender As Object, e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles rep.ItemCreated
' outside
Dim divOutside As HtmlGenericControl = DirectCast(e.Item.FindControl("divOutside"), HtmlGenericControl)
Dim txtInput As New TextBox
With txtInput
.ID = "txtInputOutside"
.AutoPostBack = True
.Text = "Event gets called, value persists accross postBack."
.Width = 400
End With
AddHandler txtInput.TextChanged, AddressOf txt_TextChanged
divOutside.Controls.Add(txtInput)
' inside
Dim divInside As HtmlGenericControl = DirectCast(e.Item.FindControl("divInside"), HtmlGenericControl)
txtInput = New TextBox
With txtInput
.ID = "txtInputInside"
.AutoPostBack = True
.Text = "Event NOT called, value is lost during postBack."
.Width = 400
End With
AddHandler txtInput.TextChanged, AddressOf txt_TextChanged
divInside.Controls.Add(txtInput)
End Sub
Protected Sub txt_TextChanged(sender As Object, e As EventArgs)
End Sub
End Class
Is there any reason why the textboxes have to be added to the divs dynamically? why not just put them in the aspx page with the repeater then on itemdatabound enable/disable or do whatever you need to do. That should work fine.
By the way, if you were to use panels instead of divs your on the fly approach will work.
Here's a link to Microsoft Connect, where I reported this is an official bug:
https://connect.microsoft.com/VisualStudio/feedback/details/652655/asp-net-bug-in-event-linking-of-2nd-level-dynamic-controls
It contains 2 PARTIAL workarounds that work in the uploaded files section, if anybody is interested or encounters the same problem, as well as details to what I found in the Workarounds tab.
To keep it short, it is the order of getting references to the containers and the order the TextBoxes are appended to their appropriate containers which either causes the issue or works as expected.
But, not to forget the most important point, the only reason the first-level TextBox is there in the first place is to showcase what I want as functionality. If the first-level TextBox does not get appended at all, then both workarounds fail to provide any sort of fix. Please keep that in mind when reviewing that problem - finding a workaround that revolves around changing the order of the items is not an actual solution or a fully working workaround!
Thanks everyone for the input!

Resources