user110714
user110714

Reputation:

Self closing Html Generic Control?

I am writing a bit of code to add a link tag to the head tag in the code behind... i.e.

HtmlGenericControl css = new HtmlGenericControl("link");

css.Attributes["rel"] = "Stylesheet";
css.Attributes["type"] = "text/css";
css.Attributes["href"] = String.Format("/Assets/CSS/{0}", cssFile);

to try and achieve something like...

<link rel="Stylesheet" type="text/css" href="/CSS/Blah.css" />

I am using the HtmlGenericControl to achieve this... the issue I am having is that the control ultimatly gets rendered as...

<link rel="Stylesheet" type="text/css" href="/CSS/Blah.css"></link>

I cant seem to find what I am missing to not render the additional </link>, I assumed it should be a property on the object.

Am I missing something or is this just not possible with this control?

Upvotes: 10

Views: 6100

Answers (5)

Bucket
Bucket

Reputation: 651

The slightly hacky way.

  • Put the control inside a PlaceHolder element.
  • In the code behind hijack the render method of the PlaceHolder.
  • Render the PlaceHolders content exactly as you wish.

This is page / control specific and does not require any overrides. So it has minimal impact on the rest of your system.

<asp:PlaceHolder ID="myPlaceHolder" runat="server">
    <hr id="someElement" runat="server" />
</asp:PlaceHolder>

protected void Page_Init(object sender, EventArgs e)
{
    myPlaceHolder.SetRenderMethodDelegate(ClosingRenderMethod);
}

protected void ClosingRenderMethod(HtmlTextWriter output, Control container)
{
    var voidTags = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase) { "br", "hr", "link", "img" };
    foreach (Control child in container.Controls)
    {
        var generic = child as HtmlGenericControl;
        if (generic != null && voidTags.Contains(generic.TagName))
        {                
            output.WriteBeginTag(generic.TagName);
            output.WriteAttribute("id", generic.ClientID);
            generic.Attributes.Render(output);
            output.Write(HtmlTextWriter.SelfClosingTagEnd);                
        }
        else
        {
            child.RenderControl(output);
        }
    }
}

Upvotes: 0

While trying to write a workaround for umbraco.library:RegisterStyleSheetFile(string key, string url) I ended up with the same question as the OP and found the following.

According to the specs, the link tag is a void element. It cannot have any content, but can be self closing. The W3C validator did not validate <link></link> as correct html5.

Apparently

HtmlGenericControl css = new HtmlGenericControl("link");

is rendered by default as <link></link>. Using the specific control for the link tag solved my problem:

HtmlLink css = new HtmlLink();

It produces the mark-up <link/> which was validated as correct xhtml and html5.

In addition to link, System.Web.UI.HtmlControls contains classes for other void element controls, such as img, input and meta.

Upvotes: 9

Tony McCreath
Tony McCreath

Reputation: 3409

I just created a solution for this, based on Ragaraths comments in another forum:

http://forums.asp.net/p/1537143/3737667.aspx

Override the HtmlGenericControl with this

protected override void Render(HtmlTextWriter writer)
{
    if (this.Controls.Count > 0)
        base.Render(writer); // render in normal way
    else
    {
        writer.Write(HtmlTextWriter.TagLeftChar + this.TagName); // render opening tag
        Attributes.Render(writer); // Add the attributes.  
        writer.Write(HtmlTextWriter.SelfClosingTagEnd); // render closing tag   
    }

    writer.Write(Environment.NewLine); // make it one per line
}

Upvotes: 0

Siewers
Siewers

Reputation: 22836

Alternatively you can use Page.ParseControl(string), which gives you a control with the same contents as the string you pass.

I'm actually doing this exact same thing in my current project. Of course it requires a reference to the current page, (the handler), but that shouldn't pose any problems.

The only caveat in this method, as I see it, is that you don't get any "OO"-approach for creating your control (eg. control.Attributes.Add("href", theValue") etc.)

Upvotes: 1

Phil Jenkins
Phil Jenkins

Reputation: 4426

I think you'd have to derive from HtmlGenericControl, and override the Render method.

You'll then be able to write out the "/>" yourself (or you can use HtmlTextWriter's SelfClosingTagEnd constant).

Edit: Here's an example (in VB)

Upvotes: 11

Related Questions