How can I use Client ID within a server control? - asp.net

I was able to do the following in a repeater and it works just fine:
<asp:TextBox ID="txtOtherPartyName" Text='<%# Eval("Name") %>' runat="server" />
<asp:RequiredFieldValidator ID="reqLoss" ControlToValidate="txtOtherPartyName"
data-valmsg-for='<%# Container.FindControl("txtOtherPartyName").ClientID%>'
Display="Dynamic" ErrorMessage="Name of party involved is required." runat="server" />
The data-valmsg-for attribute in the required validator rendered the server control id just fine. How can I do the same for a control that sits on a page and is not within a repeater? I tried similar things without any luck.
Update
So here's what I'm looking to do exactly:
<asp:Textbox id="txtTest" runat="server" />
<asp:RequiredFieldValidator id="reqTest" data-valmsg-for=**[i want the html rendered id of "txtTest" here]** runat="server" />

You can bind that custom attribute to the control's client id in the code behind.
butthead.Attributes.Add("data-custom-prop", beavis.ClientID);

In the Repeater you used databinding syntax (<%# ... %>), but that doesn't apply in this case. You should be able to set the attribute like this:
data-valmsg-for='<%= TextBox1.ClientID %>'
Why are you adding custom attributes to the validators in the first place though, if you don't mind me asking? What purpose does it serve that can't be achieved through other means?
EDIT
Have you considered building your list of control IDs in code-behind beforehand, and storing it somewhere? Could something like this be a starting point?:
var controlList = Page.Validators.OfType<BaseValidator>()
.Select(v => Page.FindControl(v.ControlToValidate).ClientID).ToList();
EDIT
Expanding on that idea, you can serialize the list to JSON using the JavaScriptSerializer (System.Web.Script.Serialization):
//initialize the javascript serializer
var serializer = new JavaScriptSerializer();
//retrieve all of the validators on the page and store the client ids in a list
var controlList = Page.Validators.OfType<BaseValidator>()
.Select(v => Page.FindControl(v.ControlToValidate).ClientID).ToList();
//serialize the control id list and register the script block on the page
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "data", string.Format("var data = {0}", serializer.Serialize(controlList)), true);
Depending on how complex the form is, you may need to create a recursive function to find all of the controls, but hopefully this demonstrates the concept.

Related

.NET 4.5 WebForms: do I (still) really have to specify all 3 templates in a FormView?

Investigating the new strongly-typed, model-binding approach within ASP.NET 4.5 WebForms:
In Scott Hanselman's example of WebForms model binding (amongst others) I've seen the use of a FormView that opens in "Edit" mode, containing a number of DynamicControls e.g.
<asp:FormView runat="server" ID="MyForm" ... DefaultMode="Edit">
<EditItemTemplate>
<asp:DynamicControl runat="server" ID="Field1" DataField="Field1" Mode="Edit" />
<asp:DynamicControl runat="server" ID="Field2" DataField="Field2" Mode="Edit" />
</EditItemTemplate>
</asp:FormView>
In my situation, my FormView's ItemTemplate, EditItemTemplate and InsertItemTemplate will be identical, except the ItemTemplate's controls will be in "ReadOnly" mode.
Do I (still) really need to provide three near-identical copies of the template within the FormView?
I'm happy to use DynamicControls, but the team here will never go for the "3x copy-paste" approach seemingly required for the FormView, especially for our large templates.
I had thought that maybe:
the DynamicControls could get their "Mode" from the containing FormView?
I could use something other than a FormView to contain my DynamicControls?
Should I manage the DynamicControls' mode in code-behind to avoid template duplication?
Any examples/ideas?
No, you don't have to specify all 3 templates. I've had the same scenario and this is my solution:
Set the default mode to the most often used mode
Then in code behind of the form manage the form mode
In code behind copy the template e.g. EditTemplate you handcoded to the other one you need
protected void Page_Init()
{
var action = RouteData.Values["action"].ToString();
switch (action)
{
case "add":
personForm.ChangeMode(FormViewMode.Insert);
this.Page.Title += " Add";
break;
case "edit":
personForm.ChangeMode(FormViewMode.Edit);
this.Page.Title += " Change";
break;
default:
personForm.ChangeMode(FormViewMode.ReadOnly);
break;
}
// Reuse inserttemplate for editing
if (personForm.CurrentMode == FormViewMode.Edit)
{
personForm.EditItemTemplate = personForm.InsertItemTemplate;
}
}
Contrary to what many believe, you need only one template in a FormView, the EditItemTemplate.
Below is a simple example showing how to do it (notice that this is not connected to the idea of "Dynamic Data".).
In this way of doing it, the ReadOnly mode is never used, and thus an ItemTemplate is not needed. And the FormView will use the EditItemTemplate for both editing and inserting.
This way to do it simplifies the markup a lot, and when you make adjustments to the layout, you only have to do it in the one single template.
Notice that the save-button has no CommandName. The command is instead determined in the FormView1_ItemCommand event (see code).
Also notice that the mode of the FormView is determined in the event SqlDataSource1_Selected (see that code, with comments).
I have not included the markup for the SqlDataSource1, because there is nothing special you need to think about for that one. Just make it as usual.
<asp:FormView ID="FormView1" runat="server" DataSourceID="SqlDataSource1"
DataKeyNames="ApplicationId,UserId"
>
<EditItemTemplate>
<asp:TextBox ID="txtFirstName" runat="server" Text='<%# Bind("firstName") %>'></asp:TextBox><br />
<asp:TextBox ID="txtAge" runat="server" Text='<%# Bind("age") %>'></asp:TextBox><br />
<asp:Button ID="btnSave" runat="server" Text="Save" />
</EditItemTemplate>
</asp:FormView>
Private Sub FormView1_ItemCommand(sender As Object, e As FormViewCommandEventArgs) Handles FormView1.ItemCommand
Select Case FormView1.CurrentMode
Case FormViewMode.Edit
FormView1.UpdateItem(True)
Case FormViewMode.Insert
FormView1.InsertItem(True)
End Select
End Sub
Private Sub SqlDataSource1_Selected(sender As Object, e As SqlDataSourceStatusEventArgs) Handles SqlDataSource1.Selected
If e.AffectedRows = 0 Then
' nothing exists yet, so make formview ready to insert
FormView1.ChangeMode(FormViewMode.Insert)
Else
' something exists already, so make formview ready to edit
FormView1.ChangeMode(FormViewMode.Edit)
End If
End Sub
Just set AutoGenerateEditButton="true". All input types in your item template will then be editable.
You might have to show/hide border of the textboxes and the other input type using jQuery.

User Control Static Name Option?

I know there is a way to force asp web user controls (ascx) to use static ID's so they don't get appended with all the appended naming garbage...but is there way to do the same for the"name" attribute? Specifically, in this case, for DDL's? JQuery needs a specific name and I need to stop .NET from doing this.
Not possible in ASP.Net for now ... I've found this combination to be an acceptable solution.
Add the ClientIDMode="Static" to the control:
<asp:HiddenField ID="HiddenField1" runat="server" ClientIDMode="Static" />
Using JQuery, on document ready, set the name attribute on the control to the value of its id:
$(document).ready(function () {
// alert($("#HiddenField1").attr("name"));
$("#HiddenField1").attr("name", "HiddenField1");
// alert($("#HiddenField1").attr("name"));
});
Try adding ClientIDMode="Static" to the control tag.
<asp:FileUpload ID="FileUpload1" runat="server" ClientIDMode="Static" />

Required Field Validations in USER CONTROL are fired in all instances of this usercontrol when it is placed multiple times in single page

I have usercontrol which has required field validation in it. I have 2 instances of this usercontrol in single web page . Now if I click button on first usercontrol the validation messages are fired for 2 user controls. But only the required fields of that user control has to be fired. Please could any one solve this problem.
Thanks
Madhavi
If you have not set the ValidationGroup property of the RequiredFieldValidator within your usercontrol then field validation will fire whenever the form is submitted regardless of which button caused the postback.
If you wish to associated specific validators with specific submit buttons then you will have to associate them to the same ValdiationGroup.
If you are setting the ValidatioGroup within your user control but finding that the validation is firing for all instances of the control then you will need to take some attribute of the user control instance and incorporate it into the ValidationGroup to ensure the user control is exclusively validated for any submit button on the control.
Here is an example:
<asp:TextBox ID="txtTest" runat="server"/>
<asp:RequiredFieldValidator ID="txtTestReqVal"
runat="server"
Display="Dynamic"
ControlToValidate="txtTest"
Text="* Field is required"
ValidationGroup="valGroup<%= ClientId %>"
CssClass="modelError"
/>
<asp:Button ID="btnSubmit" runat="server"
Text="Submit"
CausesValidation="true"
ValidationGroup="valGroup<%= ClientId %>"
/>
The trick here is the <%= ClientId %> part. This will inject the instances client side unique id into the validation group value. This means that all validation for these controls will be grouped together by the single unique instance of the user control. This way you can have multiple instances of the same user control present on the same page but all uniquely validated.
make your validation groups runat="server"
Then give each one a unique validation group like this:
string validationGroup = Guid.NewGuid().ToString();
txtContactNameValidator.ValidationGroup = validationGroup;
txtContactNumberValidator.ValidationGroup = validationGroup;
btnSave.ValidationGroup = validationGroup;
This will isolate the user controls from each other no matter how many are on the page.
I think you need to specify a validatongroup attribute on every field validation control.
Each user control would have its own validation group defined.
See here
One way to solve this is to expose a public property on your usercontrol which you can pass in the validation group name.
<uc:mycontrol id=u1 validationgroup="valA" .. />
<uc:mycontrol id=u2 validationgroup="valB" .. />
One problem with this approach is that you will need to add the validation group to every validation control within page load on the usercontrol.
I think the validation group will not work.. As it is the same user control but dropped 2 times in the page. Any of the button will fire validation on both user control wh
function ValidateRadio(button)
{
var radioOptions = document.getElementById(button.id.split("_")[0] +'_rblPollOptions');
var RVPollOptions = document.getElementById(button.id.split("_")[0] +'_RVPollOptions');
var options = radioOptions.getElementsByTagName("input");
var radioSelected=false;
for (j=0; j < options.length; j++)
{
if(options[j].checked)
{
radioSelected=true;
break; // Found it, proceed to next question
}
}
if (!radioSelected) // no option selected
{ // warn user, focus question //alert("You did not answer question");
RVPollOptions.style.visibility = "visible";
return false;
}
}
</script>
<asp:RequiredFieldValidator ID="RVPollOptions" runat="server" ControlToValidate="rblPollOptions"
ErrorMessage="Select option!"> </asp:RequiredFieldValidator>
<asp:Button ID="btnPoll" Text="Vote" OnClientClick="javascript:return ValidateRadio(this)" runat="server" OnClick="btnPoll_Click" />

how to set Name attribute for HiddenField control server-side?

I want to set the "name" attribute for HiddenField control of ASP.NET from code behind, but I cannot find the "Attributes" property. Is it not there for a purpose? How do I add the attribute?
thanks
The name attribute is automatically computed from the ID properties of the hidden field and its ancestors in the naming container chain. You don't get to set it yourself. You can only access it through the UniqueID of the control.
A possible solution, without knowing a bit more about your code, is to use a server side Html control rather than an ASP.NET web control by adding the runat="server" attribute to the Html markup:
<input type="hidden" id="myHiddenField" runat="server" />
You can then specify the id dynamically in the code behind at runtime from which the name attribute is inferred from:
myHiddenField.ID = "CodebehindName";
myHiddenField.Value = "myValue";
This will result in the following output:
<input name="CodebehindName" type="hidden" id="CodebehindName" value="myValue" />
Another unorthodox method to deal with it is to set the name attribute client side. This is useful if you are posting to a third party such as PayPal.
jQuery EG:
<script type="text/javascript">
$(function () {
$('#BusinessHid').prop('name', 'business')
$('#CurrencyHid').prop('name', 'currency_code')
$('#InvoiceHid').prop('name', 'invoice')
$('#AmountHid').prop('name', 'amount')
})
</script>
<asp:HiddenField ID="BusinessHid" runat="server" ClientIDMode="Static" />
<asp:HiddenField ID="CurrencyHid" runat="server" ClientIDMode="Static" />
<asp:HiddenField ID="InvoiceHid" runat="server" ClientIDMode="Static" />
<asp:HiddenField ID="AmountHid" runat="server" ClientIDMode="Static" />
Forget about the HiddenField control and use a Label instead, give it a name (an id), make it invisible, and store your text into it:
label = new System.Web.UI.WebControls.Label() {
Text = "Here my hidden text",
};
label.Attributes.Add("id", "MyHiddenFieldID");
label.Attributes.Add("style", "display:none;");
myParentControl.Controls.Add(label);
Get your hidden field in your javascript with:
var myHiddenField = document.getElementById("MyHiddenFieldID");
The way I ended up doing this was to set ClientIDMode="Static" on the HiddenField and then set the ID to what I want my name to be.
I ended up with ugly IDs but this was a small price to pay to get this to work.

Setting RegularExpressionValidator ValidationExpression in JavaScript

Is it possible to set the ValidationExpression of a RegularExpressionValidator using JavaScript? I'm using ASP.NET 3.5.
Here's why I want to do this...
On a payment page I have a DropDownList that allows my user to select their card type. Beneath that is a TextBox in which they type their card number.
I want to use a RegularExpressionValidator to validate that their card number is valid for their given card type. The card payment processing is performed manually in a different system, so I cannot rely on this to catch incorrect card details.
Therefore I need to use a different ValidationExpression for each card type. I would like to set the ValidationExpression using JavaScript, firing off the DropDownList onchange event.
My DropDownList is bound to an XML document:
<asp:DropDownList ID="ddlCardType" runat="server"
DataTextField="Text" DataValueField="Value"
DataSourceID="xdsCardTypes" AppendDataBoundItems="True">
<asp:ListItem Text="(select)" Value=""></asp:ListItem>
</asp:DropDownList>
<asp:XmlDataSource ID="xdsCardTypes" runat="server"
DataFile="~/App_Data/PaymentCards.xml">
</asp:XmlDataSource>
Here's the XML doc:
<?xml version="1.0" encoding="utf-8" ?>
<PaymentCards>
<PaymentCard Text="American Express" Value="AmericanExpress" RegEx="3[47](\d{13})" />
<PaymentCard Text="MasterCard" Value="MasterCard" RegEx="5[1-5](\d{14})" />
<PaymentCard Text="Maestro" Value="Maestro" RegEx="(5018|5020|5038|6304|6759|6761)\d{8,15}" />
<PaymentCard Text="Visa" Value="Visa" RegEx="4(\d{15}|\d{12})" />
</PaymentCards>
In code-behind I'm creating a JavaScript function call and adding it to the onchange event of the DropDownList:
XDocument paymentCards = XDocument.Parse(xdsCardTypes.GetXmlDocument().InnerXml, LoadOptions.None);
List<string> regExes = paymentCards.Descendants("PaymentCard")
.Select(pc => pc.GetAttribute("RegEx").Value).ToList();
string setRegExValidatorScript = string.Format("setRegExValidator('{0}', '{1}', {2});",
ddlCardType.ClientID,
txtCardNumber_RegularExpressionValidator.ClientID,
regExes.ToJavaScriptArgumentList());
ddlCardType.AddAttribute("onchange", setRegExValidatorScript);
And in a referenced .js file I have the following:
function setRegExValidator(ddlCardTypeID, regExValidatorID, regExes)
{
var regEx = regExes[$get(ddlCardTypeID).selectedIndex];
var val = $get(regExValidatorID);
// TODO: Set the ValidationExpression!
}
So my one missing link is the ability to set the ValidationExpression from JavaScript. Yes I could use a postback to achieve this, but that seems unnecessary.
(Suggestions on an alternate approach are also welcomed!)
function setRegExValidator(ddlCardTypeID, regExValidatorID, regExes)
{
var regEx = regExes[$get(ddlCardTypeID).selectedIndex];
var val = $get(regExValidatorID);
val['validationexpression'] = regEx;
}
NB: You need to ensure that the card number is validated properly on the server-side too.

Resources