I thought RequiredFieldValidators, CustomValidators, and all other validators would live in Controls collection. Basically I have a User Control that has a number of validators in it, and I would like to get a handle on them to do stuff.
Page.Validators seem to return a list of IValidator objects, which are not what I am looking for.
Where can I get the list of all validators in a User Control?
The Page.Validators collection is your best bet; all validators implement IValidator, and in turn, BaseValidator, so you can use this do to that. Since the validator is a control, it's parent reference trail will contain a reference to the user control it lives in. It may require you doing something like:
if (validator.Parent.Parent.Parent.Parent is UserControl) { }
But that is the only way to figure that out. So a combination of Page.Validators, and checking the parent control tree is the only way, unless you look for specific validation groups that a validator implements, which also is another way (a property of BaseValidator, so you'll have to do a type check and cast.
There is one last potential option; you can override AddedControl, which executes on every control add, and check if the control is a validator, and then keep that control in a local collection like:
private List<BaseValidator> _vals = ..;
protected overrides void AddedControl(Control control, int index) {
if (control is BaseValidator)
_vals.Add((BaseValidator)control);
}
Then you would know which controls belong to that user control. I've done this with custom controls, not anything with user controls, so may not work exactly as I mentioned...
Since you're using ASP.NET validation controls you might be looking for a validation summary:
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.validationsummary%28v=vs.100%29.aspx
You can do quite a bit with it fitting most usage scenarios, here's a simple example:
http://asp-net-example.blogspot.com/2008/10/validationsummary-example-how-to-use.html
Alternatively, Page.Validators would provide direct access.
Related
I have a custom user control that contains text boxes, dropdowns, etc. I need these controls to be public so that I can go like ucEmployeeAddress.txtAddr1.Text from outside the control.
I know that I can use public properties in the control that return an instance of the control inside or use FindControl to locate my control from outside the user control, but I don't want to do that due to excess code.
If there is no way to do what I want then I will just go the public property route.
Edit: Would the person who thumbed my question down be so kind as to explain how my question shows lack of research effort, is unclear, or not useful?
You just need to expose a property in the user control:
public string Address
{
get
{
return txtAddr1.Text;
}
set
{
txtAddr1.Text = value;
}
}
Do you really need to expose the entire control ?
If its just the text property you could just expose that.
public string TitleText
{
get { return this.txtTitle.Text;}
}
If you really need the control i would suggest exposing it via a property, consumers may not even know the existance or name of the control, and nor should they care about your internal workings - using FindControl is a poor solution from outside of the control.
public TextBox TitleTextBox
{
get { return this.txtTitle;}
}
As an alternative you may be able to modify the visual studio templates to expose all your controls as public, however im not sure if this is such a great idea or how you would do it..
Well, about three hours later, I finally came upon a solution. I don't know if this is new in VS2010, but you can actually edit the user control's designer and turn all members from Protected to Public. I swear I've tried this with earlier versions of VS in the past without success, but it's apparently working for me now.
What's interesting is that the IDE has a keen sense of what parts of the designer it should and should not regenerate. For example, if you comment out the entire contents of the designer class, it will not regenerate the commented-out members. To get it to regenerate them, you have to completely delete the members that you want regenerated. What's also cool is that you can comment out the entire designer class's contents, switch back to the markup and add a server control like a textbox, and flip back to the designer to discover that it generated the member definition for only that control while the rest of the member references remain commented-out. Edit: And if you delete a control from the markup whose designer member you had modified from protected to public, it will still delete the reference from the designer.
I will note that I am also using VB.NET. I would have to assume this works with C#, as well, but cannot say for sure.
The proper way to do this is through event bubbling. This way you can keep the implementation of your controls hidden while being able to modify the properties that you chose.
This link does a good job explaining how to accomplish this.
As a side note, you should be more concerned with the elegance of your code than the amount of it.
If you take the time to implement event bubbling, for example, as opposed to exposing the control's children as public, any manipulation of the control's children is handled by that control. This makes it easy to maintain if ever the logic of manipulation were to change, and easy to implement across your entire application.
However, if you expose your control's children as public instead, you must repeat that manipulation everywhere it is used.
Therefore, the "excess code" will both improve the quality of your code and actually decrease this "excess code" you are concerned about.
i have 6 textboxes which i want to iterate.
they are however in a TD in a TR in a TABLE in a PANEL etc.
the only way i've figured out to iterate them is in this way:
this.Controls[0].Controls[3].Controls[7].Controls
that's not only errorprone, but also hard to come up with.
but this.FindControl (to find one by name) doesn't work either, does findcontrol also only search in the direct child, and not the whole hierarchie?
so basicly what i'm looking for is to iterate ALL controls in the page, no matter in which level of the hierarchie, to check if it's a textbox.
Is there a way to do that?
EDIT: i don't want to find them by their name (they are server controls so i could do that) because i would have to modify that code every time i add a textbox. By iterating the form i would not have to do that.
FindControl searches the hierarchy but it doesn't go into controls that are an INamingContainer
Any control that implements this interface creates a new namespace in which all child control ID attributes are guaranteed to be unique within an entire application. The marker provided by this interface allows unique naming of the dynamically generated server control instances within the Web server controls that support data binding. These controls include the Repeater, DataGrid, DataList, CheckBoxList, ChangePassword, LoginView, Menu, SiteMapNodeItem, and RadioButtonList controls.
Basically it defines a boundary to avoid naming collisions. Consider how hard it'd be if all your control IDs really had to be unique.
Note this information is also in the FindControl remarks. Tip: Always read the remarks.
The FindControl method can be used to access a control whose ID is not available at design time. The method searches only the page's immediate, or top-level, container; it does not recursively search for controls in naming containers contained on the page. To access controls in a subordinate naming container, call the FindControl method of that container.
By doing so you could navigate to the control you want going through only the naming containers & calling FindControl at each level i.e. FindControl("SomeNamingContainer").FindControl("AChildContainer")
That's not necessarily practical, and depending on what you're doing you really just need to get All TextBoxes.
IEnumerable<TextBox> TextBoxes(ControlCollection ctrls)
{
var texts = ctrls.OfType<TextBox>();
var children = ctrls.SelectMany(c => TextBoxes(c.Controls));
return texts.Union(children);
}
Try FindControl on the Page object
Page.FindControl(id)
Are they in a formview or something?
If you don't know the ID of the textboxes as well (i.e. they are dynamic) then a quick recursion code will help. I can post the code here if Page.FindControl does not work. Let me know,.
Here is the code
List<System.Web.UI.WebControls.TextBox> _textBoxes = new List<System.Web.UI.WebControls.TextBox>();
private void FindTextBoxes(ControlCollection cc)
{
foreach (Control c in cc)
{
if (c is System.Web.UI.WebControls.TextBox)
_textBoxes.Add(c as System.Web.UI.WebControls.TextBox);
else if (c.Controls.Count > 0)
FindTextBoxes(c.Controls);
}
}
You can call it as
FindTextBoxes(Page.Controls);
FindTextBoxes(MyTable.Controls);
_textBoxes collection will contain all the textboxes the code finds.
Please click the checkbox next to my answer if it solves your problem!
I know this is a very debated topic, and usually when you are thinking about this as a solution you might want to rethink your UI logic.
I know I can pass validation using ClientScriptManager.RegisterForEventValidation. But, I'd really like to know. Is it possible to remove event validation for a single control? Is there a way work around for this?
I'm modifying a DropDownList, from the client side after it's rendered.
The SupportsEventValidation attribute is not inherited for subclasses, so if you create a subclass of DropDownList, don't apply that attribute to the subclass, and use the subclass on your page, you will have a control that doesn't trigger event validation, even if event validation is enabled on the page.
public Class DynamicDropDownList : DropDownList
{
}
http://msdn.microsoft.com/en-us/library/system.web.ui.supportseventvalidationattribute%28v=VS.110%29.aspx
Another way to handle this:
Set the property EnableViewState="False" on the particular element.
Reasoning: If you are using JavaScript to change this value outside of ASP.NET's control, then ViewState is just getting in your way at that point. Stop using it for that control.
The short answer to your question is: No, there is no way to do this. You must disable EventValidation for the entire page.
There are various workarounds... If you really don't want to disable EventValidation, store the selected value in a hidden field and restore the state of the DropDownList (or maybe just clear selection?).
If you need all the values added client-side, you have to send those up using some other method anyway, because they will not be present in the server-side control. Those values are not posted!
I found it easier to replace the control with the HTML equivalent with runat="server", you can then retrieve the value the of old fashion way with Request.Forms["id"]. There will be no validation done, so be careful on storing or processing the data.
The other option is to override the Render on the page and use Page.ClientScript.RegisterForEventValidation with all the possible answers (not nice).
something like this
protected override void Render(HtmlTextWriter writer)
{
this.Page.ClientScript.RegisterForEventValidation(ddlRisk.ID, "a");
this.Page.ClientScript.RegisterForEventValidation(ddlRisk.ID, "b");
this.Page.ClientScript.RegisterForEventValidation(ddlRisk.ID, "c");
base.Render(writer);
}
My ASP.NET WebForm has a place holder on it and user controls are dynamically added to it. The controls mostly have textboxes. Sometimes there are two user controls, sometimes there are ten. Everything is working as expected. My question is about how to implement a new feature.
My customer has a new request to copy data from the first control to another control checking a checkbox associated with the additional control in question.
At first, this sounded easy... Add a checkbox to the placeholder for each user control and then write some JavaScript to copy the data from the first control to the additional control. Then I realized that by those textboxes being in a user control, I don't really have access to modify the HTML inputs directly.
Next I started thinking about adding checkboxes that will automatically post back, but how do I go about dynamically adding a checkbox to a placeholder, and then come up with a way to add event handler to the checkbox and pass in the information necessary to loop through the controls and copy the values. This approach seems way too complicated with too much overhead to accomplish my goal.
Any suggestions?
You mentioned that since the checkboxes are in a user control, you don't have access to them.
Could you expose the ClientIDs using a property of the user control and then work with them in javascript? Something like this:
user_control {
int checkboxId { get { return checkbox.ClientId; } }
}
If you have more code that would be helpful...
This is probably too late to help you, but just so another answer is out there...
Including the checkbox as a part of the user control simplifies the issue considerably.
I had a similar situation, with maybe 10-15 UI controls in a user control, with a checkbox associated with the first one which, when checked, meant that I should copy the info from the first user control to all of the others.
Since it was all built in the codebehind, I simply exposed a boolean property of the user control named ShowCheckBox, which toggled the visibility of the checkbox. I set this to true in the first one, and false in all of the others. Thus, I knew that the event could only be raised by a click of the first user control's checkbox. Then, in the event handler for the checkbox, I handled the copying from the first user control to all of the others. (By the way, be sure to set AutoPostBack=true on that checkbox or you'll wonder why the event isn't firing.)
Javascript would definitely provide a better user experience, but this worked for me and didn't require me to figure out how to get the ClientId values into the javascript. (Although that's exactly what I need to do now, which is how I stumbled upon this question. :-) )
I've created some Web User Controls (.ascx files) and dropped them into Pages. The user controls have some TextBoxes in them that I'd like the Page to be able to access directly.
What's the easiest (read: least time) way to expose these TextBoxes in the user controls to the Pages containing the user controls?
The two options I know are calling myUserControl.FindControl(id) from the Pages (does this even work from the Page?), and writing properties in the user controls to expose the TextBox values.
Neither seem ideal. FindControl() requires the Page know the IDs of the TextBoxes in the user controls, thereby breaking encapsulation and adding hard-coded strings, and writing a bunch of properties in the user controls will be very time consuming given the number of TextBoxes in these user controls.
There's no way to declare these TextBoxes in my user controls be public instead of protected?
(Setting aside the obvious comments about the fact that what you're describing is essentially the opposite of best practice...)
If you are using a Web Application type project, you should have a designer.cs file for each UserControl. That contains the declaration of each child control, so you can change the access modifier to public.
If you are using a Web Site type project, you should probably convert to Web Application. According to Microsoft (and backed up by experience), the Web Site type is not intended for use when you plan to write extensive code which spans beyond a single code-behind.
If I have to do this I will write a public property that exposes the controls. However there is usually a way to rewrite the information such that you don't need to expose the internal controls. If all you need is the value, create a property that returns the value. If you need client ids, perhaps creating a client object that exposes values or events will solve the issue. Remember, once you make it public, you are exposes a contract and changing that contract will almost always be painful.
You can expose it as a property from the code behind. You'll really only be able to access it's properties from code, not from the ASP.Net designer though. This is an example in vb that exposes a DropDownList on a user control (and, it may not be a best practice but it certainly beats writing code to expose every property on the child controls):
''' <summary>
''' This is the exposed DropDownList for this control.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property DrowDownList() As DropDownList
Get
Return ddControlList
End Get
End Property
Why not give your user control a collection of TextBoxes, expose the collection as a property, and in your Init() method, just add all your textboxes to the collection?
That way, you have a collection that you can search by ID if you ever need to, without relying on any page logic, and you only have to expose one property. If you make your own collection class for it, you could even program in a couple of handy methods for accessing the textboxes the way you need to.
Derive all of your Web User Controls from a common base class and expose this functionality as a public method (or methods). Your base class can derive from UserControl (the way an .ascx normally would) and your controls in turn derive from it.
Then, even if using reflection seems like a bit of work, you're only doing it once. If the textboxes are dynamic and you want to avoid hardcoding things, this would be the way to go.
So, if you need to just get the value of the text box by id from a parent, you can add something like the following to your base class:
public string GetTextboxValue(string id)
{
string textValue = string.Empty;
Control ctl = FindControl(id);
if (ctl.GetType() == typeof(TextBox))
textValue = ((TextBox)ctl).Text;
return textValue;
}
I'd say to go this route vs. the previous suggestion of making the TextBox public since you really only want to read the text value from the parent and not expose the entire object as read/write.