9

How to check if ContentPlaceHolder is absolutely empty?

In the ContentPlaceHolder have text only, without tags and controls.

Example Page.Master:

<asp:ContentPlaceHolder runat="server" ID="Content" />

Example Test.aspx:

<asp:Content runat="server" ContentPlaceHolderID="Content">
    Custom text without controls. Content.Controls.Count is 0 and Content.HasControls is false.
</asp:Content>

What I need to do is that when the placeholder is empty put a default content is in another control.

Overwrite tried twice for the same placeholder but I get error when dynamic load.

e-info128
  • 3,727
  • 10
  • 40
  • 57

5 Answers5

5

You can implement a method that will render the content control into a string, then check the string to find wheahter it contains any non-white space chars:

private bool HasContent(Control ctrl)
{
    var sb = new System.Text.StringBuilder();
    using (var sw = new System.IO.StringWriter(sb)) 
    {
        using(var tw = new HtmlTextWriter(sw))
        {
            ctrl.RenderControl(tw);
        }
    }

    var output = sb.ToString().Trim();

    return !String.IsNullOrEmpty(output);
}

protected void Page_PreRender(object sender, EventArgs e)
{
    var placeholder = Master.FindControl("FeaturedContent");
    var hasContent = HasContent(placeholder);
}
defrost
  • 396
  • 2
  • 6
  • Controls.Count is 0 but have text, is not empty. – e-info128 Aug 20 '13 at 21:55
  • Sorry, a templated server control does not convert markup from the .aspx page into a server control. I've updated my answer with one possible approach you can use: render the content control in a string builder and check if there are any non-white space characters in it. – defrost Aug 20 '13 at 22:27
1

You need to find the ContentPLaceHolder on the master page first. Then you can cast the first control(which always exists) to LiteralControl and use it's Text property.

So this works as expected from Page_Load of the content-page:

protected void Page_Load(object sender, EventArgs e)
{
    var cph = Page.Master.FindControl("Content") as ContentPlaceHolder;
    if (contentPlaceHolder != null)
    {
        string textualContent = ((LiteralControl) cph.Controls[0]).Text;
        if (string.IsNullOrEmpty(textualContent))
        {
            // ...
        }
    }
}
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • Visual Studio says: ArgumentOutOfRangeException. My code is: string textualContent = ((LiteralControl) CustomHeader.Controls[0]).Text; if(string.IsNullOrEmpty(textualContent)) { /* Carga la cabecera por defecto */ UserControl head = (UserControl)Page.LoadControl("~/layout/Themes/" + config.themeSelected + "/header.ascx"); CustomHeader.Controls.Add(head); } – e-info128 Aug 20 '13 at 21:52
  • Same problem, break in string textualContent = ((LiteralControl)cph.Controls[0]).Text; with same error. When overide contents says: La colección de controles no puede modificarse porque el control contiene bloques de código (por ej. <% ... %>). – e-info128 Aug 20 '13 at 22:07
  • @WHK: I have tested it with your aspx and master code. It shows `Custom text without controls. Content.Controls.Count is 0 and Content.HasControls is false.` as `textualContent`. – Tim Schmelter Aug 20 '13 at 22:07
  • Yes, i use visual studio 2012, asp in c# and not use mvc. – e-info128 Aug 20 '13 at 22:08
  • @WHK: I'm using Visual Studio 2010, however, that doesnt matter. So im not sure why it works for me and not for you. If a control like `PlaceHolder` or `ContentPlaceHolder` contains no controls but text ASP.NET injets automatically an "invisible" control of type `LiteralControl` to it which `Text` property contains the text. Here is my answer on a similar question: http://stackoverflow.com/a/14322105/284240 – Tim Schmelter Aug 20 '13 at 22:12
  • Btw, why have you used the ID `CustomHeader` and showed `Content` in your code above? Also, you haven't used my code to get the reference of the `ContentPlaceHolder` of the master-page(`Page.Master.FindControl...`). Have you used the debugger to check what happens? – Tim Schmelter Aug 20 '13 at 22:29
1

This seems to have changed, because I am seeing in 4.5 that HasControls DOES return true when there is only literal text in the Content, even a single whitespace. I do something like this in my master page:

<asp:Panel id="SidebarPanel" CssClass="Sidebar" runat="server">
    <asp:ContentPlaceHolder id="SidebarContent" runat="server" />
</asp:Panel>

Sub Page_Load(...)
    SidebarPanel.Visible = SidebarContent.HasControls
End Sub

This renders the sidebar content, if there is any, inside a <div class="Sidebar"> -- and avoids creating an empty div on the page when there's no content.

Dana
  • 634
  • 7
  • 12
1

I really didn't want to run all the code for a render or risk that maybe some controls might have states that change after being rendered. So I came up with another approach.

public static int ChildrenCount(ContentPlaceHolder placeholder)
{
    int total = 0;
    total += placeholder.Controls.OfType<Control>().Where(x => 
        (!(x is ContentPlaceHolder) && !(x is LiteralControl)) ||
        (x is LiteralControl && !string.IsNullOrWhiteSpace(((LiteralControl)x).Text))
    ).Count();
    foreach (var child in placeholder.Controls.OfType<ContentPlaceHolder>())
        total += ChildrenCount(child);
    return total;
}

For me the text I'd place directly into a Content element would be returned by OfType as a LiteralControl with the appropriate contents. Not only this but my formatting ("\r\n\t") would also be returned the same way. I'd also get ContentPlaceholders for subsequent master pages as they passed the slot in my web pages to the next master page or actual page.

So the task now is to get a count of controls that excludes these ContentPlaceholders and also excludes LiteralControls which are whitespace. This is pretty easy using the is operator. We'll just make sure a given control is neither of those types and then count it, or if it is a Literal we check if the contents are all whitespace or not. The last step is to recursively add the results of the same operation for all child ContentPlaceholders so nested master pages work as expected.

And then finally:

if (ChildrenCount(MyContentPlaceholder) == 0)
    MyContentPlaceholder.Controls.Add(new LiteralControl("My default content!"));
Licht
  • 1,079
  • 1
  • 12
  • 27
-1

My 2 cents:

If it's a constant content you'll have to insert AND there will be no <Content> at all:

<asp:ContentPlaceHolder>
   <!-- Anything here will be inserted if there's no Content -->
</asp:ContentPlaceHolder>
ispiro
  • 26,556
  • 38
  • 136
  • 291