Sunday, June 8, 2008, 7:05:00 AM
If you need to do a global change to each control inside of a Repeater template, you can just reference the control name through the page. This can be handy for things like displaying optional links based on some expression.
For example, in the WebLog example, there's an option to remove comments and posts based on whether the user visiting the page is an administrator by displaying an optional Remove Comment/Entry link. If the user is an admin, he sees the remove links otherwise, the typical visitor only sees the actual entries without the remove link.
The relevant code from the wwWebRepeater template looks like this:
<ww:wwWebRepeater runat="server" ID="repComments">
… more omitted content
<%= DisplayMemo( FixHtmlForDisplay( TComments.Body ) ) %>
<ww:wwWebHyperlink runat="Server" ID="hypRemove"
Text="Remove Comment" PreHtml="<br />"/>
Note that there's no conditional markup here to tell whether to display the Remove link or not. Instead the code behind code can simply force the hyperlink globally to not render for the typical user by setting the hyperlink's visibility to false anywhere in code behind:
*** Show the remove button only if logged in as Admin
*** this affects all hypRemove items in the Repeater template
this.hypRemove.Visible = .F.
This behavior might seem a little unintuitive at first – after all the hyperlink is part of a template and acts like a child of that template and the repeater. But if you take a closer look at how controls are generated by the parser you'll quickly see that each control – top level or child alike - is generated as a property of the Page object. Here's the relevant declaration in the generated code for the repeater:
THIS.repComments = CREATEOBJECT("wwwebrepeater",THIS.Page,"repComments")
THIS.CTL0013 = CREATEOBJECT("wwWebitemtemplate",THIS.Page,"CTL0013")
THIS.CTL0013.UniqueId = [repComments_CTL0013]
… omitted code
CTL0026 = CREATEOBJECT("EvalControl",THIS.Page,"CTL0026")
CTL0026.EvalExpression = [ DisplayMemo( FixHtmlForDisplay( TComments.Body ) ) ]
CTL0026.UniqueId = [repComments_CTL0013_CTL0026]
__lcHtml = 
__lcHtml = __lcHtml + + CRLF +;
CTL0027 = CREATEOBJECT("wwWebLiteral",THIS.Page,"CTL0027")
CTL0027.Text = __lcHtml
CTL0027.UniqueId = [repComments_CTL0013_CTL0027]
THIS.hypRemove = CREATEOBJECT("wwwebhyperlink",THIS.Page,"hypRemove")
THIS.hypRemove.UrlControlSource = ['WeblogComments.blog?Action=Delete&id=' + TRANS(TComments.Pk)]
THIS.hypRemove.text = [Remove Comment]
THIS.hypRemove.PreHtml = [<br />]
THIS.hypRemove.UniqueId = [repComments_CTL0013_hypRemove]
You'll notice that each of the controls are created as children of THIS which is the Page object, so you can directly reference the controls through the page.
Repeater Template items render inside of the template but the template effectively reuses the same items over and over. Unlike .NET which actually creates list items for each repeater item, Web Connection only uses one template with one control each and simply rebinds the template to each new item for efficiency (it'd be way to slow and problematic for cleanup to keep the templates loaded in memory before and after rendering). So if you set a property on any of the embedded template controls that value effectively is applied throughout the rendering of templates.
Along the same lines you can cause a UDF() or a Page class method that's called via a ControlSource binding expression or via <%= %> expressions to change a value of a template control and effectively change the value for all subsequent template renderings. This can be useful for doing things like grouping – for example you could change the background color of a control or the entire template based on a group value changing.
Use the Source Luke
There's a lot of flexibility in this behavior even if it might be a little odd, especially if you think of it in terms of the standard VFP Desktop forms model where there's real containership. Web forms don't have real containership but only abstract containership which simplifies management of controls on a page.
When in doubt it always helps to look at the generated source and see exactly at what level a control was created, what properties are applied and what the containership hierarchy is. It's all right there in the source code.