How to programmatically add stuff to contentPlaceHolder? - c#

I have a master page and all of my pages are inheriting it.
For formatting, I thought to place the content that differs from one page to another in a ContentPlaceHolder.
Now, how can I insert everything into that? Since I am planning to populate the ContentPlaceHolder with stuff from a database I suppose I will have to do it programmatically.
How can I add controls to ContentPlace Holder?
I checked other answers, but I cannot access it by its ID.
Should I use multiple ContentPlaceHolders from the beginning? Let's say I want to put movies. Should there be only one with all the images and descriptions and ratings, ore one ContentPlaceHolder for each thing?
I am opened to other solutions, as I have no experience with ASP.

Old question... but I just ran into this issue and this was the #1 post that kept coming up on Google, so figure I'd add my answer since the others didn't work in my case.
Here is how I did it when a regular <asp:Content wouldn't work (though in normal use, the answer #JayC is how you do it):
MasterPage has this ContentPlaceHolder:
<asp:ContentPlaceHolder ID="ScriptsPlace" runat="server"></asp:ContentPlaceHolder>
Had to dynamically add some JavaScript from a User Control. Trying to use the ContentPlaceHolder directly gives this error:
Parser Error Message: Content controls have to be top-level controls
in a content page or a nested master page that references a master
page.
So I wanted to add the script from the code-behind. Here is the Page Load for the .ascx file:
protected void Page_Load(object sender, EventArgs e)
{
ContentPlaceHolder c = Page.Master.FindControl("ScriptsPlace") as ContentPlaceHolder;
if (c != null)
{
LiteralControl l = new LiteralControl();
l.Text="<script type=\"text/javascript\">$(document).ready(function () {js stuff;});</script>";
c.Controls.Add(l);
}
}
UPDATE: So it turns out I had to use this in more places than I expected, and ended up using a way that was much more flexible / readable. In the user control itself, I just wrapped the javascript and anything else that needed to be moved with a regular div.
<div id="_jsDiv" runat="server">
$(document).ready(function() {
//js stuff
});
Other server controls or HTML junk
</div>
And then the code behind will find that div, and then move it into the ContentPlaceHolder.
protected void Page_Load(object sender, EventArgs e)
{
ContentPlaceHolder c = Page.Master.FindControl("ScriptsPlace") as ContentPlaceHolder;
HtmlGenericCOntrol jsDiv = this.FindControl("_jsDiv") as HtmlGenericControl;
if (c != null && jsDiv != null)
{
c.Controls.Add(jsDiv);
}
}
I actually put this code in a custom user control, and I just have my regular user controls inherit from the custom user control, so once I wrap the javascript/etc with a <div id="_jsDiv" runat="server">, the custom user control takes care of the rest and I don't have to do anything in the code behind of the user control.

What normally happens is
you set up your master pages with the proper html and ContentPlaceHolders
you create pages based off that master page. If you use Visual Studio, and tell it to create a new page based upon a existing Master page, it will add the Content areas for you.
you add things to the Content areas in the newly created page.
If you want to dynamically add controls to the master (or any) page, you could add controls to any existing control. If it shouldn't be wrapped in any way, just add a Placeholder (it is an asp.net control).

I did like this
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
<asp:Literal ID="jsstuff" runat="server"></asp:Literal>
</asp:Content>
And this went into code behind:
string stuff = #"<script type=""text/javascript"">
var searchBox = 0;
var currentCountry = '';
</script>";
jsstuff.Text = stuff;

If the namespace for content Page and Master page is not same then the content page control not accessible in Codebehind in content page.
Also, check your designer files. if the control not listed in designer file then delete the file and recreate (project->convert to web application)

Related

Theme + dynamic control creation + Invisible UserControl = view state error?

default2.aspx
<%# Page Language="C#" AutoEventWireup="true" Theme="Blue" %>
<%# Register TagPrefix="uc1" TagName="favicon" Src="~/FavIcon.ascx" %>
<!DOCTYPE html>
<script runat="server">
private void Page_PreRender(object sender, System.EventArgs e)
{
HtmlGenericControl scriptControl = new HtmlGenericControl("script");
Page.Header.Controls.AddAt(0, scriptControl);//If this line is commented out, no exception will occur.
}
private void Page_Init(object sender, System.EventArgs e)
{
ScriptManager oSM = new ScriptManager();
Page.Form.Controls.Add(oSM);//If this line is commented out, no exception will occur.
}
</script>
<html>
<head runat="server">
<title></title>
<uc1:favicon runat="server"></uc1:favicon>
</head>
<body>
<form id="form1" runat="server">
<asp:Button ID="Button1" runat="server" Text="Button" />
</form>
</body>
</html>
FavIcon.ascx
<%# Control Language="C#" ClassName="FavIcon" AutoEventWireup="true" %>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
this.Visible = false;//If this line is commented out, no exception will occur.
}
</script>
Also add a stylesheet to the Blue theme.
The page opens successfully, but once I click the button, it throws exception
Failed to load viewstate. The control tree into which viewstate is
being loaded must match the control tree that was used to save
viewstate during the previous request. For example, when adding
controls dynamically, the controls added during a post-back must match
the type and position of the controls added during the initial
request.
Can anyone explain why this error happens?
To debug these errors, I strongly recommend to enable traces (Trace="true" in the #page directive, or use a global setting in web.config) so you can see the server's control tree.
On a GET, the control tree is this (note the unique IDs that were generated automatically):
HtmlHead
+ ctl05 : HtmlGenericControl (from your PreRender code)
+ ctl01 : Title (the <title> tag)
+ ctl02 : FavIcon (your favicon.ascx)
+ ctl04 : HtmlLink (from the 'Blue' theme)
HtmlForm
+ ctl03 : ScriptManager
The root causes of the 'Failed To Load ViewState' error are:
you use the ViewState. This is an option that in many cases can be disabled.
you don't use fixed IDs (that's why the Visual Studio's IDE adds automatic but fixed IDs to controls all the time).
you add controls dynamically, late, in the tree, as it changes the head's control tree. Plus, you add it before others (AddAt).
The generated IDs follow ASP.NET's lifecycle order:
Title (static)
FavIcon (static)
ScriptManager (Init)
Theme (Init, after overrides)
Dynamic controls (After Init in this case)
The ASP.NET ViewState engine is a tree serializer/deserializer. Each node in the tree has it's IDs, plus a "full ID" composed of its parent's IDs and its own ID. Of course, as soon as you change IDs between serialization and deserialization, all bets are off, the engine will detect it and raise the 'Failed to Load ViewState' error.
So, if you put Visible=false, you'll use the ViewState. If you remove it, you won't. When you don't use the ViewState yourself, there are less chances to get the 'Failed to load viewstate' error, but ASP.NET can use some ViewState on your behalf when you use all its features (Theme, etc.). In the case of Visible, it just means the control is there (and uses ViewState), but not rendered (it's Render Size is 0). But you'll see the problem with any other property that use the ViewState, it's not specific to the Visible property (you could also try this.ViewState["test"] = "whatever" ).
If you put <uc1:favicon runat="server"></uc1:favicon> elsewhere in the page, it will also work because it won't be in the Head control tree anymore, and won't interfere with Theme's link or your dynamic control.
For Theme and ScriptManager, it's just that when you play with them, it changes, or not, the IDs, and the system may detect it or not.
There is an infinite number of ways to break the ViewState. What's really difficult is it may seem to work when it shouldn't (for example as long as you don't use FavIcon's ViewState, you don't notice there is an issue with your code).
It happens due to the nature of ViewState. Briefly, when your page is being posted back a control into which ViewState is restored is matched by the control index, so when the index changes it causes this problem.
In this case, setting the property this way ViewStateMode="Disabled" for your page or changing the order you add your dynamic elements Page.Header.Controls.AddAt(Page.Header.Controls.Count, scriptControl), which lets you not interfere with the order of restored elements, can help you cope with the trouble. At least, it works out for me.

Page_Load firing when following link to another page asp.net

I'm fairly new to ASP.NET, I've been reading a few questions related to this but I'm still unable to figure out what's wrong with my code, I have a default.aspx page with a menu on top created using a list (ul and li items) and putting the <a href=""> tag to create the links to other pages but after following a link to another page, the Page_Load event fires before leaving the page, I understand this would be the expected behavior with Response.Redirect, but I don't know how to avoid this using tags (if possible), this is the markup I'm using for the Default.aspx page:
<ul id="lista">
<li><strong>Inicio</strong></li>
<li><strong>Item</strong></li>
<li><strong>IK</strong></li>
<li><strong>Acerca de</strong></li>
</ul>
And this is the code behind I have for Page_Load:
protected void Page_Load(object sender, System.EventArgs e)
{
if (!IsPostBack)
{
ExcelUtility excel = new ExcelUtility();
dtDefault = excel.LeerExcel();
gridResults.DataSource = dtDefault;
gridResults.DataBind();
gridResults.VirtualItemCount = dtDefault.Rows.Count;
}
}
Basically, what I want to do is to follow the link to some other page without loading the default page before leaving, hope I make myself clear!
Edit: The root cause of this was having the default <form runat="server"> tag at the beginning of the body section, this was causing the Page_Load event firing again in the same page once the links were being clicked, placing the hyperlinks outside of the form tag did the trick.
Your HTML code should be inside some HTML tag or custom ASP control which contains the attribute runat="server". This is supposed to fire a PostBack request to the server.

how can I wrap a control with a div from the code behind in asp.net?

Is it possible to add a div tag that can wrap a control from the behind? I want to send a list of controls to my function and have it dynamically add a div around each one, so that each control is within its own div.
Example:
html:
...
<p> some text</p>
Name: <asp:textbox id="txtName" runat="server" />
...
function should work to make the markup look like this-
...
<p> some text</p>
Name: <div id="divName" runat="server"><asp:textbox id="txtName" runat="server" /></div>
...
Yes, and there is more than one way to do this.
Recommended solution: create a custom control
You can easily create a custom control that extends System.Web.UI.WebControls.TextBox and overrides the Render method to write the markup for you. This gives you ultimate flexibility in how a control is rendered. This example uses base.Render() to render the TextBox itself, but wraps it in a DIV by writing directly to the HtmlTextWriter. The major benefits to this are that your custom control will be available in the code behind just like any other server control and that you can declare it in your .aspx/.ascx files.
To wit:
public class WrappedTextBox : System.Web.UI.WebControls.TextBox
{
protected override void Render(HtmlTextWriter writer)
{
writer.Write("<div id=\"div{0}\" runat=\"server\">", this.ID.Replace("txt", String.Empty));
base.Render(writer);
writer.WriteEndTag("div");
}
}
To use the control in an ASPX page, first Register the control's namespace:
<%# Register Assembly="WebApplication1" TagPrefix="cc" Namespace="WebApplication1" %>
Then add the control to your form like so:
<cc:WrappedTextBox ID="txtName" runat="server" Text="Wrapped!" />
cc is just an arbitrary prefix that I chose, but you can make up your own! (cc for "custom control")
Note that the runat=\"server\" is probably unnecessary in the div output (though you could be writing code that generates ASPX content, I suppose). The runat="server" just tells ASP.NET that you want it to be a server control, but in this case your WrappedTextBox is the server control. Also note that this isn't necessarily a best-practices example.. I wouldn't replace the txt normally, but rather would use a custom attribute for the div ID. (But this example will give the requested output.)
Alternate solution: Depth first search and modification of the Control Tree
protected void Page_Load(object sender, EventArgs e)
{
DeepControlSearch(Page);
}
private void DeepControlSearch(Control control)
{
if (control.HasControls())
{
for (int c = control.Controls.Count - 1; c > -1; c--)
{
DeepControlSearch(control.Controls[c]);
}
}
if (control is TextBox)
{
WrapTextBox((TextBox)control);
}
}
private void WrapTextBox(TextBox textBox)
{
System.Web.UI.HtmlControls.HtmlGenericControl div = new System.Web.UI.HtmlControls.HtmlGenericControl("div");
div.ID = String.Format("div{0}", textBox.ID.Replace("txt", String.Empty));
div.Attributes["runat"] = "server";
Control parent = textBox.Parent;
parent.Controls.Remove(textBox);
div.Controls.Add(textBox);
parent.Controls.Add(div);
}
The benefit to this approach is that you don't have to create custom controls (which requires a bit of up-front work, but usually is a worthwhile investment because you can reuse them in any ASP.NET project). The HtmlGenericControl can be manipulated like any other server control, but it is dynamic and so needs to be in ViewState if you want to access it on PostBack (unless you want to do it the oldskool way and loop through the posted Form elements to get values from dynamic controls).
Not sure I would recommend the second approach; this is really what custom controls are made for.
Alternate solution #2: emit JavaScript (jQuery makes it really easy) to wrap the controls
You could use ClientScript.RegisterStartupScript() to emit some JavaScript or jQuery to do this, for example:
ClientScript.RegisterStartupScript(
this.GetType(),
"wrap textboxes",
"$(document).ready(function(){$('input:text').each(function(){$(this).wrap('<div id=\"div' + this.id.replace('txt', '') + '\"></div>');});});",
true);

Access Textbox on Page from User Control in ASP.net

I have a some pages that are slightly different, but all have the same "action buttons" that do the same tasks for each page. Instead of duplicating the code, I made a user control that includes buttons that perform actions - but there's one action I can't seem to do.
Each page has a textbox (that's not inside the user control, as it's in a different location of the page). When I click the "Save comment" button (that is within the User Control), I can't seem to access the text in the Textbox.
I have tried using something like this:
TextBox txtComments = (TextBox)this.Parent.FindControl("txtComments");
SaveChanges(txtComments.Text);
...but txtComments comes back as null.
So, I'm wondering if this is possible, or perhaps if there's a better way to do what I'm trying to do?
Edit: The Textbox is in a Placeholder on the original page...
Edit 2: Posting minified solution - still can't figure this one out.
Edit 3: Removed solution to conserve space - resolved the issue.
My solution ended up being surprisingly simple....
TextBox txt = this.Parent.Parent.FindControl("ContentPlaceHolder2").FindControl("TextBox1") as TextBox;
if (txt != null) Label1.Text = txt.Text;
Where I was going wrong before was that I was using the wrong ContentPlaceHolder ID. I was using the ID of the placeholder in the page, rather than the ID from the Master Page.
Use the Page property exposed by WebControl, the common base of server-side controls.
You could even then cast the instance to the specific page type and access the control directly (if scope allows), instead of using FindControl.
To recap the situation - you need to do a FindControl of a control on a page from a child control, however -
Your project has a MasterPage, in which case this.Page seems to not work, and we use this.Parent instead
Your "target" control is inside a PlaceHolder, which itself is inside a ContentPlaceHolder, so it's not as simple as just this.Parent.FindControl()
Your child ASCX control that is trying to find the "target" control, in this case a textbox, is actually in ANOTHER ContentPlaceHolder, so again, this.Parent.Parent or whatever will not work.
Since you mentioned after my initial this.Parent answer about the controls being in a different ContentPlaceHolder from each other, and in another child control, it complicates your query a bit.
Based on these criteria, and the fact that you at least know the contentPlaceHolder control which contains (somewhere inside of it) your target TextBox, here's some code I wrote that works for me in a new ASP.net Web Forms application:
It recursively checks controls collection of the ContentPlaceHolder you pass to it, and finds your control.
Just pass the ControlID and ContentPlaceHolderID, and it will recursively find it.
This code is a replacement for my original one below with the same project, located inside of ChildControl.ascx.cs file:
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Linq;
using System.Collections.Generic;
namespace FindControlTest
{
public partial class ChildControl : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
var textBoxTest = FindControlInContentPlaceHolder("TextBoxTest", "FeaturedContent") as TextBox;
Response.Write(textBoxTest.Text);
Response.End();
}
private Control FindControlInContentPlaceHolder(string controlID, string contentPlaceHolderID)
{
if (null == this.Page ||
null == this.Page.Master)
{
return null;
}
var contentPlaceHolder = this.Page.Master.FindControl(contentPlaceHolderID);
var control = getChildControl(controlID, contentPlaceHolder);
return control;
}
private Control getChildControl(string controlID, Control currentControl)
{
if (currentControl.HasControls())
{
foreach(Control childControl in currentControl.Controls)
{
var foundControl = childControl.FindControl(controlID);
if (null != foundControl)
{
return foundControl;
}
else
{
return getChildControl(controlID, childControl);
}
}
}
return null;
}
}
}
Note:
I tried this in a few events and even on Init() I was able to get the TextBox value
If you are seeing a null it is likely due to an incorrect ID being passed or a situation I didn't encounter yet. If you edit your question with additional info (as there has been a lot of it) and show what variable is null, it can be resolved.
Note that I added some complexity to my MasterPage, such as a PlaceHolder inside a Panel, and then put the ContentPlaceHolder in there, and the code still works. I even compiled for .net 4.5, 4.0, 3.5, and 3.0 thinking maybe FindControl works differently with MasterPages, but still it works every time. You may need to post some additional MarkUp if you still get a null.
The Rest of the Test Project
The Page (based on default MasterPage)
<%# Page Title="Home Page" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="FindControlTest._Default" %>
<%# Register TagName="ChildControl" TagPrefix="uc1" Src="~/ChildControl.ascx" %>
<asp:Content runat="server" ContentPlaceHolderID="FeaturedContent">
<asp:PlaceHolder ID="PlaceHolderTest" runat="server">
<asp:TextBox ID="TextBoxTest" Text="Hello!" runat="server"/>
</asp:PlaceHolder>
</asp:Content>
<asp:Content ContentPlaceHolderID="MainContent" runat="server">
<uc1:ChildControl id="ChildControlTest" runat="server" />
</asp:Content>
I added a control called ChildControl.ascx that only has this in it:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="ChildControl.ascx.cs" Inherits="FindControlTest.ChildControl" %>
Hello child!
The result is "Hello!" on the page.
If you want to access the textbox property in codebehind as intellisence property, simply make it a string property in the user control.
1. In the user control, create a public string property that returns textbox string value..
public string MyText
{
get
{
return txt1.Text;
}
}
2. Register the user control on the page
<%# Register TagPrefix="uc" TagName="MyControl" Src="~/mycontrol.ascx" />
and declare it..
<uc:MyControl ID="control1" runat="server" />
3. From the codebehind now you can read the property..
Response.Write(control1.MyText);
Hope it helps
Thanks...

Add html layout to a blank aspx page

Want to create dynamic html layout without any asp controls. Actually I want to leave on aspx page only the first line
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Kletka._Default" %>
and the generate full html layout on codebehind. Advise pls how to implement this.
One way is this:
protected void Page_Load(object sender, EventArgs e)
{
string coolHTML = #"<div class=""someclass"">... and other cool content</div>";
Response.Write(coolHTML);
}
With that said. This is a terrible idea. Constructing HTML dynamically on code behind is a nightmare to maintain, it doesn't perform as best as it can and you lose many other features that asp.net offers, which are the main reason to use ASP.NET in the first place.
What you can do is create User controls for specific things (footer, header, left panel, etc) and define a layout for them in markup; then on Code behind, you can add them to specific place holders in the page, depending on some business conditions.
Assuming you have a master page (or at least some content place holders in the page) as so:
<asp:ContentPlaceHolder id="footer" runat="Server" />
On code behind you can do:
footer.Controls.Add(new FooterControl());
Update OP just mentioned in the comments that he doesn't like asp.net controls...
You don't have to use ASP.NET controls, you can use regular HTML controls and set their runat="server" attribute if you need to be able to manipulate their properties on server-side. For example:
<div id="mydiv" runat="server" > some content </div>
On Code behind:
myDiv.Attributes.Add("class","css_class");
myDiv.Attributes.Add("onclick","callJavascriptFunction();");
// and so on.
It's okay to do this sort of thing occasionally under very specific circumstances but I'd avoid this sort of code because is difficult to maintain. Imagine you need to add another class to the myDiv element, for example. Now, you'd have to change you code as opposed to just changing your markup...
So...you want to use ASP.NET web forms without using the built in controls like GridView and so on, at all?
You can write your html and use protected properties?
<%= SomeWhereText %?
or to have the FULL html layout in the code behind make a property
protected string MyEntirePage {get;set;}
and build the string in the code behind
MyEntirePage="<h1>Hello</h1><p>body here</p><p>the end</p>";
and write it out in the aspx page via
<%=MyEntirePage%>
Re: "I've got your point, but I really don't like asp.net controls. I'd prefer to use html controls and customize them with js"
Install NancyFx or maybe the old (but still great) WCF Web Api and use something like KnockoutJs, jQuery or Backbone to perform ajax calls for the dynamic content = no asp.net web forms at all. Yay.
You would need to dynamically add the controls in the Page_Init event. So you need a container to hold your HTML, so you should start by adding a Panel to the Page, then the page's controls would get added to the Panel.
Panel pnl = new Panel();
Page.Controls.Add(pnl);
TextBox txt = new TextBox();
pnl.Controls.Add(txt);
etc....

Categories

Resources