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.
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);
}
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))
{
// ...
}
}
}
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.
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!"));
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>