Question

I am building a database editor/line-of-business tool for a client using .NET 4.0, and IIS 7. I want to conditionally include some HTML in my page based on values I have stored in Session. I am doing this on the server-side, and to encapsulate the code, I created an ASP Server Control.

As you will see, the markup generated by the Server Control is not what I expected. I am hoping someone out there has seen this before and can help me understand how to control the markup generation output.

Here's the new RenderContents for a control called MyList. It is supposed to generate new list entries using the <li> tag.

protected override void RenderContents(HtmlTextWriter output) {
    output.RenderBeginTag(HtmlTextWriterTag.Li);
    output.WriteEncodedText(this.Text);
    output.RenderEndTag();
}

After compiling the main project, and adding a reference to MyList, I use MyList in the following HTML:

<h1>Favorite Things</h1>
<ul>
    <cc1:MyList ID="mL1" runat="server" Text="Code that works!" />
    <cc1:MyList ID="mL2" runat="server" Text="Going home before 8" />
    <cc1:MyList ID="mL3" runat="server" Text="Cold drinks in fridge" />
</ul>

And it generates the following:

<h1>Favorite Things</h1>
<ul>
    <span id="MainContent_mL1"><li>Code that works!</li></span>
    <span id="MainContent_mL2"><li>Going home before 8</li></span>
    <span id="MainContent_mL3"><li>Cold drinks in fridge</li></span>
</ul>

Now I add a test based on a Session value. The Page property of WebControl provides me a reference to the control's container, and thus access to my Session:

protected override void RenderContents(HtmlTextWriter output) {
    string backcolor = "Yellow";
    if (this.Page.Session["access"] == null) {
        backcolor = "Red";
    }
    output.RenderBeginTag(HtmlTextWriterTag.Li);
    output.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor, backcolor);
    output.WriteEncodedText(this.Text);
    output.RenderEndTag();
}

Now the markup begins to unravel. Note the inconsistency in 'mL1':

<h1>Favorite Things</h1>
<ul>
    <span id="MainContent_mL1"><li>Code that works!</li></span>
    <span id="MainContent_mL2" style="background-color:Red;"><li>Going home before 8</li></span>
    <span id="MainContent_mL3" style="background-color:Red;"><li>Cold drinks in fridge</li></span>
</ul>

In my real code, which is more complex, the markup turned into just the span tags. And, when I set breakpoints in RenderContents() it only got called once when I had five tags in a row.

Other info: The page with cc1:MyList controls has EnableSession=true. My web.config specifies the normal Session manager ('rml', and 'RoleBasedList' refer to my 'real' control, which I simplified to isolate the problem and make this post shorter):

<system.web>
    <trace enabled="true" localOnly="false" pageOutput="true" requestLimit="20" />
    <compilation debug="true" targetFramework="4.0" />
    <pages>
        <controls>
            <add tagPrefix="rml" assembly="RoleBasedList" namespace="SOTS.ServerControls"/>
        </controls>
    </pages>
    <httpModules>
      <add name="Session" type="System.Web.SessionState.SessionStateModule"/>
    </httpModules>

    <sessionState mode="InProc" cookieless="false" timeout="60"/>
    ...
</system.web>

And now you know everything I do!

Was it helpful?

Solution

You just need to override the WebControl.TagKey property in your custom server control:

protected override HtmlTextWriterTag TagKey
{
    get { return HtmlTextWriterTag.Li; }
}

The default value is Span, which explains what you're seeing. Of course, if you do this, you don't render the Li tag in your RenderContents override.

An alternative would be to override Render, to take full control of the rendering, or to derive from Control instead. But you'll lose some of the features of WebControl, notably styles for your outer tag.

Regarding your second problem, you are calling:

output.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor, backcolor);

which adds a style attribute to be applied to the next call to RenderBeginTag. You don't call RenderBeginTag, so this is applied to a tag in the next control in the tree.

Another point is Session will be null when your control is rendered in the Visual Studio designer. You should check for null, or alternatively check if you're running in design mode:

if ((Site != null) && (Site.DesignMode))
{
    ... running in design mode, Session won't be available
}

OTHER TIPS

Avoid System.Web.UI.WebControls. I suggest you subclass System.Web.UI.Control directly.

The <span> is being inserted by the class you're deriving from (which you haven't mentioned). Note that you're overriding RenderContents instead of Render, which means that the control superclass is free to wrap the output of RenderContents with whatever it wishes, in this case <span>.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top