Refresh ListBox control during a loop - asp.net

Using ASP.NET and VB.NET code behind, I have the following code:
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim I As Integer = 0
For I = 0 To 10
ListBox1.Items.Add(I)
ListBox1.DataBind()
System.Threading.Thread.Sleep(300)
Next
End Sub
The intended output of the code is to update the listbox1 control at each iteration, but what really happens is it updates the listbox1 control after the entire loop finishes..
Is there a way to update the listbox1 control as its intended by the code logic?

You need to place the ListBox inside UpdatePanel and trigger the Button1_Click event as as Async; Something like this:
<asp:UpdatePanel runat="server" ID="pnlUpdate">
<ContentTemplate>
<asp:ListBox ID="ListBox1" runat="server"></asp:ListBox>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
</Triggers>
</asp:UpdatePanel>

This should work accordingly:
Dim I As Integer = 0
For I = 0 To 10
ListBox1.Items.Add(I)
Next
If you debug then you'll see with each iteration an item is added to Items of ListBox1 control but the effect only be visible if the page/form is loaded.

Related

VB.Net "logger" asp:Table and asp:UpdatePanel dynamic asynchronous update

I am writing a web page in ASP.Net. Presently, I have an asp:Table that I am using as a sort of "log" for processing output. The idea is that the user selects several files and clicks a button, and each file is "processed" with the log showing what is happening. Processing occurs asynchronously.
Here is the relevant processing segment:
Protected Sub DoAsyncWork()
Dim count = 0
For Each row As GridViewRow In gvList.Rows
count = count + 1
If CType(row.FindControl("cbImport"), System.Web.UI.WebControls.CheckBox).Checked Then
push_to_log("")
push_to_log("Updating Active Projects +" + HttpUtility.HtmlDecode(row.Cells(1).Text).ToString.Substring(0, 30) + "...")
Dim xp(3) As Object
xp(0) = HttpUtility.HtmlDecode(row.Cells(0).Text)
xp(1) = HttpUtility.HtmlDecode(row.Cells(1).Text)
xp(2) = HttpUtility.HtmlDecode(row.Cells(2).Text)
xp(3) = 0
'oDC.UpdateData("Import_P3e_Project ", xp)
If (xp(3) <> 0) Then
push_to_log("Success: " + xp(3).ToString + " have been updated")
Else
push_to_log("Failure: " + xp(3).ToString + " activities updated")
End If
End If
Next
push_to_log("")
push_to_log("Import Complete!")
End Sub
This is how I am calling the process worker function:
Protected Sub button_Import(sender As Object, e As EventArgs) Handles btnImport.Click
Dim t As New Thread(New ThreadStart(AddressOf DoAsyncWork))
t.Priority = Threading.ThreadPriority.Normal
t.Start()
push_to_log("Start Import")
End Sub
The way I am appending things to the log is by dynamically creating rows and cells, then adding them to my table. Here is the relevant subroutine:
Protected Sub push_to_log(ByVal str As String)
Dim newRow As TableRow = New TableRow
Dim newCell As TableCell = New TableCell
logArrayList.Add(str)
Me.ViewState.Add("arrayListInViewState", logArrayList)
newCell.Text = str
newCell.Style("Color") = "White"
newCell.ID = "cell" + (logArrayList.Count - 1).ToString
newRow.ID = "row" + (logArrayList.Count - 1).ToString
newRow.Cells.Add(newCell)
logTable.Rows.Add(newRow)
HiddenButton_Click(HiddenButton, New EventArgs())
'UpdateLogPanel.Update()
'UpdateLogPanel.Focus()
End Sub
I've got the log persisting correctly by using the ViewState to store my arraylist of data and recreating the log on postbacks. The relevant markup for my log looks like this:
<asp:UpdatePanel ID="UpdateLogPanel" UpdateMode="Conditional" runat="server">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="HiddenButton" />
</Triggers>
<ContentTemplate>
<div ID="Div1" class="DefinitionPanel" style="text-align:left;height:200px;overflow:hidden;" runat="server">
<span style="display:inline-block; width:100px;"></span>
<div class="scrollingtable">
<div>
<div id="viewContainer">
<asp:table id="logTable" runat="server" enableviewstate="false">
</asp:table>
</div>
</div>
</div>
</div>
<asp:Button ID="HiddenButton" runat="server" style="display:none;" />
</ContentTemplate>
</asp:UpdatePanel>
I am trying to make my asp:Table update every time a message is posted to it. I thought that enabling partial postbacks and using an UpdatePanel would be the correct solution, but my log still does not output anything until the entire process has completed.
After I add a message to my asp:Table/log, I tried calling
UpdateLogPanel.Update()
which didn't seem to make a difference. Finally I tried adding the asp:AsyncPostBackTrigger and hidden button with the hope that it would fix things but it doesn't seem to. Here is what the hiddenButton event looks like:
Protected Sub HiddenButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles HiddenButton.Click
UpdateLogPanel.Visible = True
End Sub
Any guidance on how to make my log re-render itself when I add a message to it would be highly regarded.
I chose to implement using pooling instead of an asynchronous method.
I still use an asp:UpdatePanel. Instead of a table inside of the UpdatePanel, I am now using a gridview, and a timer. When the timer fires, I rebind the gridview, thereby showing any new contents
The key components to making this work:
Square things away with the parent update panel and master page:
Private Sub Page_Init(sender As Object, e As System.EventArgs) Handles Me.Init
'set reference to master site page
mstr = CType(Master, Site)
'setup partial rendering so Log can update asynchronously
scriptManager = CType(mstr.FindControl("ScriptManager1"), ScriptManager)
scriptManager.EnablePartialRendering = True
scriptManager.AsyncPostBackTimeout = 28800
CType(mstr.FindControl("UpdatePanel1"), UpdatePanel).UpdateMode = UpdatePanelUpdateMode.Conditional
CType(mstr.FindControl("UpdatePanel1"), UpdatePanel).ChildrenAsTriggers = False
End Sub
The mark-up for the UpdatePanel looks like
<asp:UpdatePanel ID="UpdateLogPanel" UpdateMode="Conditional"
RenderMode="Inline" ChildrenAsTriggers="false" runat="server">
<ContentTemplate>
<%--The Gridview and other Hidden Fields--%>
<asp:Timer ID="myTimer" OnTick="timer_tick" runat="server" Interval="1000" Enabled="false"/>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="myTimer" EventName="Tick" />
</Triggers>
</asp:UpdatePanel>
Triggering the logging mechanism is done simply by setting
myTimer.Enabled = True
The timer_tick event looks like
Public Sub timer_tick(ByVal sender As Object, ByVal e As EventArgs)
generate_log()
//other logging logic (increment counters, "timeout" mechanism)
UpdateLogPanel.Update()
End Sub

Why won't my UpdatePanel update my Listbox as I expect on button click?

I have a form with a dropdownlist, two buttons, and two Listboxes inside an UpdatePanel. The Dropdownlist, and listboxes are all bound to SqlDatasources. The dropdownlist allows you to choose your department.
The first listbox shows a list of Jobs associated with what you've selected from the department.
The second listbox shows an inverse list of those items. (Jobs in the database that are not associated with your department)
When an item is removed from the 1st listbox, it should show up in the 2nd listbox. When an item is removed from the 2nd listbox, it should show up in the 1st listbox.
This functionality allows you to add and remove jobs from your department
The are two buttons on the page function as Add and Remove buttons. Everything is working except the Listboxes will not reliably update. The Data itself is updated in the database, and if I refresh (F5) it will show correctly.
<asp:ScriptManager ID="smgrDeptsJobs" runat="server"></asp:ScriptManager>
<asp:UpdatePanel ID="uPanelDeptsJobs" runat="server">
<ContentTemplate>
<asp:DropDownList ID="ddlDepartments" runat="server"
DataSourceID="sqldsDepartments" DataTextField="Department"
DataValueField="DeptID" Width="150px" AutoPostBack="True">
</asp:DropDownList>
<asp:ListBox ID="lstJobsIn" runat="server" DataSourceID="sqldsJobsIn"
DataTextField="JobName" DataValueField="JobID" height="156px"
width="220px">
</asp:ListBox>
<asp:Button ID="btnAddJob" runat="server" Text="<<" Width="70px"
CausesValidation="False" />
<asp:Button ID="btnRemoveJob" runat="server" Text=">>" Width="70px"
CausesValidation="False" />
<asp:ListBox ID="lstJobsOut" runat="server" DataSourceID="sqldsJobsOut"
DataTextField="JobName" DataValueField="JobID" height="156px"
width="220px">
</asp:ListBox>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="ddlDepartments"
EventName="SelectedIndexChanged" />
<asp:AsyncPostBackTrigger ControlID="btnAddJob" EventName="Click" />
<asp:AsyncPostBackTrigger ControlID="btnRemoveJob" EventName="Click" />
</Triggers>
</asp:UpdatePanel>
The code for the two button click events is below:
Protected Sub btnAddJob_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAddJob.Click
Dim sqlJobsDB As New SqlConnection(ConfigurationManager.ConnectionStrings("JobsDB").ConnectionString)
Dim sqlCmdInsert As SqlCommand = sqlJobsDB.CreateCommand()
sqlJobsDB.Open()
sqlCmdInsert.CommandText = _
"INSERT INTO tblDeptsJobs (DeptID, JobID) VALUES " + _
"(#DeptID, #JobID)"
' Declare the data types for the parameters
sqlCmdInsert.Parameters.Add("#DeptID", SqlDbType.TinyInt)
sqlCmdInsert.Parameters.Add("#JobID", SqlDbType.TinyInt)
' Assign the parameters values from the form
sqlCmdInsert.Parameters("#DeptID").Value = ddlDepartments.SelectedValue
sqlCmdInsert.Parameters("#JobID").Value = lstJobsOut.SelectedValue
' Execute the insert Statement
sqlCmdInsert.ExecuteNonQuery()
sqlJobsDB.Close()
End Sub
Protected Sub btnRemoveJob_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnRemoveJob.Click
Dim sqlJobsDB As New SqlConnection(ConfigurationManager.ConnectionStrings("JobsDB").ConnectionString)
Dim sqlCmdDelete As SqlCommand = sqlJobsDB.CreateCommand()
sqlJobsDB.Open()
sqlCmdDelete.CommandText = _
"DELETE FROM tblDeptsJobs WHERE tblDeptsJobs.DeptID = #DeptID AND tblDeptsJobs.JobID = #JobID"
' Declare the data types for the parameters
sqlCmdDelete.Parameters.Add("#DeptID", SqlDbType.TinyInt)
sqlCmdDelete.Parameters.Add("#JobID", SqlDbType.TinyInt)
' Assign the parameters values from the form
sqlCmdDelete.Parameters("#DeptID").Value = ddlDepartments.SelectedValue
sqlCmdDelete.Parameters("#JobID").Value = lstJobsIn.SelectedValue
' Execute the insert Statement
sqlCmdDelete.ExecuteNonQuery()
sqlJobsDB.Close()
End Sub
It feels like when I add or remove a job, the listbox that I last selected an item in, is the one that doesn't update.
I also can't get the dropdownlist to update the listboxes without setting autopostback on the dropdownlist to True.
The ugly Band-Aid fix I've come up with is using the listbox.items.clear() method and then rebinding the data for each listbox.
Basically what is happening is that you update your database but never rebind your controls. I'm not sure exactly what you will have to put into your click handlers to make this work (because I have never used the SQL datasource controls before), but it should look something like this:
Protected Sub btnAddJob_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAddJob.Click
Dim sqlJobsDB As New SqlConnection(ConfigurationManager.ConnectionStrings("JobsDB").ConnectionString)
Dim sqlCmdInsert As SqlCommand = sqlJobsDB.CreateCommand()
sqlJobsDB.Open()
sqlCmdInsert.CommandText = _
"INSERT INTO tblDeptsJobs (DeptID, JobID) VALUES " + _
"(#DeptID, #JobID)"
' Declare the data types for the parameters
sqlCmdInsert.Parameters.Add("#DeptID", SqlDbType.TinyInt)
sqlCmdInsert.Parameters.Add("#JobID", SqlDbType.TinyInt)
' Assign the parameters values from the form
sqlCmdInsert.Parameters("#DeptID").Value = ddlDepartments.SelectedValue
sqlCmdInsert.Parameters("#JobID").Value = lstJobsOut.SelectedValue
' Execute the insert Statement
sqlCmdInsert.ExecuteNonQuery()
sqlJobsDB.Close()
//may need to do explicit call to DB to get data here
//after you have the data, rebind
lstJobsIn.DataBind();
lstJobsOut.DataBind();
End Sub
That's roughly what it will look like. I would be interested to see what exactly you do to solve your problem.
Just set dropdownlist autopostback to true, remove all triggers and set ChildrenAsTriggers="true" on the updatepanel.

ModalPopupExtender not displaying on button postback within updatepanel

I know this flavor of question has been asked multiple times, but I've spent hours sifting through answers that don't match or don't work and I'm at wits end.
Background: I have a situation where I want to evaluate a record to make sure it fits a specific set of criteria. If it fits the criteria, raise an alert message for confirmation from the user. I do not want to raise the popup unless the criteria matches.
Pseudocode of what I want to accomplish:
User enters information in multiple fields
User clicks "Save" (cmdUpdate)
within the "Save" click function it checks to see if the same record already exists
in the database (e.g. this is a duplicate).
If it's not a duplicate continue with the save function
If it's a duplicate prompt the user for confirmation to save the dup.
I can't get the popup to display before/after the postback. I've tried a hack workaround of setting a session value to maintain the state. The value tests positive in the prerender and does call the modalpopupextender.show but it never fires successfully to the screen. I'm not opposed to switching to a javascript solution if someone has a better method but I have to do the check for duplicates in the asp.net code behind.
Markup:
<asp:UpdatePanel ID="upMainContent" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="False" >
<ContentTemplate>
<asp:Label ID="lblDummy" runat="server" EnableViewState="false" Style="display: none;" />
<asp:Panel ID="pnlConfirmation" runat="server" Width="400px" Style="display: none;" CssClass="ModalPopupFront">
<div ID="Div1" runat="server" class="PopupHeader" style="width:400px;"> Duplicate Record</div>
<br />
<asp:Label ID="lblConfirmationMessage" runat="server" Text="This record has already exists.<br/> Are you sure you want to save a duplicate entry?"></asp:Label><br />
<br />
<div style="text-align:right;">
<asp:Button ID="btnSaveAnyway" runat="server" Text="Save" OnClick="btnSaveAnyway_Click" />
<asp:Button ID="btnCancelSave" runat="server" Text="Cancel" OnClick="btnCancelSave_Click" />
</div>
</asp:Panel>
<ajax:ModalPopupExtender ID="mpeSaveConfirmation" runat="server" Enabled="False"
TargetControlID="lblDummy" PopupControlID="pnlConfirmation"
BackgroundCssClass="modalBackground" DropShadow="true"
CancelControlID="btnCancelSave"
RepositionMode="RepositionOnWindowResizeAndScroll" PopupDragHandleControlID="pnlConfirmation" Drag="true">
</ajax:ModalPopupExtender>
<!-- all the input fields/misc content -->
<asp:Button id="cmdUpdate" runat="server" CausesValidation="true" OnClick="cmdUpdate_Click" Text="Save" ValidationGroup="vg1" ToolTip="Save the current record" TabIndex="102" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="ddlStateId" EventName="SelectedIndexChanged" />
<asp:AsyncPostBackTrigger ControlID="ddlCountyId" EventName="SelectedIndexChanged" />
</Triggers>
</asp:UpdatePanel>
Code behind:
Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
'...
If GetSessionValue("HackWorkaround") Then
mpeSaveConfirmation.Enabled = True
mpeSaveConfirmation.Show()
End If
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'...
If not Page.IsPostBack Then
SetSessionValue("HackWorkaround", False)
End if
'...
End Sub
Protected Sub cmdUpdate_Click(ByVal sender As Object, ByVal e As System.EventArgs)
If tbOpTill.NewRecordIdenticalToLast() And tbOpRecord.NewRecordIdenticalToLast() Then
SetSessionValue("HackWorkaround", True)
Else
SetSessionValue("HackWorkaround", False)
SetSessionValue("LastOpRecordIDSaved", tbOpRecord.OpRecordId)
Dim isEdit As Boolean = ResetOpRecord("Till", tbOpTill)
SmartRedirect("Optill/oprecord_edit.aspx")
End If
End Sub
Protected Sub btnSaveAnyway_Click(ByVal sender As Object, ByVal e As System.EventArgs)
SetSessionValue("HackWorkaround", False)
mpeSaveConfirmation.Enabled = False
mpeSaveConfirmation.Hide()
'Duplicate record exists, but the customer wants to save anyway.
DoSave()
Dim isEdit As Boolean = ResetOpRecord("Till", tbOpTill)
SmartRedirect("Optill/oprecord_edit.aspx")
End Sub
Protected Sub btnCancelSave_Click(ByVal sender As Object, ByVal e As System.EventArgs)
SetSessionValue("HackWorkaround", False)
mpeSaveConfirmation.Enabled = False
mpeSaveConfirmation.Hide()
'do nothing and return to the screen.
End Sub
Your issue is here:
<ajax:ModalPopupExtender ID="mpeSaveConfirmation" runat="server" Enabled="False"
Protected Sub btnSaveAnyway_Click(ByVal sender As Object, ByVal e As System.EventArgs)
SetSessionValue("HackWorkaround", False)
**mpeSaveConfirmation.Enabled = False**
mpeSaveConfirmation.Hide()
Make it true in code behind. and sync hide show accordingly. Also i can see in your code at some places you are using Style="display:none". So if you want to display you need use HtmlStyleWriter.Display,"block". If you use Visible true false in that case it will not work. I mean to say, that where ever you are using visible true false, in codebehind you have to use similar. If you are using style, then in codebehind you have to use the same.

Addhandler, button.click not firing using VB.NET

I am experiencing a problem with buttons and AddHandler. It only works if I use AddHandler Button1.click, AddressOf... in Page_load, but if I create the button dynamically in one of the sub routines, the event doesn't fire.
For example,
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
<asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="True">
<asp:ListItem>1</asp:ListItem>
<asp:ListItem>2</asp:ListItem>
</asp:DropDownList>
<asp:ScriptManager id="ScriptManager1" runat="server"></asp:ScriptManager>
<asp:UpdatePanel id="UpdatePanel1" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="False">
<contenttemplate>
<asp:PlaceHolder id="PlaceHolder1" runat="server"></asp:PlaceHolder>
</contenttemplate>
</asp:UpdatePanel>
<asp:UpdatePanel id="UpdatePanel2" runat="server" UpdateMode="Conditional">
<contenttemplate>
<asp:Label id="Label2" runat="server" Text="Label"></asp:Label>
</contenttemplate>
</asp:UpdatePanel>
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
Label1.Text = Date.Now
ScriptManager1.RegisterAsyncPostBackControl(DropDownList1)
End Sub
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs)
Label2.Text = "Panel refreshed at " + Date.Now.ToString()
End Sub
Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList1.SelectedIndexChanged
Dim b As New Button
b.Text = "Click"
ScriptManager1.RegisterAsyncPostBackControl(b)
AddHandler b.Click, AddressOf Button1_Click
PlaceHolder1.Controls.Add(b)
UpdatePanel1.Update()
End Sub
The dropdownlist works, but the button doesn't. What am I doing wrong?
You have to regenerate your dynamically created controls on every postback (at last in Page_Load, better in Page_Init). You have to set the ID of the controls accordingly because ASP.Net needs it to identify which control caused a Postback and to handle the appropriate events.
You could save the number of created buttons in ViewState and use this to regenerate them on Page_Load. Increase the number when you add a new button. Use this number also to make the Button's ID unique(append it to the ID) to ensure that its the same on every postback.
For further informations, have a look the Page-Lifecycle and ViewState with dynamically added controls.
Edit: As Joel commented, if you only need one Button you can set it's ID statically, but you have to regenerate it on postback f.e. to handle its click-event.
Just to aid anyone who has this problem and isn't quite sure how to implement. Here's a quick example.
This example starts out by displaying a dropdownlist. When user selects something from the dropdown, another dropdownlist appears.
I typed this off the top of my head, so it MAY contain errors, but you get the idea =)
In the aspx file, add a placeholder:
And in your codebehind:
...
Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
'Store control count in viewstate
If Not IsPostBack Then ViewState("ControlCounter") = 1
'Rebuild dynamic controls on every postback so when page's life cycle hits Page_Load,
'selected values in the viewstate (asp.net default behavior) can be loaded into the dropdowns
Build_Dynamic_Controls()
End Sub
Protected Sub Build_Dynamic_Controls()
'Clear placeholder
myPlaceholder.Controls.Clear()
'This is where the control counter stored in the viewstate comes into play
For i as Integer = 0 To CInt(ViewState("ControlCounter") -1
Dim ddlDynamic as New DropDownList With {
.ID = "ddlDynamicDropdown" & i,
.AutoPostBack = True
}
'This is the event that will be executed when the user changes a value on the form
'and the postback occurs
AddHandler ddlDynamic.SelectedIndexChanged, AddressOf ddlDynamic_SelectedIndexChanged
'Add control to the placeholder
myPlaceholder.Controls.Add(ddl)
'Put some values into the dropdown
ddlDynamic.Items.Add("Value1")
ddlDynamic.Items.Add("Value2")
ddlDynamic.Items.Add("Value3")
Next i
End Sub
Protected Sub ddlDynamic_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)
'When a dropdown value is changed, a postback is triggered (autopostback=true)
'The event is captured here and we add another dropdown.
'First we up the control count:
ViewState("ControlCounter") = CInt(ViewState("ControlCounter")) + 1
'Now that the "total controls counter" is upped by one,
'Let's recreate the controls in the placeholder to include the new dropdown
Build_Dynamic_Controls()
End Sub
...

Adding PostBackTriggers and AsyncPostBackTriggers to UpdatePanel for dynamically-generated grandchild controls

I have a page with a ScriptManager, a generic HTML drop-down list (<select>), and an UpdatePanel. The UpdatePanel contains a PlaceHolder (for now). During Page_Load, a number of user controls are added to the PlaceHolder (really, it's several instances of the same user control). The number to add is not known until the page loads, so they do need to be loaded dynamically. The drop-down list is populated with the same number of menu items, and there is javascript on the page also (using jQuery) to show only one of the controls at a time depending on the state of the drop-down list.
Each user control has two buttons that should generate an asynchronous postback, a drop-down list that should generate an asynchronous postback on a change in selected value, and a button that should generate a synchronous postback. If I was not generating the controls dynamically, and if there was only one control, the structure would be something like:
<asp:UpdatePanel ID="myUpdatePanel" runat="server" UpdateMode="Conditional"
ChildrenAsTriggers="false">
<ContentTemplate>
<asp:TextBox ID="textBox1" runat="server" />
<asp:TextBox ID="textBox2" runat="server" />
<asp:Button ID="asyncButton1" runat="server" Text="Button1"
onclick="asyncButton1_Click" />
<asp:DropDownList ID="asyncDropDown" ruant="server" AutoPostBack="true"
OnSelectedIndexChanged="asyncDropDown_SelectedIndexChanged" />
<asp:Button ID="asyncButton2" runat="server" Text="Button2"
OnClick="asyncButton2_Click" />
<asp:Button ID="syncButton" runat="server" Text="SyncButton"
OnClick="syncButton_Click" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="asyncButton1" EventName="Click" />
<asp:AsyncPostBackTrigger ControlID="asyncButton2" EventName="Click" />
<asp:AsyncPostBackTrigger ControlID="asyncDropDown"
EventName="SelectedIndexChanged" />
<asp:PostBackTrigger ControlID="syncButton" />
</Triggers>
</asp:UpdatePanel>
Of course, all the controls inside the ContentTemplate would actually be part of each user control.
Adding the triggers on the server side does not seem to work because no ControlID seems to help the UpdatePanel find the relevant controls. I can use either the control's ID or the control's UniqueID, and it does not work, and I get an error along the lines of
A control with ID 'ctl00$ContentPlaceHolder1$ctl01$asyncButton1' could not be
found for the trigger in UpdatePanel 'myUpdatePanel'.
So, I wonder if I need to register the triggers in the client instead using ASP.NET Ajax. I found this page that basically explains how. However, I do not know how to get the EventName taken into consideration. The examples I have seen so far have merely been adding button clicks, but I don't know how to handle the SelectedIndexChanged event from the DropDownList.
Any help here? Are there examples out there I have missed? It doesn't help, of course, that the method in the link I gave appears to be "unofficial," so I don't see any MSDN documents on the subject.
Thanks!
My suggestion would be to pull all your controls inclusive this UpdatePanel out of this UpdatePanel into an UserControl. Define events in your usercontrol that are raised when the buttons are clicked or the Dropdown's selected index get changed. Handle these events in your page that holds the Placeholder(in a single UpdatePanel,conditional,without triggers). Call the Update-method of the main update panel manually if you add UserControls.
To clarify what i mean have a look at following example:
Main-page aspx:
<asp:UpdatePanel ID="Upd1" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
</ContentTemplate>
</asp:UpdatePanel>
Codebehind:
Private Property UserControlCount() As Int32
Get
If ViewState("UserControlCount") Is Nothing Then
ViewState("UserControlCount") = 1
End If
Return DirectCast(ViewState("UserControlCount"), Int32)
End Get
Set(ByVal value As Int32)
ViewState("UserControlCount") = value
End Set
End Property
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
recreateUserControls()
End Sub
Private Sub recreateUserControls()
For i As Int32 = 1 To Me.UserControlCount
Dim uc As DynamicControls = DirectCast(Me.LoadControl("DynamicControls.ascx"), DynamicControls)
uc.ID = "DynamicControls_" & i
Addhandlers(uc)
Me.PlaceHolder1.Controls.Add(uc)
Next
End Sub
Private Sub Addhandlers(ByVal uc As DynamicControls)
AddHandler uc.asyncButton1Clicked, AddressOf ucAsyncButton1Clicked
AddHandler uc.asyncButton2Clicked, AddressOf ucAsyncButton2Clicked
AddHandler uc.syncButtonClicked, AddressOf ucSyncButtonClicked
AddHandler uc.asyncDropDownSelectedIndexChanged, AddressOf ucAsyncDropDownSelectedIndexChanged
End Sub
Private Sub addUserControl()
Me.UserControlCount += 1
Dim uc As DynamicControls = DirectCast(Me.LoadControl("DynamicControls.ascx"), DynamicControls)
uc.ID = "DynamicControls_" & Me.UserControlCount
Addhandlers(uc)
Me.PlaceHolder1.Controls.Add(uc)
Upd1.Update()
End Sub
Private Sub ucAsyncButton1Clicked(ByVal sender As Object, ByVal e As EventArgs)
'only to demonstrate how to add control dynamically and update the UpdatePanel'
addUserControl()
Me.Upd1.Update()
End Sub
Private Sub ucAsyncButton2Clicked(ByVal sender As Object, ByVal e As EventArgs)
End Sub
Private Sub ucSyncButtonClicked(ByVal sender As Object, ByVal e As EventArgs)
End Sub
Private Sub ucAsyncDropDownSelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)
End Sub
ascx which holds your controls:
<%# Control Language="vb" AutoEventWireup="false" CodeBehind="DynamicControls.ascx.vb" Inherits="AJAXEnabledWebApplication1.DynamicControls" %>
<%# Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>
<asp:UpdatePanel ID="myUpdatePanel" runat="server" UpdateMode="Conditional"
ChildrenAsTriggers="false">
<ContentTemplate>
<asp:TextBox ID="textBox1" runat="server" />
<asp:TextBox ID="textBox2" runat="server" />
<asp:Button ID="asyncButton1" runat="server" Text="Button1" />
<asp:DropDownList ID="asyncDropDown" runat="server" AutoPostBack="true" />
<asp:Button ID="asyncButton2" runat="server" Text="Button2" />
<asp:Button ID="syncButton" runat="server" Text="SyncButton" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="asyncButton1" EventName="Click" />
<asp:AsyncPostBackTrigger ControlID="asyncButton2" EventName="Click" />
<asp:AsyncPostBackTrigger ControlID="asyncDropDown" EventName="SelectedIndexChanged" />
<asp:PostBackTrigger ControlID="syncButton" />
</Triggers>
</asp:UpdatePanel>
Codebehind of UserControl:
Public Partial Class DynamicControls
Inherits System.Web.UI.UserControl
Public Event asyncButton1Clicked(ByVal sender As Object, ByVal e As System.EventArgs)
Public Event asyncButton2Clicked(ByVal sender As Object, ByVal e As System.EventArgs)
Public Event syncButtonClicked(ByVal sender As Object, ByVal e As System.EventArgs)
Public Event asyncDropDownSelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)
Private Sub asyncButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles asyncButton1.Click
RaiseEvent asyncButton1Clicked(sender, e)
End Sub
Private Sub asyncButton2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles asyncButton2.Click
RaiseEvent asyncButton2Clicked(sender, e)
End Sub
Private Sub syncButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles syncButton.Click
RaiseEvent syncButtonClicked(sender, e)
End Sub
Private Sub asyncDropDown_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles asyncDropDown.SelectedIndexChanged
RaiseEvent asyncDropDownSelectedIndexChanged(sender, e)
End Sub
End Class
On this way you won't have problems with ClientID's.
Addition:
If you need access to the controls of your UserControls in the event-handlers, use one of following two options:
cast the sender's NamingContainer to the userControl's type: Dim uc As DynamicControls = DirectCast(DirectCast(sender, Control).NamingContainer, DynamicControls)
replace all occurences of (ByVal sender As Object, ByVal e As System.EventArgs) with (uc as DynamicControls). On this way the reference of your UserControl is added to the event as parameter and you could access public properties of it from the page, f.e.:
dim txt1 as String = uc.Text1
If you have exposed a property Text1 in the UserControl:
Public Property Text1() As String
Get
Return textBox1.Text
End Get
Set(ByVal value As String)
textBox1.Text = value
End Set
End Property
The second option is the cleanest and most readable way.
Update:
According to your comment: you should place the UpdateProgress in the UserControl inside of the UpdatePanel that gets updated. Remember to set the AssociatedUpdatePanelID correctly. For example:
<asp:UpdatePanel ID="UdpForm" runat="server" UpdateMode="conditional" ChildrenAsTriggers="false" >
<ContentTemplate>
<asp:panel ID="FormPanel" runat="server">
<asp:UpdateProgress ID="UpdateProgress1" DynamicLayout="true" runat="server" AssociatedUpdatePanelID="UdpForm" DisplayAfter="0" >
<ProgressTemplate>
<div class="progress">
<asp:Image ID="ImgProgress1" runat="server" ImageUrl="~/images/ajax-loader-arrows.gif" ToolTip="loading..." /> please wait...
</div>
</ProgressTemplate>
</asp:UpdateProgress>
<asp:FormView ID="FormView1" runat="server" DefaultMode="ReadOnly" >
<ItemTemplate></ItemTemplate>
<EditItemTemplate></EditItemTemplate>
<InsertItemTemplate></InsertItemTemplate>
<EmptyDataTemplate>
</EmptyDataTemplate>
<PagerTemplate >
</PagerTemplate>
</asp:FormView>
</asp:panel>
</contenttemplate>
</asp:UpdatePanel>
<asp:UpdatePanel ID="UpdContent" runat="server" UpdateMode="conditional" ChildrenAsTriggers="false" >
<ContentTemplate>
<asp:Panel ID="PnlMain" runat="server">
<asp:UpdateProgress ID="UpdateProgress2" DynamicLayout="true" runat="server" AssociatedUpdatePanelID="UpdContent" DisplayAfter="0" >
<ProgressTemplate>
<div class="progress">
<asp:Image ID="ImgProgress1" runat="server" ImageUrl="~/images/ajax-loader-arrows.gif" ToolTip="loading..." /> please wait...
</div>
</ProgressTemplate>
</asp:UpdateProgress>
Content
</asp:Panel>
</ContentTemplate>
<Triggers ></Triggers>
</asp:UpdatePanel>

Resources