The Problem
When using asp.net server controls id attributes such as the following are automatically generated.
<img id="ctl00_body_ULRepeater_ctl01_LIRepeater_ctl00_PartImg" src="img.png" />
While I'm not averse to id attributes in general, I try to stay away from using these unnecessarily verbose types of names and use concise, descriptive names.
The Question
Can I stop asp.net from generating these id attributes? They look terrible, and if I generate a lot of items with a repeater or something they actually add a good bit of page weight. How do I get rid of them?
Notes
I am using asp.net 3.0 in Visual Studio 2008.
[update]
Ok, so I can subclass (ClientID is declared overridable), but this is no fun really. I can use Literal Controls everywhere. Or I can grit my teeth and bear the painfully slow rendering of my pages with nearly nothing on them.
I believe that one of the features coming in asp.net 4.0 will be the ability to better control the IDs that are generated. For now, you are going to get the name mangling for any server generated control. This is what allows asp.net to guarantee the uniqueness of your control's ID.
You can always use straight HTML markup (do not runat=server) to avoid this issue. You would be sacrificing the ease of use for a lighter weight page though.
You can hide these id attributes completely by setting each control's Id property to null at runtime, e.g.
Private Sub repeNewsletters_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles repeNewsletters.ItemDataBound
If e.Item.DataItem Is Nothing Then
Return
End If
Dim hlDetails = DirectCast(e.Item.FindControl("hlDetails"), System.Web.UI.WebControls.HyperLink)
hlDetails.ID = Nothing
end sub
As of now ControlID's are ReadOnly properties. In the upcoming release of ASP.NET Web Forms (with .NET 4.0) this will be a settable property using a number of different methods (such as static, inherit, etc.)
Related
I have an ASP webpage that has 52 custom control all maintaining ViewState by loading in the PreInit routine
It is a report generator page that depending on which report, up to 5 of the custom controls are visible but not all of them.
For instance,
The Client report does not need the Employee questions.
And the Employee report does not need the Client questions.
But both need the Date Range questions.
(and all this works perfectly)
BUT...
I would like to instead LOAD ONLY the controls that are appropriate for the report that the user is running. (Which sound to me) like I need to store information on WHICH controls to load in the ViewState.
Problem is... viewstate is not available in the PreInit routine, so I cannot use it to determine which controls to load.
My options are then to store the information on WHICH controls to load in:
SessionState.
Database
URL argument.
Something else I haven't thought of.
Each of which carries its own problems for doing what I need.
What (in your opinion) is the best practice for this.
My code (snip)
Private Sub WebForm2_PreInit(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreInit
ReportOptions.Controls.Add(UserControl1)
ReportOptions.Controls.Add(UserControl2)
ReportOptions.Controls.Add(UserControl3)
Etc...
End Sub
My HTML (snip)
<div id="ReportOptions" class="ReportOptions" runat="server"/>
Viewstate is not an option because you must reconstruct your page exactly as it was (controls and all) in order to read the view state correctly on a post back.
Session sux. (single threaded in asp.net provider)
Database is way to heavy and an overkill.
URL method is stateless and probably your best bet.
I'm finding a dropdown in an ascxcontrol on my aspx in the following way.
Dim cp As ContentPlaceHolder = DirectCast(Page.Form.FindControl("ContentPlaceHolder1"), ContentPlaceHolder)
Dim ascx As UserControl = DirectCast(cp.FindControl("drpType"), UserControl)
Dim drpType As DropDownList = DirectCast(ascx.FindControl("drpType"), DropDownList)
Is there a faster way without having to acces all the elements on the page?
I wouldn't try and reference a control within a user control this way, the user control should encapsulate these and the page should talk to public properties.
Depends on what you're trying to do.
Without a great deal of context, I can only assume that you are either getting or setting the value of the dropdown.
I wouldn't use the approach that you're going for. It introduces an element of implementation specific coupling.
You would be far better off exposing whatever you need to get/set via a property which you can call from the .aspx page.
However, in answer to your question, if you are going to reference the dropdown from the .aspx page, you will have to use FindControl.
If you know what naming containing the control is in you can go
ucNamingContainerControl.FindControl(controlId)
That will at least limit it to that section of the page.
Otherwise the only other thing I can think of is if you are accessing a predefined set of controls - put them in a Dictionary collection and use the Find method to pick them out. Could be a quicker retrieval but might look a bit clunky on the page.
In ASP.NET web forms when a control is rendered on the page, the ID of each field is modified with the ctrl01 as needed to ensure collisions don't happen (aka myField becomes ctrl01$myField or whatnot).
I was wondering where one might find details on HOW the algorithm itself works, or where it might be I can find it. I am assuming the INamingContainer has something to do with this-- but alas I cannot find the part which actually decides the rendered field name.
Any help would be awesome!
You are probably looking for this msdn article.
It's based on the hierarchical layout of the webpage. But you can control this with the ClientId property.
So a textbox in a usercontrol will be named ctrl01#textboxname (Like you said in your post)
It concatenates it's own name with your original id.
In ASP.NET 4 you can suppress this concatenation and keep your own id in three different ways:
Each server control has an attribute called clientIdMode which you can set to Static
You can also set clientIdMode in the page directive which will affect all controls on the page.
Or you can set it in the web.config to affect all controls in all pages. (Unless the page or control is set to a different clientIdMode
Note: If you are using AJAX Control Toolkit you will need to set those controls that are part of the toolkit to a clientIdMode of Predictive
Apart from the other answers, if you are using ASP .NET 4, you have much more control over it.
Take a look # these web pages
http://www.west-wind.com/weblog/posts/54760.aspx
http://weblogs.asp.net/asptest/archive/2009/01/06/asp-net-4-0-clientid-overview.aspx
.Net is kindly changing the element ids on my pages by appending a ct100_ to them. As much as I appreciate microsoft trying to help me keep from duplicating ids on my site, I think I can manage it on my own. Can anyone tell me how to prevent this?
Thanks in advance.
That's just how aspnet works. Controls provide the clientid method for you to use in your code behind for this reason.
If you want to refer to objects from js, you can either inject the clientid or use classes or other attributes.
Edit: Note that this only applies to the ASP.NET controls. If you use the HTML controls, the given IDs are preserved. You can access them in your code behind by adding the runat=server attribute to them, too. Obviously these controls break the webforms model with viewstate, etc. but they do give you your desired functionality.
Of course it's been a while since I worried about it so I could be wrong...(please comment or edit if I am!).
You cannot prevent this in the current version of ASP.NET - the next version will allow you to do this. Perhaps ASP.NET MVC is a good choice for you?
Any control which has the INamingContainer interface on it will get the control heirarchy appended to it to allow for multiple controls to be on the page without conflicting. You should be using the ClientID property of the control if you wish to know what the id of the element will be on the client.
You will need to:
override regular controls' behavior to decouple the Control.UniqueID property from the Control.ID property
override naming container controls to allow us to control how ID generation is done and how to find a control
References:
http://nunogomes.net/post/2008/06/02/ASPNET-Controls-Improving-automatic-ID-generation-Architectural-Changes-(-Part-3).aspx
http://weblogs.asp.net/nunogomes/archive/2008/06/04/asp-net-controls-improving-automatic-id-generation-architectural-changes-part-3.aspx
http://forums.asp.net/t/1394822.aspx
to not use anything on the server side.
This is an inherent aspect of the ASP.NET system, and there is no way to use .NET Server controls and not have the prefixes appended.
To access the client-side name, you can use the myControl.ClientID property.
Suppose you have 2 different ASP.NET applications in IIS. Also, you have some ASCX controls that you want to share across these 2 applications.
What's the best way to create a "user control library", so that you can use the same control implementation in the 2 applications, withuot having to duplicate code?
Controls have ASCX with HTML + code behind.
Composite controls will be difficult, because we work with designers who use the HTML syntax in the ASCX files to style the controls.
Tundey, we use SVN here. Do you have an example on how to implement your suggestion? How can SVN share the ASP.NET controls?
Thanks!
Scott Guthrie gives some great advice here on how to set up a User Control Library project, then use pre-build events to copy the user controls into multiple projects. It works really well.
http://webproject.scottgu.com/CSharp/usercontrols/usercontrols.aspx
You would need to create composite controls instead of .ASCX controls if you wanted to be able to use them in separate projects.
In addition to what Tundey said, NTFS Link shell extension is helpful when it comes to sharing a large chunk of content (e.g.: a folder with .ascx/.aspx) between otherwise independent projects. In case of code, i think making another working copy from VCS is preferable.
Have a look at this: http://www.codeproject.com/KB/aspnet/ASP2UserControlLibrary.aspx?msg=1782921
An alternative is to use your source control tool to "share" the ASCX controls between your webapps. This will allow you to make changes to the controls in either application and have the source control ensure the changes are reflected in the our webapps.
The biggest problem I've noticed with controls in ASP.Net is that you can't easily get designer support for both building the control and using the control in a site once you built it. The only way I've been able to do that is create an .ascx control with no code-behind (ie: all the server-side code is in a script tag in the .ascx file with the runat="server" attribute).
But even then, you still have to copy the .ascx file around, so if you ever need to make a change that means updating the file at every location where you've used it. So yeah, make sure it's in source control.
I managed to do this by sacrificing some of the ease of building the controls in the first place.
You can create a Control Library project that will generate a control library DLL for you. The drawback is that you have to create the controls with code only. In my last project, this was fine. In more complicated controls, this may be a problem.
Here's an example:
<DefaultProperty("Text"), ToolboxData("<{0}:BreadCrumb runat=server />")> _
Public Class BreadCrumb
WebControl
<Bindable(True)> _
Property Text() As String
'...'
End Property
Protected Overrides Sub RenderContents(output as HtmlTextWriter)
output.write(Text)
End Sub
Private Sub Page_Load(...) Handles MyBase.Load
' Setup your breadcrumb and store the HTML output '
' in the Text property '
End Sub
End Class
Anything you put in that Text property will be rendered.
Then, any controls you put in here can function just like any other control you use. Just import it into your Toolbox, make your registration reference, then plop it onto the ASP page.
I use StarTeam here and it allows you to "share" objects (files, change requests, requirements etc) across multiple folders. Not sure if Subversion (SVN) has that feature. If it doesn't, here's another trick you can use: create a junction from the primary location of the controls to a location in the other projects. A junction is just like a Unix symbolic link. You can download the tool for creating junctions in Windows from here
I have a suggestion.WE can use user control across multiples application by creating user control inside website project as normally.Then change the website property Use fixed naming and single page assemblies.Then we can use the user control dll into multiple applications.
I recently did a web application that just referenced the files (about 90 in total) from one web application (aspx, master and ascx) without too much of an issue. That said I was using a heavily modified version of the MVP pattern, a lot of interfaces and conventions to keep the complexity down, the same middle tier and one site was a subset of the other.
Big issues:
Master pages (and in turn designers and html view formatting) don’t work on a referenced file so you lose a lot of functionality. A pre-build step and a lot of svn:ignore entries was my hack around this. It was also a pain to get CruiseControl.NET to get the pre-build task to execute in the right folders.
Shared pages/controls need to be extremely aware of what they touch and reference as to avoid bringing in extra dependencies.
Both sites are locked together for deployment.
I now have to pray that the maintainer reads my pokey little document about the mess I made. It’s so far outside of what I’ve seen in ASP.NET projects.
I was under a massive time pressure to get it to work, it does and now both apps are in production. I wouldn’t recommend it but if you’re interested start at:
Add Existing Item, select some files, click on the Add button’s arrow and say Add as a Link.