ASP.Net CustomValidator in GridView is not fired - asp.net

I got a Gridview in an UpdatePanel with this EditTemplate:
<edititemtemplate>
<asp:textbox id="txtDistFrom" runat="server" text='<%# Bind("distFrom") %>' width="30" />
<asp:CustomValidator ID="valDistFrom" ValidateEmptyText="True" OnServerValidate="valDistFromTo_ServerValidate" ControlToValidate="txtDistFrom" Text="Missing" ToolTip="Invalid" Display="Dynamic" runat="server" />
</edititemtemplate>
And a simple Server-side function:
Protected Sub valDistFromTo_ServerValidate(ByVal source As Object, ByVal args As System.Web.UI.WebControls.ServerValidateEventArgs)
Dim cv As CustomValidator = CType(source, CustomValidator)
Dim gvr As GridViewRow = cv.NamingContainer
Dim tbV As UI.WebControls.TextBox = gvr.FindControl("txtDistFrom")
If tbV.Text <> "" Then
args.IsValid = False
cv.ErrorMessage = "inhalt ist " & tbV.Text
End If
End Sub
But when debugging this code the server-side function is not fired, whatever it does. It seems it has to do with the gridview, so I cannot access the control directly by its id. Any suggestions?

If you modify your VB to:
Protected Sub valDistFromTo_ServerValidate(ByVal source As Object, ByVal args As System.Web.UI.WebControls.ServerValidateEventArgs)
Dim cv As CustomValidator = CType(source, CustomValidator)
If args.Value <> "" Then
args.IsValid = False
cv.ErrorMessage = "inhalt ist " & args.Value
End If
End Sub
It should work. Note that I'm using args.Value. I use CustomValidators and TextBox within EditTemplates with ControlToValidate set to the TextBox ID all the time and it works, you just can't get the TextBox object the way you're trying it. I think this is far less of a pain and much cleaner than messing around with RowUpdating Event as suggested in TGnat's answer.

In this case you can use a required field validator. Which should work just fine in a grid.
For server side validation I would move the custom validator outside the grid entirely and leave the ControlToValidate property blank. You can move your validation to the RowUpdating event of the grid and set any error messages on the custom validator. Rmember to set the validators IsValid property appropriately.

The problem is related to the ControlToValidate property, because the ID of your text box is not used in repeating elements like GridView, ListView and Repeater. In other words: You have stumbled upon a limitation in ASP.NET's engine.
I am not sure how to solve this problem, though. You might be able do it, by adding the CustomValidator programmatically by attaching a method to the GridView's OnRowBound method.
This article might provide an answer This article might provide an answer: Integrating Asp.Net Validation Controls with GridView at run-time.

I also tend to think that ControlToValidate is the problem. .NET changes the ID of that control at runtime and the custom validator probably isn't picking it up.
I would try adding the customvalidator on RowCreated or RowDatabound using the FindControl()

I had the same problem. When I explicitly set this property in my customvalidator, the server side code fired:
EnableClientScript="false"

Related

Server Side Custom Validator Not Firing

I'm having an odd problem in that one of the 4 custom validators on my web page is not firing. Everything looks correct based on the working validators. Below is the simplified code.
ASPX code -
<asp:TextBox ID="CMT_TXT" runat="server" Columns="60" Rows="8"
TextMode="MultiLine" Text='<%#Eval("CMT_TXT")%>'></asp:TextBox><br />
<asp:CustomValidator ID="csvCMT_TXT" runat="server" ControlToValidate="CMT_TXT"
Display="Dynamic" EnableClientScript="False" ErrorMessage="Msg">
</asp:CustomValidator>
VB code -
Public Sub csvCMT_TXT_ServerValidate(source As Object,
args As ServerValidateEventArgs) _
Handles csvCMT_TXT.ServerValidate
dim s As String = CMT_TXT.Text
args.IsValid = s.Length <= 3500
End Sub
When testing,
The contents of field CMT_TXT has approximately 3000 characters. So it is not an empty field issue.
Page.Validate is called in the main body of the code
for server side validation to fire you need to call Page.Validate, this should trigger all your server side validation and update Page.IsValid
Also it does not look like you have the event set up on the custom val. may want to add the prop OnServerValidate
OnServerValidate="csvCMT_TXT_ServerValidate"
<asp:CustomValidator ID="csvCMT_TXT" runat="server" ControlToValidate="CMT_TXT"
Display="Dynamic" EnableClientScript="False" ErrorMessage="Msg" OnServerValidate="csvCMT_TXT_ServerValidate">
</asp:CustomValidator>
I'm not too familiar with VB.NET, but with C# I'll check the Page.IsValid field before continuing on with a page that has a CustomValidator.
An example with a Wizard control that contains a CustomValidator in the final stage, I will check this value in the FinishButtonClick event.
protected void Wizard1_FinishButtonClick(object sender, WizardNavigationEventArgs e)
{
if (Page.IsValid == false)
{
// validator failed, stop wizard from continuing
return;
}
// page is valid, continue on
// ...
}
Not sure if there are any differences with VB.NET, but may be worth a shot
At a glance, it looks like you named your handler incorrectly. The control ID is csvCMT_TXT while the handler is csv_CMT_TXT.ServerValidate. There's an extra _ in the handler.
The problem turned out to be a problem with the VS2010 project build.
To this the problem, I had to
Deleted all files in BIN directory
Deleted all file in OBJ directory
Deleted Custom Validator
Rebuilt the Project
Add Custom Validation back into program. Added the original code back into program
It's a bit extreme, but this is what it took to resolve the problem.

Set a property of a user control from repeater databound data

I have a user control inside of a repeater that is being bound by a sqldatasource. I get the following error: Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control. EDIT: NEVERMIND Egg on my face. I was getting this databind error because I was binding it somewhere else in an effort to troubleshoot my real problem from last friday but I forgot about it.
WHAT MY REAL PROBLEM IS: The usercontrol is getting bound before the properties get set so it appears as if they are never set. When stepping through I see that they get on the property is hit before the set on the property is hit. For example if I put <%# EVal("Address_ID") %> before the user control I will see the ID displayed but then the user control will display an emptydatatemplate because it is being passed the ID of 0.
<asp:SqlDataSource ID="sqlFacilityAddresses" runat="server" DataSourceMode="DataSet" SelectCommandType="StoredProcedure" SelectCommand="SP_Facility_GetAddresses" ConnectionString="<%$ ConnectionStrings:Trustaff_ESig2 %>">
<SelectParameters>
<asp:Parameter Name="Facility_ID" DbType="Int32" />
</SelectParameters>
</asp:SqlDataSource>
<asp:Repeater ID="repeaterAddresses" DataSourceID="sqlFacilityAddresses" runat="server">
<ItemTemplate>
<Select:Address ID="AddressControl" runat="server" AddressID='<%# EVal("Address_ID") %>' />
</ItemTemplate>
</asp:Repeater>
You could handle the repeater's ItemDataBound-Event in codebehind, get a reference to the UserControl via Item.FindControl and set the property according to the Item.DataItem object and your column Address_ID.
For example(debug to see if the type of your dataitem is really DataRowView):
Sub repeaterAddresses_ItemDataBound(Sender As Object, e As RepeaterItemEventArgs)
' This event is raised for the header, the footer, separators, and items.
' Execute the following logic for Items and Alternating Items.
If (e.Item.ItemType = ListItemType.Item) Or _
(e.Item.ItemType = ListItemType.AlternatingItem) Then
Dim addressControl as AddressControl = DirectCast(e.Item.FindControl("AddressControl", AddressControl)
addressControl.AddressID = DirectCast(e.Item.DataItem, DataRowView)("Address_ID").ToString
End If
End Sub
What exactly does your Address UC look like? You can use your AddressID property to do this: e.g.
private bool _AddressID;
public bool AddressID
{
get { return _AddressID; }
set
{
if (_AddressID != value)
{
//addressid is changed
_AddressID = value;
ReloadMyUC();
}
}
}
The ReloadMyUC method does the job of getting data and rebinding the UC.
I just figured this out. First I changed the repeater event to itemcreated instead of itemdatabound but then the repeater was being databound before the event that was resetting the page was being executed which resulted in a 0 ID being sent to my address user control. What I ended up doing was creating a boolean value for the class page setting it to true on the user control raised event this way when it went through the repeater itemcreated event the first time it wouldn't error out and when it went through the second time it would work correctly. This is probably not a best practices way of accomplishing this result but it works.
Maybe using DataBinder.Eval instead of Eval would help?

asp.net usercontrol SetFocusOnError

I have a aspx page that several of the same usercontrols on the page. The usercontrol houses a textbox that has a Required field validator on it. The validator works but the setonfocus="true" does not seem to be working, further more, the button the aspx page when the validator shows the error message, the button still fires the code behind.
Here is what the aspx page looks like as far as the user control and the the button.
ucTB:ucTextBox ID="ucTextR" runat="server" ValidationGroup="txtRequired" Required="_true"
asp:Button ID="btnSave" runat="server" Text="Click" ValidationGroup="txtRequired"
and the usercontrol validator
asp:RequiredFieldValidator ID="rfTextBox" runat="server" ControlToValidate="txtTextBox"
SetFocusOnError="true" ErrorMessage="Required Field" EnableClientScript="false"
the user control has been wired to grab the validator from the aspx page and use it in the usercontrol... something like this
Public Property ValidationGroup() As String
Get
Return CType(ViewState("ValidationGroup"), String)
End Get
Set(ByVal Value As String)
ViewState("ValidationGroup") = Value
End Set
End Property
Protected Sub AssignValidation()
For Each control As Control In Me.Controls
Dim [property] As PropertyInfo = control.[GetType]().GetProperty("ValidationGroup")
If [property] Is Nothing Then
Continue For
End If
[property].SetValue(control, ValidationGroup, Nothing)
Next
End Sub
and i load the AssignValidation on page_load
anyway.. hope this is the info you need to point me in the right direction.
What i'm looking to do is if the required field validator to put the focus on the usercontrol if there is nothing in the usercontrol text box and also for the button on the aspx page not to fire.. like i think it behaves if you use a validator on a aspx page with no usercontrol
thanks
shannon
You can't set the user control to visible because it's not a visible container. You can set the focus yourself programmatically. See this:
http://forums.digitalpoint.com/showthread.php?t=282224
Or, you can programmably set the validator to the ID of the textbox within the user control; expose a textboxID property in the user control code-behind which returns the textbox's ID, and have your page assign the validator's controltovalidateID to this.

AutoCompleteExtender control in a repeater

I have an AutoCompleteExtender AjaxControlToolkit control inside a repeater and need to get a name and a value from the web service. The auto complete field needs to store the name and there is a hidden field that needs to store the value. When trying to do this outside of a repeater I normally have the event OnClientItemSelected call a javascript function similiar to
function GetItemId(source, eventArgs)
{
document.getElementById('<%= ddItemId.ClientID %>').value = eventArgs.get_value();
}
However since the value needs to be stored in a control in a repeater I need some other way for the javascript function to "get at" the component to store the value.
I've got some JavaScript that might help you. My ASP.Net AutoComplete extender is not in a repeater, but I've modified that code to detect the ID of the TextBox you are going to write the erturned ID to, it should work (but I haven't tested it all the way through to post back).
Use the value from 'source' parameter in the client side ItemSelected method. That is the ID of the calling AutoComplete extender. Just make sure that you assign an ID the hidden TextBox in the Repeater Item that is similar to the ID of the extender.
Something like this:
<asp:Repeater ID="RepeaterCompareItems" runat="server">
<ItemTemplate>
<ajaxToolkit:AutoCompleteExtender runat="server"
ID="ACE_Item"
TargetControlID="ACE_Item_Input"
...other properties...
OnClientItemSelected="ACEUpdate_RepeaterItems" />
<asp:TextBox ID="ACE_Item_Input" runat="server" />
<asp:TextBox ID="ACE_Item_IDValue" runat="server" style="display: none;" />
</ItemTemplate>
</asp:Repeater>
Then the JS method would look like this:
function ACEUpdate_CustomerEmail(source, eventArgs) {
UpdateTextBox = document.getElementById(source.get_id() + '_IDValue');
//alert('debug = ' + UserIDTextBox);
UpdateTextBox.value = eventArgs.get_value();
//alert('customer id = ' + UpdateTextBox.value);
}
There are extra alert method calls that you can uncomment for testing and remove for production. In a simple and incomplete test page, I got IDs that looked like this: RepeaterCompareItems_ctl06_ACE_Item_IDValue (for the text box to store the value) and RepeaterCompareItems_ctl07_ACE_Item (for the AC Extender) - yours may be a little different, but it looks practical.
Good Luck.
If I understand the problem correctly, you should be able to do what you normally do, but instead of embeding the ClientId, use the 'source' argument. That should allow you to get access to the control you want to update.
Since you are using a Repeater I suggest wiring the OnItemDataBound function...
<asp:Repeater id="rptResults" OnItemDataBound="FormatResults" runat="server">
<ItemTemplate>
<asp:PlaceHolder id="phResults" runat="server" />
</ItemTemplate>
</asp:Repeater>
Then in the code behind use something like
`Private Sub FormatResults(ByVal sender As Object, ByVal e As RepeaterItemEventArgs)
Dim dr As DataRow = CType(CType(e.Item.DataItem, DataRowView).Row, DataRow) 'gives you access to all the data being bound to the row ex. dr("ID").ToString
Dim ph As PlaceHolder = CType(e.Item.FindControl("phResults"), PlaceHolder)
' programmatically create AutoCompleteExtender && set properties
' programmatically create button that fires desired JavaScript
' use "ph.Controls.Add(ctrl) to add controls to PlaceHolder
End Sub`
Voila

Including eval / bind values in OnClientClick code

I have a need to open a popup detail window from a gridview (VS 2005 / 2008). What I am trying to do is in the markup for my TemplateColumn have an asp:Button control, sort of like this:
<asp:Button ID="btnShowDetails" runat="server" CausesValidation="false"
CommandName="Details" Text="Order Details"
onClientClick="window.open('PubsOrderDetails.aspx?OrderId=<%# Eval("order_id") %>',
'','scrollbars=yes,resizable=yes, width=350, height=550');"
Of course, what isn't working is the appending of the <%# Eval...%> section to set the query string variable.
Any suggestions? Or is there a far better way of achieving the same result?
I believe the way to do it is
onClientClick=<%# string.Format("window.open('PubsOrderDetails.aspx?OrderId={0}',scrollbars=yes,resizable=yes, width=350, height=550);", Eval("order_id")) %>
I like #AviewAnew's suggestion, though you can also just write that from the code-behind by wiring up and event to the grid views ItemDataBound event. You'd then use the FindControl method on the event args you get to grab a reference to your button, and set the onclick attribute to your window.open statement.
Do this in the code-behind. Just use an event handler for gridview_RowDataBound. (My example uses a gridview with the id of "gvBoxes".
Private Sub gvBoxes_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles gvBoxes.RowDataBound
Select Case e.Row.RowType
Case DataControlRowType.DataRow
Dim btn As Button = e.Row.FindControl("btnShowDetails")
btn.OnClientClick = "window.open('PubsOrderDetails.aspx?OrderId=" & DataItem.Eval("OrderId") & "','','scrollbars=yes,resizable=yes, width=350, height=550');"
End Select
End Sub

Resources