Is there any way to declaratively pass code-behind property values to server controls?
Asked Answered
M

3

9

Can anyone explain why you can’t use inline code blocks within server control declarations in ASP.Net?

The following is a simple example...

....
<form id="form1" runat="server">
    <asp:Label ID="Label1" runat="server" Text="<%= SomeProperty %>"></asp:Label>
</form>
....

The code block is rendered literally to the page...

<span id="Label1"><%= SomeProperty %></span>

My initial thoughts are it has to do with the order that these things are processed in the page life-cycle. The <%=...%> blocks are, as I understand it, equivalent to a Response.Write(...) in code-behind. And since the server control is not actually rendered as declared in the markup, I suppose it may not be possible to process an embedded code block before this rendering takes place.

I would be very grateful of anyone could explain that a little better.

However, the data binding code block <%#...%> is obviously different in the way it behaves, but can anyone tell me why it is possible to embed these within a server control...

....
<asp:Repeater id=Repeater1 runat="server">
    ....
    <ItemTemplate>
        <asp:Label ID="Label1" runat="server" Text='<%# Eval(“SomeProperty”) %>'></asp:Label>
    </ItemTemplate>
    ....
</asp:Repeater>
....

This works fine.

Mousse answered 13/8, 2009 at 10:29 Comment(1)
E
7

You're mostly right about the <%=...%> syntax. Here is an example of what happens under the hood:

<script runat="server">
    public string SomeProperty { get { return "Hello World!"; } }
</script>

<form id="form1" runat="server">
    <%= SomeProperty %>
    <div>
        <asp:Label ID="Label1" runat="server" Text="<%= SomeProperty %>"></asp:Label>
    </div>
</form>

This is parsed and the following C# code is created (I've simplified it a bit):

private Label @__BuildControlLabel1() 
{
    Label @__ctrl = new Label();
        
    this.Label1 = @__ctrl;
    @__ctrl.ApplyStyleSheetSkin(this);
    @__ctrl.ID = "Label1";
    @__ctrl.Text = "<%= SomeProperty %>";
    return @__ctrl;
}

private void @__Renderform1(HtmlTextWriter @__w, Control parameterContainer) 
{
    @__w.Write( SomeProperty );
    @__w.Write("\r\n    <div>\r\n        ");
    parameterContainer.Controls[0].RenderControl(@__w);
    @__w.Write("\r\n    </div>\r\n    ");
}

Here is an example of what happens under the hood for the <%#...%> syntax:

<script runat="server">
    public string SomeProperty { get { return "Hello World!"; } }
    protected void Page_Load(object sender, EventArgs e) { Label1.DataBind(); }
</script>

<form id="form1" runat="server">
    <div>
        <asp:Label ID="Label1" runat="server" Text="<%# SomeProperty %>"></asp:Label>
    </div>
</form>

Generates this code:

private Label @__BuildControlLabel1() 
{
    Label @__ctrl = new Label();
    
    this.Label1 = @__ctrl;
    @__ctrl.ApplyStyleSheetSkin(this);
    @__ctrl.ID = "Label1";
    @__ctrl.DataBinding += new System.EventHandler(this.@__DataBindingLabel1);
    return @__ctrl;
}

public void @__DataBindingLabel1(object sender, EventArgs e) 
{
    Label dataBindingExpressionBuilderTarget = ((Label)(sender));
    Page Container = ((Page)(dataBindingExpressionBuilderTarget.BindingContainer));
        
    dataBindingExpressionBuilderTarget.Text = System.Convert.ToString( SomeProperty , System.Globalization.CultureInfo.CurrentCulture);
}

As you can see the <%=...%> syntax can be used outside of a server control's properties to directly render the returned value. On the other hand the <%#...%> syntax generates a event handler for the DataBinding event of the label. This event sets the value of the label's property to the value of SomeProperty. The DataBinding event fires whenever the DataBind method is called which is why I added that call to the Page_Load event.
Hopefully this will help you understand the difference between them.

Englishry answered 14/8, 2009 at 22:13 Comment(0)
C
3

You could create a custom ExpressionBuilder so you use something like <%$ Code: SomeProperty %>

Cesspool answered 13/8, 2009 at 10:38 Comment(3)
Interesting - thanks for the reply. So the part to the right of the colon is an expression that the ExpressionBuilder would need to evaluate. Would I therefore have to use reflection to read the property from the page object? I can't see how that would work given that the ExpressionBuilder would not have a reference to the page. Am I missing something?Mousse
ExpressionBuilder's most interesting method is GetCodeExpression, for which you'd simply need to return a CodeSnippetExpression of the bit to the right of the colon (which you can get from entry.Expression.Trim())Prepossessing
Oh, and if you find this reply interesting/useful, at least give me a vote ;)Prepossessing
F
0

You can create a custom databound control, e.g.

namespace FooBar.WebControls
{
    public class DataBoundPlaceHolder:PlaceHolder
    {
        private bool hasDataBound = false;
        protected override void CreateChildControls()
        {
            if (!hasDataBound)
            {
                this.DataBind();
                hasDataBound = true;
            }
            base.CreateChildControls();
        }
    }
}

Then wrap your code in this new control, and use the <%# %> syntax, e.g.

<%@ Register TagPrefix="WebControls" Namespace="FooBar.WebControls" Assembly="FooBar" %>

<form id="form1" runat="server">
    <WebControls:DataBoundPlaceHolder runat="server">
        <asp:Label ID="Label1" runat="server" Text='<%# SomeProperty %>'></asp:Label>
    </WebControls:DataBoundPlaceHolder>
</form>
Frida answered 23/4, 2014 at 10:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.