UserControl with templates, change server-control

2019-07-31 17:39发布

问题:

TL;DR...

When creating a [UserControl] with single use templates, is it possible to get make server-side controls placed within those templates to render their id without the UserControl or container IDs?


In my ASP.NET web application I've create a UserControl with multiple single-use template (I've only given the code here for one of those templates)...

Public Class MyUserCtrl
    Inherits System.Web.UI.UserControl

    Private Class MyUserCtrlLiteralContainer
        Inherits Control
        Implements INamingContainer
    End Class

    <TemplateContainer(GetType(MyUserCtrlLiteralContainer)),
     TemplateInstance(TemplateInstance.Single),
     PersistenceMode(PersistenceMode.InnerProperty)>
    Public Property FirstTemplate As ITemplate

    Private Sub Page_Init(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Init
        If FirstTemplate IsNot Nothing Then
            Dim container As New MyUserCtrlLiteralContainer()
            FirstTemplate.InstantiateIn(container)
            plhFirst.Controls.Add(container)
        End If
    End Sub
End Class    

(Note, plhFirst is a <asp:PlaceHolder> in the mark-up)

This is an example of the control being used in a page (controlled by a Master page)...

<asp:Content runat="server" ID="content" ContentPlaceHolderID="mainContent">
  <uc1:MyUserCtrl runat="server" id="muc1">
    <FirstTemplate>
      <asp:Button runat="server" id="btnMyButton" />
    </FirstTemplate>
  </uc1:MyUserCtrl>
</asp:Content>

When the <asp:Button> is rendered to the HTML, the id of the element results in...

id="ctl00_content_muc1_ctl00_btnMyButton"

... where the second ctl00 is the ID of the MyUserCtrlLiteralContainer instance.


Is there any way to make the id of the control render as follows? (So the ID of the container is not used)

ctl00_content_muc1_btnMyButton

Or even better, is there any way to make the id of the control render as follows? (So neither the ID of the usercontrol or container is used)

ctl00_content_btnMyButton

Alternatively, is there a different way to instantiate the controls in the UserControl that would result in either of the above?

回答1:

The button id includes muc1 and ctl00 because UserControl and MyUserCtrlLiteralContainer implement INamingContainer. If you don’t want the id to include those parts, but you do want the id to include other ancestor IDs, then you can’t use those control types.

Removing ctl00: Unless you specifically need a custom container control, you can delete MyUserCtrlLiteralContainer and just instantiate the template directly in the PlaceHolder.

Removing muc1: You must rewrite your UserControl as a server control that doesn't implement INamingContainer.

Here’s a sample implementation:

<ParseChildren(True)>
Public Class MyUserCtrl
    Inherits Control

    Private plhFirst As PlaceHolder

    <TemplateInstance(TemplateInstance.Single),
     PersistenceMode(PersistenceMode.InnerProperty)>
    Public Property FirstTemplate As ITemplate

    Private Sub Page_Init(sender As Object, e As EventArgs) Handles MyBase.Init
        plhFirst = New PlaceHolder()
        Controls.Add(plhFirst)

        If FirstTemplate IsNot Nothing Then
            FirstTemplate.InstantiateIn(plhFirst)
        End If
    End Sub

    Protected Overrides Sub Render(writer As HtmlTextWriter)
        ' <div class="myUserCtrl">
        writer.AddAttribute(HtmlTextWriterAttribute.Class, "myUserCtrl")
        writer.RenderBeginTag(HtmlTextWriterTag.Div)

        ' <div class="firstControls">
        writer.AddAttribute(HtmlTextWriterAttribute.Class, "firstControls")
        writer.RenderBeginTag(HtmlTextWriterTag.Div)

        plhFirst.RenderControl(writer)

        ' </div>
        writer.RenderEndTag()

        ' </div>
        writer.RenderEndTag()
    End Sub
End Class

Note: To avoid possible ID conflicts, I intentionally leave plhFirst.ID unset.



回答2:

I don't believe you are going to be able to get the IDs to look as you wish because they were designed to show control hierarchy, ugly as they are.

You can, however use static IDs on a control, page, or entire project if you wish. Check out this handy ScottGu article from way back in 2010:

https://weblogs.asp.net/scottgu/cleaner-html-markup-with-asp-net-4-web-forms-client-ids-vs-2010-and-net-4-0-series

The section you'll be looking for is Today’s Cleaner Markup Topic: Client IDs