freefaller
freefaller

Reputation: 19953

Accessing template-based controls in UserControl from parent page

Before I start, I am not asking how to access a server-side control hosted within a UserControl from the parent page. That's been asked many times, and this is not a duplicate of that.

This question is about server-side controls placed within a template item of a UserControl instance.


In ASP.NET I have a UserControl with multiple template handlers, which results in an HTML block being rendered with a single instance of each template (it is not like a <asp:Repeater> where a template is used multiple times). For example:

<uc1:MyUserControl runat="server" ID="myCtrl1">
  <TopControls>
    <asp:Literal runat="server" ID="litTop" />
  </TopControls>
  <BottomControls>
    <asp:Button runat="server" id="btnBottom" />
  </BottomControls>
</uc1:MyUserControl>

And the UserControl is set up something like...

<div class="myUserControl">
  <div class="topControls">
    <asp:PlaceHolder runat="server" id="plhTopControls" />
  </div>
  <div class="bottomControls">
    <asp:PlaceHolder runat="server" id="plhBottomControls" />
  </div>
</div>

The problem is that in order for the parent page to access the controls, it's necessary for me to have a method in the UserControl to find them:

Public Overrides Function FindControl(id As String) As System.Web.UI.Control
    Dim ctrl As Control = Nothing
    If Not TopControlsContainer Is Nothing Then
        ctrl = TopControlsContainer.FindControl(id)
    End If
    If ctrl Is Nothing AndAlso Not BottomControlsContainer Is Nothing Then
        ctrl = BottomControlsContainer.FindControl(id)
    End If
    If ctrl Is Nothing Then
        ctrl = MyBase.FindControl(id)
    End If
    Return ctrl
End Function

This is because the Visual Studio 2015 designer no longer sees the two server-side controls as belonging to the page, but instead the controls belong to the UserControl, and so I have to specifically declare them in the page and set them up in the Page_Load:

Protected WithEvents litTop as Literal
Protected WithEvents btnBottom as Button

litTop = myCtrl1.FindControl("litTop")
btnBottom = myCtrls.FindControl("btnBottom")

Is it possible to set up the UserControl so that the server-side controls within the templates are picked up by the designer file for the parent page, so I don't have to go through this each time I add a new UserControl or server-side control within that UserControl?

If it is not possible with a UserControl, is it possible to do it with a server-side control? (If so, what attributes are required for that?)

Upvotes: 1

Views: 725

Answers (1)

Michael Liu
Michael Liu

Reputation: 55409

In MyUserControl.ascx.vb, decorate the ITemplate properties with TemplateInstanceAttribute, specifying TemplateInstance.Single. (The default is Multiple.) From the documentation:

A single instance of a template allows you to reference controls that are contained within the template.

VB.NET:

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

<PersistenceMode(PersistenceMode.InnerProperty)>
<TemplateInstance(TemplateInstance.Single)>
Public Property BottomControls as ITemplate

C#:

[PersistenceMode(PersistenceMode.InnerProperty)]
[TemplateInstance(TemplateInstance.Single)]
public ITemplate TopControls { get; set; }

[PersistenceMode(PersistenceMode.InnerProperty)]
[TemplateInstance(TemplateInstance.Single)]
public ITemplate BottomControls { get; set; }

After you compile the code and re-save the parent page, the Visual Studio designer will generate backing fields for controls declared inside the templates.

Note: You should only specify TemplateInstance.Single if the template is instantiated exactly once.

Upvotes: 3

Related Questions