Get html of div in codebehind ASP.NET c# [duplicate] - c#

<form runat="server" id="f1">
<div runat="server" id="d">
grid view:
<asp:GridView runat="server" ID="g">
</asp:GridView>
</div>
<asp:TextBox runat="server" ID="t" TextMode="MultiLine" Rows="20" Columns="50"></asp:TextBox>
</form>
Code behind:
public partial class ScriptTest : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
g.DataSource = new string[] { "a", "b", "c" };
g.DataBind();
TextWriter tw = new StringWriter();
HtmlTextWriter h = new HtmlTextWriter(tw);
d.RenderControl(h);
t.Text = tw.ToString();
}
}
Even the GridView is within a from tag with runat="server", still I am getting this error.
Any clues please ?

You are calling GridView.RenderControl(htmlTextWriter), hence the page raises an exception that a Server-Control was rendered outside of a Form.
You could avoid this execption by overriding VerifyRenderingInServerForm
public override void VerifyRenderingInServerForm(Control control)
{
/* Confirms that an HtmlForm control is rendered for the specified ASP.NET
server control at run time. */
}
See here and here.

An alternative to overriding VerifyRenderingInServerForm is to remove the grid from the controls collection while you do the render, and then add it back when you are finished before the page loads. This is helpful if you want to have some generic helper method to get grid html because you don't have to remember to add the override.
Control parent = grid.Parent;
int GridIndex = 0;
if (parent != null)
{
GridIndex = parent.Controls.IndexOf(grid);
parent.Controls.Remove(grid);
}
grid.RenderControl(hw);
if (parent != null)
{
parent.Controls.AddAt(GridIndex, grid);
}
Another alternative to avoid the override is to do this:
grid.RenderBeginTag(hw);
grid.HeaderRow.RenderControl(hw);
foreach (GridViewRow row in grid.Rows)
{
row.RenderControl(hw);
}
grid.FooterRow.RenderControl(hw);
grid.RenderEndTag(hw);

Just after your Page_Load add this:
public override void VerifyRenderingInServerForm(Control control)
{
//base.VerifyRenderingInServerForm(control);
}
Note that I don't do anything in the function.
EDIT: Tim answered the same thing. :)
You can also find the answer Here

Just want to add another way of doing this. I've seen multiple people on various related threads ask if you can use VerifyRenderingInServerForm without adding it to the parent page.
You actually can do this but it's a bit of a bodge.
First off create a new Page class which looks something like the following:
public partial class NoRenderPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{ }
public override void VerifyRenderingInServerForm(Control control)
{
//Allows for printing
}
public override bool EnableEventValidation
{
get { return false; }
set { /*Do nothing*/ }
}
}
Does not need to have an .ASPX associated with it.
Then in the control you wish to render you can do something like the following.
StringWriter tw = new StringWriter();
HtmlTextWriter hw = new HtmlTextWriter(tw);
var page = new NoRenderPage();
page.DesignerInitialize();
var form = new HtmlForm();
page.Controls.Add(form);
form.Controls.Add(pnl);
controlToRender.RenderControl(hw);
Now you've got your original control rendered as HTML. If you need to, add the control back into it's original position. You now have the HTML rendered, the page as normal and no changes to the page itself.

Here is My Code
protected void btnExcel_Click(object sender, ImageClickEventArgs e)
{
if (gvDetail.Rows.Count > 0)
{
System.IO.StringWriter stringWrite1 = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter htmlWrite1 = new HtmlTextWriter(stringWrite1);
gvDetail.RenderControl(htmlWrite1);
gvDetail.AllowPaging = false;
Search();
sh.ExportToExcel(gvDetail, "Report");
}
}
public override void VerifyRenderingInServerForm(Control control)
{
/* Confirms that an HtmlForm control is rendered for the specified ASP.NET
server control at run time. */
}

Tim Schmelter's answer helped me a lot, but I had to do one more thing to get it to work on my aspx page. I am using this code to email an embedded GridView control (as HTML), for report automation.
In addition to adding the override sub, I had to do the render() in Me.Handles.onunload, or else I got an error on the RenderControl line.
Protected Sub Page_After_load(sender As Object, e As EventArgs) Handles Me.Unload
If runningScheduledReport Then
Dim stringBuilder As StringBuilder = New StringBuilder()
Dim stringWriter As System.IO.StringWriter = New System.IO.StringWriter(stringBuilder)
Dim htmlWriter As HtmlTextWriter = New HtmlTextWriter(stringWriter)
GridView1.RenderControl(htmlWriter)
Dim htmlcode As String = stringBuilder.ToString()
Func.SendEmail(Context.Request.QueryString("email").ToString, htmlcode, "Auto Report - Agent Efficiency", Nothing)
End If
End Sub

Related

Does a child web page inherit the text boxes which were there in the master web page? [duplicate]

I have a master page which contains a label for status messages. I need to set the status text from different .aspx pages. How can this be done from the content page?
public partial class Site : System.Web.UI.MasterPage
{
public string StatusNachricht
{
get
{
return lblStatus.Text;
}
set
{
lblStatus.Text = value;
}
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
I have tried this, but was unsuccessful in making it work:
public partial class DatenAendern : System.Web.UI.Page
{
var master = Master as Site;
protected void Page_Load(object sender, EventArgs e)
{
if (master != null)
{
master.setStatusLabel("");
}
}
protected void grdBenutzer_RowCommand(object sender, GridViewCommandEventArgs e)
{
try
{
//some code
if (master != null)
{
master.setStatusLabel("Passwort erfolgreich geändert.");
}
}
catch (Exception ex)
{
if (master != null)
{
master.setStatusLabel("Passwort konnte nicht geändert werden!");
}
}
}
}
}
In the MasterPage.cs file add the property of Label like this:
public string ErrorMessage
{
get
{
return lblMessage.Text;
}
set
{
lblMessage.Text = value;
}
}
On your aspx page, just below the Page Directive add this:
<%# Page Title="" Language="C#" MasterPageFile="Master Path Name"..... %>
<%# MasterType VirtualPath="Master Path Name" %> // Add this
And in your codebehind(aspx.cs) page you can then easily access the Label Property and set its text as required. Like this:
this.Master.ErrorMessage = "Your Error Message here";
In Content page you can access the label and set the text such as
Here 'lblStatus' is the your master page label ID
Label lblMasterStatus = (Label)Master.FindControl("lblStatus");
lblMasterStatus.Text = "Meaasage from content page";
It Works
To find master page controls on Child page
Label lbl_UserName = this.Master.FindControl("lbl_UserName") as Label;
lbl_UserName.Text = txtUsr.Text;
I have a helper method for this in my System.Web.UI.Page class
protected T FindControlFromMaster<T>(string name) where T : Control
{
MasterPage master = this.Master;
while (master != null)
{
T control = master.FindControl(name) as T;
if (control != null)
return control;
master = master.Master;
}
return null;
}
then you can access using below code.
Label lblStatus = FindControlFromMaster<Label>("lblStatus");
if(lblStatus!=null)
lblStatus.Text = "something";
You cannot use var in a field, only on local variables.
But even this won't work:
Site master = Master as Site;
Because you cannot use this in a field and Master as Site is the same as this.Master as Site. So just initialize the field from Page_Init when the page is fully initialized and you can use this:
Site master = null;
protected void Page_Init(object sender, EventArgs e)
{
master = this.Master as Site;
}
This is more complicated if you have a nested MasterPage. You need to first find the content control that contains the nested MasterPage, and then find the control on your nested MasterPage from that.
Crucial bit: Master.Master.
See here: http://forums.asp.net/t/1059255.aspx?Nested+master+pages+and+Master+FindControl
Example:
'Find the content control
Dim ct As ContentPlaceHolder = Me.Master.Master.FindControl("cphMain")
'now find controls inside that content
Dim lbtnSave As LinkButton = ct.FindControl("lbtnSave")
If you are trying to access an html element: this is an HTML Anchor...
My nav bar has items that are not list items (<li>) but rather html anchors (<a>)
See below: (This is the site master)
<nav class="mdl-navigation">
<a class="mdl-navigation__link" href="" runat="server" id="liHome">Home</a>
<a class="mdl-navigation__link" href="" runat="server" id="liDashboard">Dashboard</a>
</nav>
Now in your code behind for another page, for mine, it's the login page...
On PageLoad() define this:
HtmlAnchor lblMasterStatus = (HtmlAnchor)Master.FindControl("liHome");
lblMasterStatus.Visible =false;
HtmlAnchor lblMasterStatus1 = (HtmlAnchor)Master.FindControl("liDashboard");
lblMasterStatus1.Visible = false;
Now we have accessed the site masters controls, and have made them invisible on the login page.

Exception thrown when rendering a dynamic label WebControl with the AssociateControlID

I get an exception when calling a web service which dynamically builds HTML elements at run-time and returns the rendered elements as a string.
The following error occurs on the RenderControl() method call:
System.Web.HttpException: Unable to find control with id 'txtCertificate' that is associated with the Label 'lblCertificate'.
StringWriter stringWriter = new StringWriter();
HtmlTextWriter writer = new HtmlTextWriter(stringWriter);
// Create System.Web.UI.WebControls.Panel (container)
Panel pnlFRow1 = new Panel();
pnlFRow1.CssClass = "f-row";
// Create System.Web.UI.WebControls.Textbox
TextBox txtCertificate = new TextBox();
txtCertificate.ID = "txtCertificate";
txtCertificate.CssClass = "f-input f-input-full";
// Create System.Web.UI.WebControls.Label
Label lblCertificate = new Label();
lblCertificate.ID = "lblCertificate";
lblCertificate.CssClass = "f-label f-label-full";
lblCertificate.AssociatedControlID = txtCertificate.ID;
lblCertificate.Text = "Certificate:";
Panel pnlCertificate = new Panel();
pnlCertificate.CssClass = "f-label f-label-full";
// Binding child controls to parent controls
pnlFRow1.Controls.Add(lblCertificate);
pnlFRow1.Controls.Add(pnlCertificate);
pnlCertificate.Controls.Add(txtCertificate);
// Render control
pnlContent.RenderControl(writer);
// Return rendered HTML
return writer.InnerWriter.ToString();
I tried placing the line pnlFRow1.Controls.Add(lblCertificate); after the line pnlCertificate.Controls.Add(txtCertificate); thinking that this might be an issue where the order matters, however this causes the same error.
The AssociatedControlID attribute is a must have in order to have the Label WebControl render as an actual <label> element and must be displayed before the input control.
One way of doing it would be to inherit the Label class, and replace the RenderBeginTag method with something that's more to your liking. If you nest the Label class it should keep in the local context and stop it leaking out into the rest of your application
For example:
public partial class _Default : Page
{
public class Label : System.Web.UI.WebControls.Label {
public string For = null;
public override void RenderBeginTag(HtmlTextWriter writer) {
AddAttributeIfExists(writer, "ID", ID);
AddAttributeIfExists(writer, "For", For);
writer.RenderBeginTag("Label");
}
private void AddAttributeIfExists(HtmlTextWriter writer, string name, string value) {
if (!string.IsNullOrWhiteSpace(value))
writer.AddAttribute(name, value);
}
}
protected void Page_Load(object sender, EventArgs e) {
StringWriter stringWriter = new StringWriter();
HtmlTextWriter writer = new HtmlTextWriter(stringWriter);
Panel test = new Panel() { ID = "PanelTest" };
TextBox txtCertificate = new TextBox() { ID = "txtCertificate" };
Label lblCertificate = new Label() { ID = "lblCertificate", Text = "Certificate", For = txtCertificate.ClientID };
test.Controls.Add(lblCertificate);
test.Controls.Add(txtCertificate);
test.RenderControl(writer);
string teststring = writer.InnerWriter.ToString();
}
}
Note: Instead of using AssociatedControlId, you can now use a custom For property which will do the same thing. Also note how the Label class is nested inside your page class, so it doesn't interfere with other pages.
Unfortunately, as we discovered in the first version of this answer, if you try to use .Attributes["for"] without the AssociatedControlId, .NET will convert the <Label> to a <span>, so the above example should get around this.

How to loop through the div to reset the delegate of the links in?

If i have method like this to Draw my side Menu Dynamically :
private void DrawSideMenu()
{
LinkButton x;
TaskDTO TaskList = new TaskDTO();
List<TaskDTO> List = TaskList.DrawMenu(int.Parse(Session["emp"].ToString()));
HtmlGenericControl myDIV = new HtmlGenericControl("div");
myDIV.ID = "menu8";
HtmlGenericControl myOrderedList = new HtmlGenericControl("ul");//css clss for <ul>
myOrderedList.ID = "orderedList";
myOrderedList.Attributes.Add("class", "task");
HtmlGenericControl listItem1;
string count = "";
foreach (TaskDTO i in List)
{
count = AdjustMenuCount1(i.TaskCode);
x = new LinkButton();
x.ID = i.TaskCode.ToString();
x.Text = i.TaskName + " " + count;
x.Click += new EventHandler(TaskC);
x.Style["FONT-FAMILY"] = "tahoma";
listItem1 = new HtmlGenericControl("li");
listItem1.Attributes.Add("class", "normal");
if (count != "0")
{
listItem1.Controls.Add(x);
myOrderedList.Controls.Add(listItem1);
}
}
myDIV.Controls.Add(myOrderedList);
MenuTD.Controls.Add(myDIV);
Session["SideMenu"] = myDIV;//Save to redraw when page postbacks
}
This Method takes long time to draw my menu.so i call it one time in (!IsPostBack) and save it in session so that i could redraw it like that :
MenuTD.Controls.Add( ((System.Web.UI.Control)(Session["SideMenu"])));
It redraws it successfully but when i click on any link it doesn't hit the event because i thought it's not possible to save the x.Click += new EventHandler(TaskC); in the session ,so i want to know how to loop through my session content to resetting the delegate of my link ?
That idea won't work because if you're not wiring up the Event Handler every time the page is loaded, it won't run.
If we come back to the original issue, you said it's slow. Creating controls at runtime cannot be slow and it's most likely the way you create your list of items:
List<TaskDTO> List = TaskList.DrawMenu(int.Parse(Session["emp"].ToString()));
Instead of storing complete menu, try to store in the Session only List and create all controls as usual. If menu is required on one page only, then use ViewState instead of Session.
Also it makes sense to change the entire code as currently you hardcode all style and layout settings in the code. Create all layout (div, ul, li) in aspx, move all styles in css (for example, you use "task" class but still set "tahoma" in the code). This would simplify the code and bring more flexibility.
List<TaskDTO> List = null;
void Page_Load(object sender, EventArgs e)
{
if (ViewState["List"] != null) {
List = (List<TaskDTO>)ViewState["List"];
} else {
// ArrayList isn't in view state, so we need to load it from scratch.
List = TaskList.DrawMenu(int.Parse(Session["emp"].ToString()));
}
// Code to create menu, e.g.
if (!Page.IsPosBack) {
Repeater1.DataSource = List;
Repeater1.DataBind();
}
}
void Page_PreRender(object sender, EventArgs e)
{
// Save PageArrayList before the page is rendered.
ViewState.Add("List", List);
}
...
<ul id="orderedList">
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
<li><%# Eval("TaskName") %></li>
</ItemTemplate>
</asp:Repeater>
</ul>
Maybe save it in application level so it only gets built once, then just put the menu into an object and loop through it to re-add the clicks.
I'm afraid that in order for it to work you are going to have to rebind the Click handler on every Page_Load.
Based on your code, and assuming your TaskC is available, you can make this method:
private void RebindMenuHandlers() {
if(Session["SideMenu"] == null)
return; // Your menu has not been built yet
var menu = ((System.Web.UI.Control)(Session["SideMenu"]));
var orderedList = menu.Controls[0];
foreach(var listItem in orderedList){
foreach(var control in listItem){
var linkButton = control as LinkButton;
if(linkButton != null){
linkButton.Click += new EventHandler(TaskC);
}
}
}
}
Then call it on your Page_Load event:
void Page_Load(object sender, EventArgs e)
{
RebindMenuHandlers();
// .... etc
}
I just typed this directly here, so please forgive any silly compilation mistakes, this should be enough to give you the general idea. Hope that helps.

Moving code to master page SavePageStateToPersistenceMedium & LoadPageStateFromPersistenceMedium

I want to remove the view state using the method below. My problem is I don't like it put the code below into almost every page. I like to move it to the masterpage or convert it to a class and run it from the masterpage.
The code below on works on the non-masterpage .
It will have this problem if I put inside the masterpage CS0115: 'MasterPage.SavePageStateToPersistenceMedium(object)': no suitable method found to override
#region Disable ViewState
protected override void SavePageStateToPersistenceMedium(object state)
{
}
protected override object LoadPageStateFromPersistenceMedium()
{
return null;
}
#endregion
<% EnableViewState="false" %>
you can make this EnableViewState="false" on top of every page, which will disable view state on that particular page, but if you want any control to have the view state enabled than you can enable it by the same EnableViewState="true" on that control.
You should put this in a base class and have each of your pages that you don't want viewstate on inherit from it
//a basepage that overrides the methods
public class BasePage : System.Web.UI.Page
{
protected override void SavePageStateToPersistenceMedium(object state)
{
}
protected override object LoadPageStateFromPersistenceMedium()
{
return null;
}
}
//your page class that inherits your base page
public class Page1 : BasePage
{
}
Each page has an option to not use ViewState.
<asp:Page EnableViewState="True|False" />
//using the below you will need to change asp:ImageButton coding method. You need to convert it to a regular submit button. There may be other problems which i don't now yet.
Place the code below on the master page.
protected override void Render(HtmlTextWriter writer)
{///Articles/How-to-disable-or-remove-ViewState-Hidden-Field-in-ASP.Net-Page.aspx
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
HtmlTextWriter ht = new HtmlTextWriter(sw);
base.Render(ht);
string html = sb.ToString();
//The remove the value and it id from the view status this next line
html = Regex.Replace(html, "<input[^>]*id=\"(__VIEWSTATE)\"[^>]*>", "<input type=\"hidden\" name=\"__VIEWSTATE\" value=\"\"/>", RegexOptions.IgnoreCase);
//To completely remove the view state use the line below
// html = Regex.Replace(html, "<input[^>]*id=\"(__VIEWSTATE)\"[^>]*>", "", RegexOptions.IgnoreCase);
writer.Write(html);
sb = null; html = null; ht.Dispose(); sw.Dispose();
}
Use this technique to detect the clicked button on your aspx page(not the master page). Place this code in the code behind
if (Request.Form["nonPostBackButton1.y"] != null)
{
Response.Write("nonPostBackButton1 is just pressed");
}
if (Request.Form["nonPostBackButton2.y"] != null)
{
Response.Write("<br/><b>nonPostBackButton2 is just pressed</b>");
}
This is the sample of the image submit buttons
<input type="image" alt="nonPostBackButton1" name="nonPostBackButton1"/> Use this method instead of the postback buttons. This is the image submit button 1</p>
<p><input type="image" alt="nonPostBackButton2" name="nonPostBackButton2"/> Use this method instead of the postback buttons. This is the image submit button 1</p>

UserControl's RenderControl is asking for a form tag in (C# .NET)

I asked how to render a UserControl's HTML and got the code working for a dynamically generated UserControl.
Now I'm trying to use LoadControl to load a previously generated Control and spit out its HTML, but it's giving me this:
Control of type 'TextBox' must be placed inside a form tag with runat=server.
I'm not actually adding the control to the page, I'm simply trying to grab its HTML. Any ideas?
Here's some code I'm playing with:
TextWriter myTextWriter = new StringWriter();
HtmlTextWriter myWriter = new HtmlTextWriter(myTextWriter);
UserControl myControl = (UserControl)LoadControl("newUserControl.ascx");
myControl.RenderControl(myWriter);
return myTextWriter.ToString();
Alternatively you could disable the ServerForm/Event-validation on the page that is rendering the control to a string.
The following example illustrates how to do this.
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string rawHtml = RenderUserControlToString();
}
private string RenderUserControlToString()
{
UserControl myControl = (UserControl)LoadControl("WebUserControl1.ascx");
using (TextWriter myTextWriter = new StringWriter())
using (HtmlTextWriter myWriter = new HtmlTextWriter(myTextWriter))
{
myControl.RenderControl(myWriter);
return myTextWriter.ToString();
}
}
public override void VerifyRenderingInServerForm(Control control)
{ /* Do nothing */ }
public override bool EnableEventValidation
{
get { return false; }
set { /* Do nothing */}
}
}
This is a dirty solution I used for the moment (get it working then get it right, right?).
I had already created a new class that inherits the UserControl class and from which all other "UserControls" I created were derived. I called it formPartial (nod to Rails), and this is going inside the public string renderMyHTML() method:
TextWriter myTextWriter = new StringWriter();
HtmlTextWriter myWriter = new HtmlTextWriter(myTextWriter);
UserControl myDuplicate = new UserControl();
TextBox blankTextBox;
foreach (Control tmpControl in this.Controls)
{
switch (tmpControl.GetType().ToString())
{
case "System.Web.UI.LiteralControl":
blankLiteral = new LiteralControl();
blankLiteral.Text = ((LiteralControl)tmpControl).Text;
myDuplicate.Controls.Add(blankLiteral);
break;
case "System.Web.UI.WebControls.TextBox":
blankTextBox = new TextBox();
blankTextBox.ID = ((TextBox)tmpControl).ID;
blankTextBox.Text = ((TextBox)tmpControl).Text;
myDuplicate.Controls.Add(blankTextBox);
break;
// ...other types of controls (ddls, checkboxes, etc.)
}
}
myDuplicate.RenderControl(myWriter);
return myTextWriter.ToString();
Drawbacks off the top of my head:
You need a case statement with every
possible control (or controls you
expect).
You need to transfer all
the important attributes from the
existing control (textbox, etc) to
the new blank control.
Doesn't take full advantage of
Controls' RenderControl method.
It'd be easy to mess up 1 or 2. Hopefully, though, this helps someone else come up with a more elegant solution.
You can add the control into page, render html and then remove the control from page.
Or try this:
Page tmpPage = new TempPage(); // temporary page
Control tmpCtl = tmpPage.LoadControl( "~/UDynamicLogin.ascx" );
//the Form is null that's throws an exception
tmpPage.Form.Controls.Add( tmpCtl );
StringBuilder html = new StringBuilder();
using ( System.IO.StringWriter swr = new System.IO.StringWriter( html ) ) {
using ( HtmlTextWriter writer = new HtmlTextWriter( swr ) ) {
tmpPage.RenderControl( writer );
}
}
You can either add a form to your user control, or use a regular html input box
<input type="text" />
Edit: If you are trying to do something AJAXy, maybe you want something like this
http://aspadvice.com/blogs/ssmith/archive/2007/10/19/Render-User-Control-as-String-Template.aspx
public static string RenderView<D>(string path, D dataToBind)
{
Page pageHolder = new Page();
UserControl viewControl = (UserControl) pageHolder.LoadControl(path);
if(viewControl is IRenderable<D>)
{
if (dataToBind != null)
{
((IRenderable<D>) viewControl).PopulateData(dataToBind);
}
}
pageHolder.Controls.Add(viewControl);
StringWriter output = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, output, false);
return output.ToString();
}
You can remove the data binding part if not needed.
I was having the same problem using similar code to #TcKs and haven't been able to make any of these examples work for me. I got it working by using the LoadControl method of a UserControl as such:
UserControl uc = new UserControl();
Control c = uc.LoadControl("newUserControl.ascx");
c.RenderControl(myWriter);

Categories