dynamically deleting tab panel in tab container in asp.net using vb.net - asp.net

I'm creating a chat application in asp.net. In this I'm creating tab panels in a tab container dynamically for each user. when you select a new user I'm generating a tab for him with two text boxes and send button to send messages. If he doesn't want to continue his chat I need to remove or delete that tab. Is there any way that i can remove or delete those tabs in asp.net using vb.net?

Here is a complete working sample, it could be broken down into this line(in RemoveTab):
Me.TabContainer1.Tabs.Remove(tab)
But have a look yourself ...
Page-ASPX:
<asp:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server" CombineScripts="true">
</asp:ToolkitScriptManager>
<div>
<asp:UpdatePanel ID="UpdTabContainer" ChildrenAsTriggers="false" UpdateMode="Conditional" runat="server">
<ContentTemplate>
<asp:TabContainer ID="TabContainer1" runat="server" AutoPostBack="true">
<asp:TabPanel ID="TabUserList" runat="server" HeaderText="UserList">
<ContentTemplate>
<asp:UpdatePanel ID="UpdUserList" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:ListBox ID="ListBox1" SelectionMode="Single" AutoPostBack="true" OnSelectedIndexChanged="UserChanged" runat="server">
<asp:ListItem Text="User 1" Value="1"></asp:ListItem>
<asp:ListItem Text="User 2" Value="2"></asp:ListItem>
<asp:ListItem Text="User 3" Value="3"></asp:ListItem>
</asp:ListBox>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="TabContainer1" EventName="ActiveTabChanged" />
</Triggers>
</asp:UpdatePanel>
</ContentTemplate>
</asp:TabPanel>
</asp:TabContainer>
</ContentTemplate>
</asp:UpdatePanel>
</div>
Codebehind:
Public Class TabContainerSample
Inherits System.Web.UI.Page
Const idSuffix = "_"c
Property CreatedTabIDs As List(Of String)
Get
If Session("CreatedTabIDs") Is Nothing Then
Session("CreatedTabIDs") = New List(Of String)
End If
Return DirectCast(Session("CreatedTabIDs"), List(Of String))
End Get
Set(value As List(Of String))
Session("CreatedTabIDs") = value
End Set
End Property
Private Sub TabContainerSample_Init(sender As Object, e As System.EventArgs) Handles Me.Init
For Each userID In CreatedTabIDs
AddTab(userID)
Next
End Sub
Private Sub AddTab(tabID As String)
Dim ucChat = DirectCast(Page.LoadControl("ChatControl.ascx"), ChatControl)
ucChat.UserName = tabID
AddHandler ucChat.ChatSubmitted, AddressOf chatSubmitted
AddHandler ucChat.ChatClosed, AddressOf chatClosed
Dim newTabPanel = New AjaxControlToolkit.TabPanel
newTabPanel.ID = String.Format("Tab{0}{1}", idSuffix, tabID)
newTabPanel.HeaderText = String.Format("TabPanel {0}", tabID)
newTabPanel.Controls.Add(ucChat)
TabContainer1.Tabs.Add(newTabPanel)
End Sub
Private Sub RemoveTab(tabID As String)
Dim tab = (From t In Me.TabContainer1.Tabs.Cast(Of AjaxControlToolkit.TabPanel)()
Where t.ID.Contains(idSuffix)
Let id = t.ID.Substring(t.ID.LastIndexOf(idSuffix) + 1)
Where id = tabID
Select t).First
Me.TabContainer1.Tabs.Remove(tab)
Me.CreatedTabIDs.Remove(tabID)
Me.UpdTabContainer.Update()
End Sub
Protected Sub UserChanged(sender As Object, e As EventArgs)
Dim userName = DirectCast(sender, ListBox).SelectedValue
If Not CreatedTabIDs.Contains(userName) Then
AddTab(userName)
CreatedTabIDs.Add(userName)
Me.UpdTabContainer.Update()
End If
End Sub
Private Sub chatSubmitted(chat As ChatControl)
' do something '
End Sub
Private Sub chatClosed(chat As ChatControl)
RemoveTab(chat.UserName)
End Sub
End Class
The Chat-UserControl:
<%# Control Language="vb" AutoEventWireup="false" CodeBehind="ChatControl.ascx.vb" Inherits="WebApplication1.ChatControl" %>
<asp:UpdatePanel ID="UpdChatControl" runat="server" UpdateMode="Conditional">
<ContentTemplate>
Username: <asp:Label ID="LblUserName" runat="server" Text="0"></asp:Label>
<br /><asp:TextBox ID="TxtChat" runat="server" TextMode="MultiLine" Rows="4"></asp:TextBox>
<br /><asp:Button ID="BtnSend" runat="server" Text="Send" />
<br /><asp:Button ID="BtnClose" runat="server" Text="Close" />
</ContentTemplate>
</asp:UpdatePanel>
Codebehind:
Public Class ChatControl
Inherits System.Web.UI.UserControl
Public Event ChatSubmitted(sender As ChatControl)
Public Event ChatClosed(sender As ChatControl)
Public Property UserName As String
Get
Return Me.LblUserName.Text
End Get
Set(value As String)
Me.LblUserName.Text = value
End Set
End Property
Public Property ChatText As String
Get
Return Me.TxtChat.Text
End Get
Set(value As String)
Me.TxtChat.Text = value
End Set
End Property
Private Sub BtnSend_Click(sender As Object, e As System.EventArgs) Handles BtnSend.Click
RaiseEvent ChatSubmitted(Me)
Me.UpdChatControl.Update()
End Sub
Private Sub BtnClose_Click(sender As Object, e As System.EventArgs) Handles BtnClose.Click
RaiseEvent ChatClosed(Me)
Me.UpdChatControl.Update()
End Sub
End Class
If you have further question, ask.

Related

DropdownList OnSelectedIndexChanged inside UpdatePanel

I have an asp dropdownlist inside an update panel in my program. The function of OnSelectedIndexChanged event is working properly but the issue is the dropdownlist is not visible after post back. Following is my code.
<asp:UpdatePanel ID="updatePanel1" runat="server">
<ContentTemplate>
<div class="row cancel-paddings cancel-margins">
<div class="field">
<asp:DropDownList ID="ddlDropdownSub1" EnableViewState="true" Visible="true" runat="server" class="dropdown" OnSelectedIndexChanged="OnSelectedIndex_dropdownSub1" AutoPostBack="true"></asp:DropDownList>
</div>
</div>
<div class="card-content">
<div class="listview is-selectable custom-scroll-one" id="task-listview" data-tmpl="task-tmpl" data-dataset="demoTasks" tabindex="-1" role="listbox" aria-label="Tasks">
<ul role="presentation">
<asp:GridView ID="gvCaseCards" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:TemplateField >
<ItemTemplate>
<li aria-posinset="1" aria-setsize="12" tabindex="0" class="centered-block" aria-selected="true">
<asp:Label CssClass="listview-subheading" ID="lblCaseIdCaseCard" runat="server" Text=''></asp:Label><br />
<asp:Label CssClass="listview-heading wrap-text" ID="lblDescCaseCard" runat="server" Text=''></asp:Label><br />
<asp:Label CssClass="listview-subheading" ID="lblDueCaseCard" runat="server" Text=''></asp:Label><br /><br />
</li>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</ul>
</div>
</div>
</ContentTemplate>
</asp:UpdatePanel>
In my OnSelectedIndex_dropdownSub1 function I could get values to gridview dynamically. My page load method is below.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
Me.BindDataToDropDown1() 'fill data to dropdown
Me.BindDataToGridview() 'bind data to grid view
End If
End Sub
Following is OnSelectedIndex_dropdownSub1 event
Protected Sub OnSelectedIndex_dropdownSub1(ByVal sender As Object, ByVal e As System.EventArgs) Handles ddlDropdownSub1.SelectedIndexChanged
Me.BindDataToGridview()
End Sub
Following is my binddatatodropdown function
Public Function BindDataToDropDown1()
Dim ds As New DataSet
Dim alUtil As New ALUtility
Dim connString As String = AppSettings("conString")
Using cnn As New SqlConnection(connString)
cnn.Open()
Using dad As New SqlDataAdapter("SELECT * FROM product", cnn)
dad.Fill(ds)
End Using
cnn.Close()
End Using
ddlDropdownSub1.DataSource = ds
ddlDropdownSub1.DataTextField = "product_name"
ddlDropdownSub1.DataValueField = "product_id"
ddlDropdownSub1.DataBind()
End Function
And BindDataToGrid function is bellow,
Public Function BindDataToGridview()
Dim ds As New DataSet
Dim alUtil As New ALUtility
Dim connString As String = AppSettings("conString")
Dim product_id As Integer = Convert.ToInt32(ddlDropdownSub1.SelectedValue)
Using cnn As New SqlConnection(connString)
cnn.Open()
Using dad As New SqlDataAdapter(" Select * from Product Where product_id = " + product_id.ToString(), cnn)
dad.Fill(ds)
End Using
cnn.Close()
End Using
gvCaseCards.DataSource = ds
gvCaseCards.DataBind()
End Function
All functions are working properly except disappearance of the dropdown after postback.
Could anyone help me to solve that issue.
As far as I can see, there should be a couple of changes to the Update Panel. I really hate Update Panels generally, and think you should always try and use your own AJAX code.
First, I think you can just wrap the Gridview in the <ContentTemplate> tags, as that is only control which is being updated.
Then you need to add in a trigger, such as this:
<Triggers>
<asp:AsyncPostBackTrigger ControlID="ddlDropdownSub1" EventName="SelectedIndexChanged" />
</Triggers>
This goes outside the <ContentTemplate> tags. I think you should also put in a UpdateMode="Conditional" on the UpdatePanel control.

General Function

in a project I am working on I have TabContainer (AJAX.NET) have many tabPanels all of them are doing the same function BUT each on on a different Table
let me give a sample :
<asp:TabContainer ID="TabContainer3" runat="server" ActiveTabIndex="0" BorderStyle="None"
BorderWidth="0" CssClass="MyTabStyle" Width="625px">
<asp:TabPanel ID="TabPanel1" runat="server">
<HeaderTemplate>
Tab_x
</HeaderTemplate>
<ContentTemplate>
<asp:TextBox ID="txt_x" runat="server"></asp:TextBox>
<asp:Button ID="btnx" runat="server" Text="Button" />
</ContentTemplate>
</asp:TabPanel>
<asp:TabPanel ID="TabPanel2" runat="server">
<HeaderTemplate>
Tab_y
</HeaderTemplate>
<ContentTemplate>
<asp:TextBox ID="txt_y" runat="server"></asp:TextBox>
<asp:Button ID="btny" runat="server" Text="Button" />
</ContentTemplate>
</asp:TabPanel>
</asp:TabContainer>
Code behind (VB.NET)
Protected Sub btnx_Click(sender As Object, e As System.EventArgs) Handles btnx.Click
SaveText_x(txt_x.Text)
End Sub
Protected Sub btny_Click(sender As Object, e As System.EventArgs) Handles btny.Click
SaveText_y(txt_y.Text)
End Sub
is there a way to create general Sub or Function so if I clicked btnx function Save_x(txt_x.Text) be called
and when I click btny function Save_y(txt_y.Text) be called ?
You can assign multiple buttons to have the same click handler with the following code :
Protected Sub btn_Click(sender As Object, e As System.EventArgs) Handles btnx.Click, btny.Click
Dim btn As Button = CType(sender, Button)
If btn.ID = "btnx" Then
SaveText_x(txt_x.Text)
ElseIf btn.ID = "btny" Then
SaveText_y(txt_y.Text)
End If
End Sub
Both btnx and btny will both fire this Sub and it will check the button that sent it to see which method to call.
1)Made user control with public string property. use view state to store value of that property.
2)Add that user control into your tabs set that property with values like "X" or "Y" or any.
3)On button click check that property with if .. else if .. else or by switch statement and call your SaveText functions variants.

How to assign value to the DropBox text propery?

I'm writing a program where user has 3 drop boxes to input date, month and a year. after user selects the values I concatenate them and check for valid By default when a page loads I need to assign a current day, month and year to each drop box accordingly. Then I check validity of the date and pass value to the database.
My problem is that when I assign the values to the text of DropBoxes upon the loading of the page they are becoming permanent. even if the index is changed the values which are passed to the database are the ones which assigned to them when the page is loaded.
I can't actually understand what am I doing wrong:
these are the code samples which I've used:
I populate them with a current date values using folowing code(on the page Load event):
Dim CurYear As Integer = DatePart("yyyy", Now)
Dim CurDate As Integer = DatePart("d", Now)
Dim CurMonth As String = Format(Today.Date, "MMMM")
Dim CurDate2 As Integer = DatePart("d", Now)
Dim CurMonth2 As String = Format(Now, "MM")
Dates.Text = CurDate
Monthe.Text = CurMonth
years.Text = CurYear
Month2.Text = CurMonth2
Dates2.Text = CurDate2
Than I had to synchronize the selected index of 2 the dropboxes which contain numerical value of the month and 2 digit format of the day, to to form the proper string for checking of the date
Protected Sub Months_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs) Handles Months.SelectedIndexChanged
Month2.SelectedIndex = Months.SelectedIndex
TextBox3.Text = years.Text & "-" & Monthes.Text & "-" & Dates.Text
End Sub
Protected Sub Dates_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs) Handles Dates.SelectedIndexChanged
Dates2.SelectedIndex = Dates.SelectedIndex
TextBox3.Text = years.Text & "-" & Monthes.Text & "-" & Dates.Text
End Sub
And this is front end ASP code:
<asp:DropDownList ID="Dates" runat="server" autopostback="true">
<asp:ListItem></asp:ListItem>
<asp:ListItem>1</asp:ListItem>
...
<asp:ListItem>26</asp:ListItem>
<asp:ListItem>27</asp:ListItem>
<asp:ListItem>28</asp:ListItem>
<asp:ListItem>29</asp:ListItem>
<asp:ListItem>30</asp:ListItem>
<asp:ListItem>31</asp:ListItem>
</asp:DropDownList>
<asp:DropDownList ID="Months" runat="server" autopostback="true" >
<asp:ListItem></asp:ListItem>
<asp:ListItem>January</asp:ListItem>
...
<asp:ListItem>November</asp:ListItem>
<asp:ListItem>December</asp:ListItem>
</asp:DropDownList>
<asp:DropDownList ID="years" runat="server" autopostback="true" >
<asp:ListItem></asp:ListItem>
</asp:DropDownList>
<asp:DropDownList ID="Dates2" runat="server" AutoPostBack="True">
<asp:ListItem></asp:ListItem>
<asp:ListItem>01</asp:ListItem>
<asp:ListItem>02</asp:ListItem>
....
<asp:ListItem>29</asp:ListItem>
<asp:ListItem>30</asp:ListItem>
<asp:ListItem>31</asp:ListItem>
</asp:DropDownList>
<asp:DropDownList ID="Month2" runat="server" AutoPostBack="True">
<asp:ListItem></asp:ListItem>
<asp:ListItem>01</asp:ListItem>
....
<asp:ListItem>11</asp:ListItem>
<asp:ListItem>12</asp:ListItem>
</asp:DropDownList>
Again, if I don't assign the default values to the boxes upon loading of the page it works perfectly. if i do, those values are fixed no mater what you choose
The comparevalidator:
<asp:UpdatePanel ID="UpdatePanel19" runat="server">
<ContentTemplate>
<asp:TextBox ID="TextBox3" ValidationGroup="CompareValidatorDateTest" runat="server"></asp:TextBox>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Dates" EventName="SelectedIndexChanged" />
<asp:AsyncPostBackTrigger ControlID="Monthes" EventName="SelectedIndexChanged" />
<asp:AsyncPostBackTrigger ControlID="years" EventName="SelectedIndexChanged" />
</Triggers>
</asp:UpdatePanel>
<asp:CompareValidator ID="CompareValidator3" Display="dynamic" ControlToValidate="TextBox3"
Type="Date" Operator="LessThanEqual" Text="Please enter a valid date" runat="server"
ValidationGroup="CompareValidatorDateTest"
I suppose you didnt use IsPostBack property while binding the dropdown with values on page load.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
// Bind your dropdown here here
End If

asp:Gridview getting focused column/lifecycle issue

I am working on a proof of concept (POC) GridView for a much more complex set of functionality where any given cell is filled with a textbox whether it is in normal or edit mode. When any given textbox receives focus (tab or mouse), the row is set to an edit state and it's corresponding textbox in edit mode should receive focus. Once that textbox loses focus it should cause an update on that row. I have the POC working except finding a viable way to select the column ("textbox") that received focus which I believe is a lifecycle issue, but am not coming up with any good workarounds. Please remember this is a POC and there are things I have done that may not be best practice as a quick and dirty way to get it working. I would greatly appreciate any suggestions.
Form:
<%# Page Language="vb" AutoEventWireup="false" CodeBehind="StandardContactEditor.aspx.vb" Inherits="EditableGridView.StandardContactEditor" EnableEventValidation="false" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="contactsDataGrid"
AllowPaging="true" PageSize="5"
DataKeyNames="ID"
AutoGenerateColumns="false"
runat="server">
<Columns>
<asp:TemplateField HeaderText="First Name">
<ItemTemplate>
<asp:TextBox ID="txtFirstName" Text='<%# Bind("FirstName") %>' runat="server" />
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="txtFirstNameEdit" Text='<%# Bind("FirstName") %>' runat="server" />
</EditItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Last Name">
<ItemTemplate>
<asp:TextBox ID="txtLastName" Text='<%# Bind("LastName") %>' runat="server" />
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="txtLastNameEdit" Text='<%# Bind("LastName") %>' runat="server" />
</EditItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
</form>
</body>
</html>
Code:
Imports FileHelpers
Public Class StandardContactEditor
Inherits System.Web.UI.Page
private fileName As String = "C:\TestFiles\TestContacts.csv"
Private records As List(Of ContactCSV)
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack
BindGridData()
End If
End Sub
Private Sub BindGridData()
PopulateRecords()
Me.contactsDataGrid.DataSource = records
Me.contactsDataGrid.DataBind()
End Sub
Protected Sub RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs) Handles contactsDataGrid.RowDataBound
e.Row.Attributes("onfocus") = ClientScript.GetPostBackClientHyperlink(contactsDataGrid, "Edit$" + e.Row.DataItemIndex.ToString(), false)
If Not ((e.Row.RowState = DataControlRowState.Edit) _
Or (e.Row.RowState = (DataControlRowState.Alternate Or DataControlRowState.Edit)))
Try
' First Name Column
Dim firstNameTb As TextBox = e.Row.FindControl("txtFirstName")
' firstNameTb.Attributes("onfocus") = ClientScript.GetPostBackClientHyperlink(contactsDataGrid, "Edit$" + e.Row.DataItemIndex.ToString(), false)
' ISSUE CAUSED BY APPENDING "000" - Used for col/textbox identification
firstNameTb.Attributes("onfocus") = ClientScript.GetPostBackClientHyperlink(contactsDataGrid, "Edit$" + e.Row.DataItemIndex.ToString() + "000", false)
Catch ex As Exception
Console.WriteLine()
End Try
Try
Dim lastNameTb As TextBox = e.Row.FindControl("txtLastName")
' lastNameTb.Attributes("onfocus") = ClientScript.GetPostBackClientHyperlink(contactsDataGrid, "Edit$" + e.Row.DataItemIndex.ToString(), false)
' ISSUE CAUSED BY APPENDING "001" - Used for col/textbox identification
lastNameTb.Attributes("onfocus") = ClientScript.GetPostBackClientHyperlink(contactsDataGrid, "Edit$" + e.Row.DataItemIndex.ToString() + "001", false)
Catch ex As Exception
End Try
End If
End Sub
Protected Sub RowEditing(ByVal sender As Object, ByVal e As GridViewEditEventArgs) Handles contactsDataGrid.RowEditing
contactsDataGrid.EditIndex = (e.NewEditIndex / 1000)
Dim col As Integer = (e.NewEditIndex Mod 1000)
' contactsDataGrid.EditIndex = e.NewEditIndex
BindGridData()
Dim fntb As TextBox = contactsDataGrid.Rows(contactsDataGrid.EditIndex).FindControl("txtFirstNameEdit")
fntb.Attributes("onblur") = ClientScript.GetPostBackClientHyperlink(contactsDataGrid, "Update$" + contactsDataGrid.EditIndex.ToString(), false)
' fntb.Focus()
Dim lntb As TextBox = contactsDataGrid.Rows(contactsDataGrid.EditIndex).FindControl("txtLastNameEdit")
lntb.Attributes("onblur") = ClientScript.GetPostBackClientHyperlink(contactsDataGrid, "Update$" + contactsDataGrid.EditIndex.ToString(), false)
' lntb.Focus()
If(col = 0)
fntb.Focus()
Else If(col = 1)
lntb.Focus()
End If
End Sub
Protected Sub RowUpdating(ByVal sender As Object, ByVal e As GridViewUpdateEventArgs) Handles contactsDataGrid.RowUpdating
' AT THIS POINT THE EDIT FIELDS ARE NOT VISIBLE AND AN EXCEPTION IS THROWN WHEN TRYING TO RETRIEVE THOSE TEXTBOXES
' Trying to override here
contactsDataGrid.EditIndex = (contactsDataGrid.EditIndex / 1000)
PopulateRecords()
Dim id As String = contactsDataGrid.DataKeys(e.RowIndex).Value.ToString()
Dim record As ContactCSV = records.Where(Function(x) x.ID = id).SingleOrDefault()
Dim firstNameTb As TextBox = contactsDataGrid.Rows(e.RowIndex).FindControl("txtFirstNameEdit")
record.firstName = firstNameTb.Text
Dim lastNameTb As TextBox = contactsDataGrid.Rows(e.RowIndex).FindControl("txtLastNameEdit")
record.lastName = lastNameTb.Text
WriteRecordsToFile()
contactsDataGrid.EditIndex = -1
' update the actual data
contactsDataGrid.DataSource = records
contactsDataGrid.DataBind()
End Sub
Protected Sub SelectedColumn(ByVal Sender As Object, ByVal e As EventArgs)
Console.WriteLine()
End Sub
Private Sub PopulateRecords()
Dim fileEngine As New FileHelperEngine(GetType(ContactCSV))
Try
Dim objs As ContactCSV() = fileEngine.ReadFile(fileName)
records = objs.ToList()
Catch ex As exception
End Try
End Sub
Private Sub WriteRecordsToFile()
Dim fileEngine As New FileHelperEngine(GetType(ContactCSV))
fileEngine.WriteFile(fileName, records)
End Sub
End Class
This was a classic case of over-thinking a solution with a combination of an unfamiliar technology. The idea was to build a highly responsive (per field edit) page. Rather than mix any built-in GridView functionality, a simple OnTextChanged worked as I expected after everything else was stripped out. The problem turned out with the previous code posted is after an OnBlur, with the column identification parts in place, a row was set to Alternate state rather than Alternate Or Edit state.
Form:
<%# Page Language="vb" AutoEventWireup="false" CodeBehind="StandardContactEditor.aspx.vb" Inherits="EditableGridView.StandardContactEditor" EnableEventValidation="true" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="contactsDataGrid"
AllowPaging="true" PageSize="5"
DataKeyNames="ID"
AutoGenerateColumns="false"
runat="server">
<Columns>
<asp:TemplateField HeaderText="First Name">
<ItemTemplate>
<asp:TextBox ID="txtFirstName" Text='<%# Bind("FirstName") %>' OnTextChanged="textBox_TextChanged" AutoPostBack="true" runat="server" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Last Name">
<ItemTemplate>
<asp:TextBox ID="txtLastName" Text='<%# Bind("LastName") %>' OnTextChanged="textBox_TextChanged" AutoPostBack="true" runat="server" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Phone Number">
<ItemTemplate>
<asp:TextBox ID="txtPhoneNumber" Text='<%# Bind("PhoneNumber") %>' OnTextChanged="textBox_TextCHanged" AutoPostBack="true" runat="server" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
</form>
</body>
</html>
Code:
Imports FileHelpers
Public Class StandardContactEditor
Inherits System.Web.UI.Page
private fileName As String = "C:\TestFiles\TestContacts.csv"
Private records As List(Of ContactCSV)
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack
BindGridData()
End If
End Sub
Private Sub BindGridData()
PopulateRecords()
Me.contactsDataGrid.DataSource = records
Me.contactsDataGrid.DataBind()
End Sub
Protected Sub textBox_TextChanged(ByVal sender As Object, ByVal e As EventArgs)
PopulateRecords()
Dim tb as TextBox = sender
Dim row As GridViewRow = tb.Parent.Parent
Dim record As ContactCSV = records.Where(Function(x) x.ID = row.RowIndex).SingleOrDefault()
Dim firstNameTb As TextBox = contactsDataGrid.Rows(row.RowIndex).FindControl("txtFirstName")
record.firstName = firstNameTb.Text
Dim lastNameTb As TextBox = contactsDataGrid.Rows(row.RowIndex).FindControl("txtLastName")
record.lastName = lastNameTb.Text
Dim phoneNumberTb As TextBox = contactsDataGrid.Rows(row.RowIndex).FindControl("txtPhoneNumber")
record.PhoneNumber = phoneNumberTb.Text
WriteRecordsToFile()
End Sub
Private Sub PopulateRecords()
Dim fileEngine As New FileHelperEngine(GetType(ContactCSV))
Try
Dim objs As ContactCSV() = fileEngine.ReadFile(fileName)
records = objs.ToList()
Catch ex As exception
End Try
End Sub
Private Sub WriteRecordsToFile()
Dim fileEngine As New FileHelperEngine(GetType(ContactCSV))
fileEngine.WriteFile(fileName, records)
End Sub
End Class

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