Event handlers for controls in an ASP Repeater - asp.net

I need to be able to trigger events when a user clicks on a radio button that is generated within an control on my page.
I've added an OnSelectedIndexChanged handler to the RadioButtonList and created a function in my code behind that should handle the selection of the RadioButtonList's ListItems, but I don't know how to pass a value to that function.
Here's my code:
<asp:Repeater runat="server" ID="rptQuestions">
<HeaderTemplate><table width="100%"></HeaderTemplate>
<ItemTemplate>
<tr>
<td colspan="2"><%# Container.DataItem("QuestionText")%></td>
</tr>
<tr>
<td>
<asp:RadioButtonList runat="server" ID="rblAnswerSelection" RepeatDirection="Horizontal" AutoPostBack="true" OnSelectedIndexChanged="CheckAnswer">
<asp:ListItem Text="True" Value="1"></asp:ListItem>
<asp:ListItem Text="False" Value="0"></asp:ListItem>
</asp:RadioButtonList>
<asp:Label runat="server" ID="lblCorrectAnswer" Text="<%#Container.DataItem("CorrectAnswer") %>"></asp:Label>
</td>
<td><asp:Label runat="server" ID="lblResult"></asp:Label></td>
</tr>
</ItemTemplate>
<FooterTemplate></table></FooterTemplate>
</asp:Repeater>
Private Function CheckAnswer(ByVal SelectedAnswer As Integer) As Boolean
Select Case SelectedAnswer
Case 1 ' User answered True
If lblCorrectAnswer.Text = "True" Then
Return True
Else
Return False
End If
Case 0 ' User answered False
If lblCorrectAnswer.Text = "False" Then
Return True
Else
Return False
End If
End Select
End Function
The idea is that the user is to be informed whether or not their selected answer was correct. The value of the CheckAnswer function would determine a "CORRECT" or "INCORRECT" message displaying on lblResult. If there's a way to do this without a PostBack, then that would be ideal.
Any suggestions will be greatly appreciated.

Here's brief outline of client side solution (w/o post-back). Use html radio-buttons along with a hidden field to store the correct answer. Attach click event handlers on radio-buttons to match the value with the value from hidden field.
Repeater Mark-up:
<tr>
<td colspan="2"><%# Container.DataItem("QuestionText")%></td>
</tr>
<tr>
<td class="answerContainer">
<input type="radio" class="option" name='answer_<%# Container.ItemIndex %>' value="1">True
<input type="radio" class="option" name='answer_<%# Container.ItemIndex %>' value="0">False
<input type="hidden" id="correct_answer" value='<%#Container.DataItem("CorrectAnswer") %>' />
</td>
<td>
<span class="result" />
</td>
</tr>
Java-script (using jquery):
$(document).ready(function() {
$('td.answerContainer .option').click(function() {
var opt = $(this);
var correctAns = opt.next('#correct_answer');
var result = (opt.val() == correctAns.val()) ? "Correct" : "Incorrect";
opt.parent().next('td').find('.result').html(result);
});
});
Disclaimer: untested code
Note that radio button names are suffixed with the item index so that on the server side, you can read the user's answers by looking into the request (for example, value of Request["answer_1"] should tell user selected answer for second question -> "1" : True, "0": False and empty string: no selection).
The obvious disadvantage is that correct answer is stored in the html and so it will be simpler to cheat the system. Solution can be to make ajax call to the server to check if the answer is correct or not - this will need creating salted time-bound pseudo-random tokens to identify questions (otherwise user can make same ajax calls to get answers).

Cut your RadioButton code from repeater somewhere outside repeater, go to Design view and click on that RadioButton (now it's not included in repeater and you can assign event handler to it without typing any code manually).
When you select this RadioButton in DesignView, click events button in your proporties tab and click OnSelectedIndexChanged and this will auto generate eventhandler function. You have to enable autopostback on this radiobutton (arrow in top right corner of control - in Design view).
Now cut back this RadioButton and place it inside your repeater. Now every radiobutton change will cause calling generated handler function where you can cast sender object to RadioButton, and you can access its value with SelectedValue property.
I've tried this solution many times, but only in C# so I am sorry if something is different in VB.NET

You can't use Function as an event handler. It must be a sub which takes two argument.
Take a look at this sample.
Markup
<asp:Repeater runat="server" ID="rptQuestions">
<HeaderTemplate><table width="100%"></HeaderTemplate>
<ItemTemplate>
<tr>
<td colspan="2"><%# Eval("Name")%></td>
</tr>
<tr>
<td>
<asp:RadioButtonList runat="server"
ID="rblAnswerSelection"
RepeatDirection="Horizontal"
AutoPostBack="true"
OnSelectedIndexChanged="CheckAnswer">
<asp:ListItem Text="True" Value="1"></asp:ListItem>
<asp:ListItem Text="False" Value="0"></asp:ListItem>
</asp:RadioButtonList>
<asp:Label runat="server"
ID="lblCorrectAnswer"
Text='<%#Eval("CorrectAnswer") %>'>
</asp:Label>
</td>
<td><asp:Label runat="server" ID="lblResult"></asp:Label></td>
</tr>
</ItemTemplate>
<FooterTemplate></table></FooterTemplate>
</asp:Repeater>
Code-behind
Public Class Data
Public Property QuestionText As String
Public Property CorrectAnswer As String
End Class
Dim lst As List(Of Data)
Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
lst = New List(Of Data)
lst.Add(New Data() With {.QuestionText = "A", .CorrectAnswer = "True"})
lst.Add(New Data() With {.QuestionText = "B", .CorrectAnswer = "False"})
lst.Add(New Data() With {.QuestionText = "C", .CorrectAnswer = "True"})
rptQuestions.DataSource = lst
rptQuestions.DataBind()
End If
End Sub
Protected Sub CheckAnswer(sender As Object, e As System.EventArgs)
Dim rad As RadioButtonList = DirectCast(sender, RadioButtonList)
Dim item As RepeaterItem = DirectCast(rad.Parent, RepeaterItem)
Dim correctAns As Label = DirectCast(item.FindControl("lblCorrectAnswer"), Label)
Dim result As Label = DirectCast(item.FindControl("lblResult"), Label)
Dim SelectedAnswer As String = rad.SelectedItem.Text
If correctAns.Text = SelectedAnswer Then
result.Text = "Correct"
Else
result.Text = "Incorrect"
End If
End Sub

Related

Dropdown in listview SelectedIndexChanged doesn't work first time but does after

I've searched high and low, I can't seem to find out what's happening with this. I've simplified the code, but I really have taken it back to as basic as this and still have the same problem.
I have a drop down list in a a repeater (in a Web Form with master page):
<asp:DropDownList ID="TicketQuantityDDL" runat="server" CssClass="qtyddl" AutoPostBack="true" OnSelectedIndexChanged="TicketQuantityDDL_SelectedIndexChanged" CausesValidation="false" SelectedIndex='<%# CInt(Eval("Quantity")) - 1%>'>
<asp:ListItem Value="1">1</asp:ListItem>
<asp:ListItem Value="2">2</asp:ListItem>
<asp:ListItem Value="3">3</asp:ListItem>
<asp:ListItem Value="4">4</asp:ListItem>
<asp:ListItem Value="5">5</asp:ListItem>
<asp:ListItem Value="6">6</asp:ListItem>
</asp:DropDownList>
Handler
Protected Sub TicketQuantityDDL_SelectedIndexChanged(sender As Object, e As EventArgs)
myLiteral.text = "Selected Index Changed handled..."
End Sub
The first time the page is loaded if I change the DDL the the page is posted back - the selected index change handler is NOT fired (I've stepped through the code, page.ispostback is true). Every time after the handler works unless the page is full reloaded.
Things I've tried:
Manually adding a handler OnItemCreated
Manually adding a handler OnItemDataBound
Manually registering the control for async postback with scriptmanager
Using OnClientSelectedIndexChanged to trigger postback from the client
Removing AutoPostBack and all of the above again...
I've used Page.Request.Params("__EVENTTARGET") to verify that when the partial postback is fired that the control is the drop down.
Even though viewstate is enabled I've tried specifying this for the control and the page directly.
Disabling validation.
I've tried not binding the value of the drop down and just leaving it
as is with no selected value and then manually setting the initial
selected value - no dice.
Tried removing update panel, same issue.
Things that are DEFINITELY not happening here.
I'm not rebinding on post back if not page.ispostback... databind...
I'm not selecting the same value/first item in the drop down
This isn't an auto ID problem, the controls ID stay the same through postbacks.
I'm not doing anything funky other than binding the repeater to a list of objects.
Why isn't the handler firing the first time? After the first time everything works exactly as intended.
Update
I've replicated the exact same behaviour in a list view. Due to time constraints I've used another approach but I'd really like to know how to fix this or at least know why it doesn't work.
Update 2
I've tested the functionality with a bog standard web form and it functions correctly. Something is up with this being in a contentplaceholder from a masterpage, the script manager or update panel. It's as if the event handler for the dropdown is only registered after the first post back, I've tried registering the handler in DataBound and also in the page LoadComplete events, the same thing still happens.
Update 3
I've since changed it to a list view, I'm having the exact same issue though.
This is on a web form with master page, the master page contains the script manager, the list view is in an update panel, although I've tried removing this and I still have the same issue. I've not included the onselectedindexchanged code, I've made it as simple as changing the text of a literal - doesn't work first post back, does the second.
I had originally specified the list items manually but have changed this to programatically at itemDataBound, still no difference.
As I stated above when I check which control caused the postback it's definitely the ddl, it just doesn't fire selectindexchanged the first time. I've also tried specifying the OnSelectedIndexChange in the control itself, still no dice.
Page load ,bind, list view and on item created code.
Page Load
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
Dim _Basket = SessionHandler.getSessionObject(SessionHandler.SessionObjects.Basket)
If _Basket Is Nothing OrElse DirectCast(_Basket, BasketContainer).BasketItemList.Count = 0 Then
BasketSectionContainer.Visible = False
alertLiteral.Text = AlertGenerator.GetAlertHTML("No Items in Basket", "There are no items in your basket, please use the menu above to navigate the site.", AlertGenerator.AlertType.warning)
If _Basket IsNot Nothing Then SessionHandler.removeSessionObject(SessionHandler.SessionObjects.Basket)
Exit Sub
Else
Dim lBasket = DirectCast(_Basket, BasketContainer)
BindBasket(lBasket)
End If
End If
End Sub
Bind
Private Sub BindBasket(lBasket As BasketContainer)
basketListView.DataSource = lBasket.BasketItems
basketListView.DataBind()
bindTotals(lBasket) 'This just sets text of literals on the page outside of the listview
If lBasket.Postage Then
PostageDDL.visible = True 'This is outside of the list view also
End If
End Sub
Item Created
Private Sub basketListView_ItemCreated(sender As Object, e As ListViewItemEventArgs) Handles basketListView.ItemCreated
Dim QtyDDL As DropDownList = DirectCast(e.Item.FindControl("TicketQuantityDDL"), DropDownList)
AddHandler QtyDDL.SelectedIndexChanged, AddressOf TicketQuantityDDL_SelectedIndexChanged
End Sub
_Item Data Bound _
Private Sub basketListView_ItemDataBound(sender As Object, e As ListViewItemEventArgs) Handles basketListView.ItemDataBound
Dim data As BasketItem = DirectCast(e.Item.DataItem, BasketItem)
Dim QtyDDL As DropDownList = DirectCast(e.Item.FindControl("TicketQuantityDDL"), DropDownList)
For i As Integer = 1 To 6
QtyDDL.Items.Add(New ListItem(i.ToString, i.ToString))
Next
QtyDDL.DataTextField = data.BasketItemID.ToString 'no command arg for DDL so using this, I've tested without, doesn't make a difference.
Select Case data.BasketType
Case BasketInfo.BasketItemType.DiscountedTickets, BasketInfo.BasketItemType.Tickets, BasketInfo.BasketItemType.Goods
'tickets and goods...
QtyDDL.Items.FindByValue(data.Quantity.ToString).Selected = True
Case Else
'non ticket or goods type, disable quantity selection
QtyDDL.Items.FindByValue("1").Selected = True
QtyDDL.Enabled = False
End Select
End Sub
_List View _
<asp:ListView ID="basketListView" runat="server">
<LayoutTemplate>
<table class="cart-table responsive-table">
<tr>
<th>Item</th>
<th>Description</th>
<th>Price</th>
<th>Quantity</th>
<th>Total</th>
<th></th>
</tr>
<asp:PlaceHolder ID="itemPlaceholder" runat="server" />
</table>
<table class="cart-table bottom">
<tr>
<th>
<asp:Button ID="ApplyDiscountCodeButton" runat="server" CssClass="button color pull-right" Text="Apply Code" />
<asp:TextBox ID="DiscountCodeTextBox" runat="server" CssClass="discount-tb pull-right" />
</th>
</tr>
</table>
<div class="clearfix"></div>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td>
<img src="/images/shows/<%# Eval("imageURL")%>.jpg" alt="<%#Eval("BasketItemTitle")%>" class="basketimg" /></td>
<td class="cart-title">
<%#Eval("BasketItemTitle")%>
<br />
<%# String.Format("{0:dddd} {1} {0:MMMM yyyy} | {0:HH:mm}", Eval("PerformanceStarts"), Eval("OrdinalDay"))%>
<br />
<%# Eval("VenueTitle")%>
</td>
<td>
<%#Eval("PriceBandType")%>
<br />
# <%# String.Format("{0:c}", Eval("PriceBandValue"))%>
</td>
<td>
<asp:DropDownList ID="TicketQuantityDDL" runat="server" CssClass="qtyddl" AutoPostBack="true" ClientIDMode="Static" />
</td>
<td class="cart-total"><%#String.Format("{0:c}", Eval("BasketItemTotalValue"))%></td>
<td>
<asp:LinkButton ID="RemoveLinkBtn" runat="server" CssClass="cart-remove" CommandName="RemoveBasketItem" CommandArgument='<%#Eval("BasketItemID")%>' />
</td>
</tr>
</ItemTemplate>
</asp:ListView>
Since the Dropdown in inside the repeater, you can try the following Option instead.
Add OnItemCommand to the repeater. This should definitely Trigger the Event on selection Change in the Dropdown. Then in the OnItemCommand you Need to cast the Sender to DropDownList to be able get the selected value of the dropdown
You have to register the dropdown list event like this.
protected virtual void OnRepeaterItemCreated(object sender, RepeaterItemEventArgs e)
{
DropDownList dropdown = (DropDownList)e.Item.FindControl("TicketQuantityDDL");
dropdown.SelectedIndexChanged += TicketQuantityDDL_SelectedIndexChanged;
}
And also add this piece of code in your repeater.
OnItemCreated="OnRepeaterItemCreated"
And then you can do the selected index changed event like this.
protected void TicketQuantityDDL_SelectedIndexChanged(object sender, EventArgs e)
{
DropDownList mydropdownlist = (DropDownList)sender;
Response.Write(mydropdownlist.SelectedValue);
}
protected void dropdownlist1_SelectedIndexChanged(object sender, EventArgs e)
{
DropDownList ddlListFind = (DropDownList)sender;
ListViewItem item1 = (ListViewItem)ddlListFind.NamingContainer;
DropDownList getDDLList = (DropDownList)item1.FindControl("dropdownlist1");
Label lblMessage = (Label)item1.FindControl("lblMsg");
lblMessage.Visible = true; lblMessage.Text = "dropDown text is : " + getDDLList.SelectedItem.Text + " and value is : " + getDDLList.SelectedItem.Value;
}

Dropdown List not setting selectedValue in its databind event

I've currently run into an error with binding causing an error when the value pulled from the database is blank.
In an attempt to fix it I am instead binding the value from the database to a label, and then looping through the items in the combobox's databound event, matching the against the label. If it finds a match, it is supposed to set the selected value of the combobox to the text value of the label (lblSalesManagerValue).
<td align="left">
<asp:Label ID="lblSalesManager" runat="server" Text="Sales Manager:" Width="120px"></asp:Label>
<telerik:RadComboBox ID="cboSalesManager" runat="server" DataSourceID="SalesManagersDataSource" DataTextField="Name" DataValueField="No." Width="149px" OnDataBound="cboSalesManager_DataBound" />
<asp:Label ID="lblSalesManagerValue" runat="server" Text='<%# Bind("SM") %>' Visible="false"></asp:Label>
<asp:Label ID="lblTest" runat="server" Text="" Visible="True"></asp:Label>
<asp:RequiredFieldValidator id="RequiredFieldValidator4" runat="server"
ErrorMessage="Please select Sales Manager." Text=" *" ControlToValidate= "cboSalesManager"></asp:RequiredFieldValidator>
</td>
I am working in VB, using a Telerik RadGrid.
Protected Sub cboSalesManager_DataBound(sender As Object, e As EventArgs)
Dim cbo As RadComboBox = DirectCast(sender, RadComboBox)
Dim gr As GridEditFormItem = DirectCast(cbo.Parent.NamingContainer, GridEditFormItem)
For Each itm As RadComboBoxItem In cbo.Items
If itm.Value.ToString = CType(gr.FindControl("lblSalesManagerValue"), Label).Text Then
cbo.SelectedValue = CType(gr.FindControl("lblSalesManagerValue"), Label).Text
CType(gr.FindControl("lblTest"), Label).Text = CType(gr.FindControl("lblTest"), Label).Text & ";" & cbo.SelectedValue.ToString
End If
Next
End Sub
Everything fires, as I get a semicolon beside my combobox, but the value just won't set for some reason and I am baffled.
Edit: After playing with it some more, I have discovered that nothing from that label will stick. I tried setting the test label (I'd response.write but this is an asynchronous call) and it won't set either.
I figured it out. The combobox was binding before the label. I added a forced
CType(gr.FindControl("lblSalesManagerValue"), Label).DataBind()
And it works perfectly. =)

CustomValidator handler not being called after user clicks the Update link on the GridView

On the DropDownList SelectedIndexChanged, the page posts back and records the name of the developer in the radiobutton. When the user clicks the Update link on the GridView, I want the customValidator to check if the Radiobutton is empty.
<EditItemTemplate>
<table>
<tr>
<td>
<asp:DropDownList ID="_ddlAllDev" runat="server" AutoPostBack = "true" DataTextField = "Text"
DataValueField = "Value" OnSelectedIndexChanged = "ddlAllDev_SelectedIndexChanged">
</asp:DropDownList>
</td>
</tr>
<tr>
<td>
<asp:RadioButtonList ID="_rblAllDev" runat="server" AutoPostBack = "true" DataTextField = "Text"
DataValueField = "Value" OnSelectedIndexChanged = "rblAllDev_SelectedIndexChanged">
</asp:RadioButtonList>
<asp:CustomValidator ID="_valDev" runat="server" ControlToValidate = "_rblAllDev" Text = "*"
ErrorMessage="Please choose at leat one developer for this task" OnServerValidate = "AllDev_ServerValidate" />
</td>
</tr>
</table>
</EditItemTemplate>
The problem is that it looks like the CustomerValidator handler is not even being called after the user click the UPPDATE link on the GridView. I have put a break point inside the method but the method is not even being called.
protected void AllDev_ServerValidate(object sender, ServerValidateEventArgs args)
{
}
Am I missing something?
Thanks for helping.

ASP.Net 3.5 AjaxControlToolkit CollapsiblePanelExpander in Master In UpdatePanel partial postback woes

Long time reader first time poster
I have a content page linked to a master page. The master has the toolkitscriptmanager. On the content page, I have a nested repeater within the item template of the parent repeater, I have a CollapsiblePanelExtender for the child repeater control. Upon partial postback, if the collapsed attribute is not set, then all of the panels expand. If the collapsed attribute is set to true, then all of the panels are collapsed upon partial postback.
The databinding of the repeater is occurring in the page_init section of the code behind, and I am checking the state of the panels by iterating through the items in the page_load event. Nothing appears to be working, while debugging the code, I do see the properties being appropriately set, but when rendered in the browser, all panels are open. The edit event that triggers the partial postback is preformed in a modalpopup.
I have attempted to move the panels into their own update panel, remove the update panel, place the modal outside of the update panel to no avail. The ViewState is not maintained.
Page Code:
<asp:Repeater ID="rptrConsultants" runat="server" OnItemCommand="rptrConsultants_ItemCommand" OnItemDataBound="rptrConsultants_ItemDataBound">
<HeaderTemplate>
<div class="pageTitle">
Your Consultants (Click on the image beside the consultant to view their group memberships)
<br />
Official names are listed. Expanding the consultant will show the name assigned and the related groups.
</div>
<br />
<table>
<tr>
<td style="width: 60px; font-weight: bold; text-align: center;">
Actions
</td>
<td style="font-weight: bold;">
Consultant information
</td>
</tr>
</HeaderTemplate>
<FooterTemplate>
</table>
<asp:Label ID="lblNoConsultant" runat="server" Text="You have not created any consultants" Visible="false"></asp:Label>
</FooterTemplate>
<ItemTemplate>
<tr>
<td style="width: 60px; text-align: center;">
<asp:Image ID="imgConsultantExpander" runat="server" ToolTip="Click here to toggle consultant details" AlternateText="Toggle Consultant Details" />
<asp:ImageButton ID="btnDeleteConsultant" AlternateText="Delete Consultant" runat="server" CausesValidation="false" CommandName="DeleteConsultant" CommandArgument='<%#DataBinder.Eval(Container.DataItem, "IndividualsID")%>' ImageUrl="~/Styles/Images/Icons/remove_consultant.png" ToolTip="Delete consultant" />
<asp:ImageButton ID="btnAddConsultantToNewGroup" AlternateText="Add consultant to a new group" runat="server" CommandName="AddConsultantToGroup" CommandArgument='<%#DataBinder.Eval(Container.DataItem, "IndividualsID")%>' ImageUrl="~/Styles/Images/Icons/add_user_to_group.png" ToolTip="Add consultant to a new group" />
</td>
<td>
<%#DataBinder.Eval(Container.DataItem, "Name")%>
(<%#DataBinder.Eval(Container.DataItem, "UserID") %>)
<asp:Label ID="lblGroupCount" runat="server" />
</td>
</tr>
<tr>
<td colspan="2">
<asp:Panel ID="pnlConsultantExpander" runat="server">
<asp:Repeater ID="rptrConsultantGroups" runat="server" DataSource='<%#Container.DataItem.Row.GetChildRows("ConsultantGroupRelation") %>' OnItemCommand="rptrConsultantGroups_ItemCommand">
<HeaderTemplate>
<table style="margin-left: 50px;">
<tr>
<td style="width: 40px; text-align: center; font-weight: bold;">
Actions
</td>
<td style="font-weight: bold;">
Membership
</td>
<td style="font-weight: bold;">
Nick Name
</td>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td style="width: 40px;">
<asp:ImageButton ID="btnEditConsultant" AlternateText="Edit Consultant" runat="server" CommandName="EditConsultantInGroup" CommandArgument='<%#Container.DataItem("GroupID") & "|" & Container.DataItem("GroupMembershipID")%>' ImageUrl="~/Styles/Images/Icons/edit_consultant.png" />
<asp:ImageButton ID="btnDeleteConsultant" AlternateText="Remove from group" runat="server" CommandName="DeleteConsultantFromGroup" CommandArgument='<%#Container.DataItem("GroupID") & "|" & Container.DataItem("GroupMembershipID")%>' ImageUrl="~/Styles/Images/Icons/delete_fromgroup.png" ToolTip="Remove from group" />
</td>
<td class="textbold">
<%#Container.DataItem("GroupName")%>
</td>
<td class="textitalic">
<%#Container.DataItem("nickname")%>
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
</asp:Panel>
<asp:CollapsiblePanelExtender ID="cpepnlConsultantExpander" runat="server" TargetControlID="pnlConsultantExpander" CollapseControlID="imgConsultantExpander" ExpandControlID="imgConsultantExpander" CollapsedImage="~/Styles/Images/Icons/user_info.png" ExpandedImage="~/Styles/Images/Icons/user_open.png" ImageControlID="imgConsultantExpander" EnableViewState="true" CollapsedSize="0" />
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
And then the code behind method that occurs in page_load:
Private Sub setCollapsiblePanelsInRepeater(ByVal rptr As Repeater)
For Each item As RepeaterItem In rptr.Items
If ((item.ItemType = ListItemType.AlternatingItem) Or (item.ItemType = ListItemType.Item)) Then
For Each ctl As Control In item.Controls
If (TypeOf ctl Is AjaxControlToolkit.CollapsiblePanelExtender) Then
Dim cpe As AjaxControlToolkit.CollapsiblePanelExtender = DirectCast(ctl, AjaxControlToolkit.CollapsiblePanelExtender)
If (Not IsPostBack()) Then
cpe.Collapsed = True
cpe.ClientState = "true"
Else
Dim isCollapsed As Boolean
If (Request.Form(cpe.UniqueID & "_ClientState") IsNot Nothing) Then
isCollapsed = (Request.Form(cpe.UniqueID & "_ClientState").ToString() = "true")
Else
isCollapsed = True
End If
If (isCollapsed) Then
cpe.ClientState = "true"
cpe.Collapsed = True
Else
cpe.ClientState = "false"
cpe.Collapsed = False
End If
End If
End If
Next
End If
Next
End Sub
Note that the initial load event works! The panels are all collapsed on page load
Also note, that the repeater is nested in a tabPanel
Thank you in advance for your answer!
sorry for the long winded explanation
EDIT:
Update:
Attempted the Javascript Route -- Still no avail
pageLoad = function()
{
CheckStatusOfPanels();
};
function CheckStatusOfPanels()
{
var allBehaviors = Sys.Application.getComponents();
for (var loopIndex = 0; loopIndex < allBehaviors.length; loopIndex++)
{
currentBehavior = allBehaviors[loopIndex];
if (currentBehavior._name && currentBehavior.get_name() == 'CollapsiblePanelBehavior')
{
var myID = currentBehavior.get_id() + '_ClientState';
var isCollapsed = document.getElementById(myID).value
if (isCollapsed == 'true')
{
currentBehavior.expandPanel();
currentBehavior._ClientState = isCollapsed;
}
else
{
currentBehavior.collapsePanel();
currentBehavior._ClientState = isCollapsed;
}
}
}
}
** UPDATE #2 **
My Rep is under 100 so I have to wait 8 hrs for the ability to answer my own question:
Here is my "fix" after working almost all day on this:
Ok finally found a solution. Here it goes:
Using the SetCollapsiblePanelsInRepeater method posted in my original post, I added the call to this method in the updatePanel prerender method, and Voila the panels now preserve their state....
Protected Sub updtpnlMain_PreRender(ByVal sender As Object, ByVal e As EventArgs) Handles updtpnlMain.PreRender
setCollapsiblePanelsInRepeater(rptrGroups)
setCollapsiblePanelsInRepeater(rptrConsultants)
End Sub
For Clarity for others Googling this issue, the method that sets the panel events is:
Private Sub setCollapsiblePanelsInRepeater(ByVal rptr As Repeater)
For Each item As RepeaterItem In rptr.Items
If ((item.ItemType = ListItemType.AlternatingItem) Or (item.ItemType = ListItemType.Item)) Then
For Each ctl As Control In item.Controls
If (TypeOf ctl Is AjaxControlToolkit.CollapsiblePanelExtender) Then
Dim cpe As AjaxControlToolkit.CollapsiblePanelExtender = DirectCast(ctl, AjaxControlToolkit.CollapsiblePanelExtender)
If (Not IsPostBack()) Then
cpe.Collapsed = True
cpe.ClientState = "true"
Else
Dim isCollapsed As Boolean
If (Request.Form(cpe.UniqueID & "_ClientState") IsNot Nothing) Then
isCollapsed = (Request.Form(cpe.UniqueID & "_ClientState").ToString() = "true")
Else
isCollapsed = True
End If
If (isCollapsed) Then
cpe.ClientState = "true"
cpe.Collapsed = True
Else
cpe.ClientState = "false"
cpe.Collapsed = False
End If
End If
End If
Next
End If
Next
End Sub
Toolkitscript manager at master pages causes real problems. You may find your solution as using at individual content pages.
Also are you sure that you have enabled viewstate at content pages like this
<%# Page EnableViewState="true" Language="C#" AutoEventWireup="true" CodeFile="myfile.aspx.cs"
Inherits="myfile" %>
This is for C#
Ok finally found a solution. Here it goes:
Using the SetCollapsiblePanelsInRepeater method posted in my original post, I added the call to this method in the updatePanel prerender method, and Voila the panels now preserve their state....
Protected Sub updtpnlMain_PreRender(ByVal sender As Object, ByVal e As EventArgs) Handles updtpnlMain.PreRender
setCollapsiblePanelsInRepeater(rptrGroups)
setCollapsiblePanelsInRepeater(rptrConsultants)
End Sub
For Clarity for others Googling this issue, the method that checks the status of the panels and collapses / expands them is:
Private Sub setCollapsiblePanelsInRepeater(ByVal rptr As Repeater)
For Each item As RepeaterItem In rptr.Items
If ((item.ItemType = ListItemType.AlternatingItem) Or (item.ItemType = ListItemType.Item)) Then
For Each ctl As Control In item.Controls
If (TypeOf ctl Is AjaxControlToolkit.CollapsiblePanelExtender) Then
Dim cpe As AjaxControlToolkit.CollapsiblePanelExtender = DirectCast(ctl, AjaxControlToolkit.CollapsiblePanelExtender)
If (Not IsPostBack()) Then
cpe.Collapsed = True
cpe.ClientState = "true"
Else
Dim isCollapsed As Boolean
If (Request.Form(cpe.UniqueID & "_ClientState") IsNot Nothing) Then
isCollapsed = (Request.Form(cpe.UniqueID & "_ClientState").ToString() = "true")
Else
isCollapsed = True
End If
If (isCollapsed) Then
cpe.ClientState = "true"
cpe.Collapsed = True
Else
cpe.ClientState = "false"
cpe.Collapsed = False
End If
End If
End If
Next
End If
Next
End Sub

Conditional required field validation in an ASP.net ListView

I'm having a heck of a time trying to figure out how to implement validation in a ListView. The goal is to require the user to enter text in the comments TextBox, but only if the CheckBox is checked. Downside is that these controls are in the EditTemplate of a ListView. Below is a snippet of the relevant code portion of the EditTemplate:
<tr style="background-color: #00CCCC; color: #000000">
<td>
Assume Risk?
<asp:CheckBox ID="chkWaive" runat="server"
Checked='<%# Bind("Waive") %>' />
</td>
<td colspan="5">
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
runat="server" ErrorMessage="Comments required"
ControlToValidate="txtComments" />
<asp:TextBox Width="95%" ID="txtComments" runat="server"
Text='<%# Eval("Comment") %>'></asp:TextBox>
</td>
<td>
<asp:Button ID="btnSave" runat="server"
Text="Save" CommandName="Update" Width="100px" />
</td>
</tr>
Is there a way to do conditional validation using this method? If not, is there a way I could validate manually in the ItemUpdating event of the Listview, or somewhere else, and on a failure, alert the user of the error via a label or popup alert?
You can use a CustomValidator.
ASPX
<asp:CustomValidator runat="server" id="custPrimeCheck"
ControlToValidate="txtPrimeNumber"
OnServerValidate="PrimeNumberCheck"
ClientValidationFunction="CheckPrime"
ErrorMessage="Invalid Prime Number" />
Server Side Validation
Sub PrimeNumberCheck(sender as Object, args as ServerValidateEventArgs)
Dim iPrime as Integer = Cint(args.Value), iLoop as Integer, _
iSqrt as Integer = CInt(Math.Sqrt(iPrime))
For iLoop = 2 to iSqrt
If iPrime mod iLoop = 0 then
args.IsValid = False
Exit Sub
End If
Next
args.IsValid = True
End Sub
Clientside Validation
<script language="JavaScript">
<!--
function CheckPrime(sender, args)
{
var iPrime = parseInt(args.Value);
var iSqrt = parseInt(Math.sqrt(iPrime));
for (var iLoop=2; iLoop<=iSqrt; iLoop++)
if (iPrime % iLoop == 0)
{
args.IsValid = false;
return;
}
args.IsValid = true;
}
// -->
</script>
Sample taken from https://web.archive.org/web/20211020145934/https://www.4guysfromrolla.com/articles/073102-1.aspx
In a catastrophic display of ineptitude, I managed to miss that the ListView exposes the property EditItem. Which means I can get away with
CType(ListView1.EditItem.FindControl("chkWaive"),CheckBox).Checked
I can query the state of that and the text box using a CustomValidator, per Mr. Grassman's response.

Resources