I'm trying to take an existing bunch of code that was previously on a full .aspx page, and do the same stuff in a .ashx handler.
The code created an HtmlTable object, added rows and cells to those rows, then added that html table the .aspx's controls collection, then added it to a div that was already on the page.
I am trying to keep the code in tact but instead of putting the control into a div, actually generate the html and I'll return that in a big chunk of text that can be called via AJAX client-side.
HtmlTable errors out when I try to use the InnerHtml property (says it isn't supported), and when I try RenderControl, after making first a TextWriter and next an HtmlTextWriter object, I get the error that Page cannot be null.
Has anyone done this before? Any suggestions?
*Most recent is above.
OK, even after Matt's update there is a workaround ;)
Firstly, we have to use a page with form inside. Otherwise we won't be able to add a ScriptManager control. One more thing: the ScriptManager control should be the first control in the form. Further is easier:
Page page = new Page();
Button button = new System.Web.UI.WebControls.Button
{
ID = "btnSumbit",
Text = "TextButton",
UseSubmitBehavior = true
};
HtmlForm form = new HtmlForm
{
ID="theForm"
};
ScriptManager scriptManager = new ScriptManager
{
ID = "ajaxScriptManager"
};
form.Controls.Add(scriptManager);
form.Controls.Add(button);
page.Controls.Add(form);
using (StringWriter output = new StringWriter())
{
HttpContext.Current.Server.Execute(page, output, false);
context.Response.ContentType = "text/plain";
context.Response.Write(output.ToString());
}
This works. The output is quite large so I decided not to include it into my answer :)
Actually, there is a workaround. Yep, we may render a control in handler.
Firstly, we need a formless page. Because without it we get:
Control 'btnSumbit' of type 'Button'
must be placed inside a form tag with
runat=server.
public class FormlessPage : Page
{
public override void VerifyRenderingInServerForm(Control control)
{
}
}
Secondly, nobody can prevent us from creating an instance of our FormlessPage page. And now let's add a control there (I decided to add a Button control as an example, but you could use any).
FormlessPage page = new FormlessPage();
Button button = new System.Web.UI.WebControls.Button
{
ID = "btnSumbit",
Text = "TextButton",
UseSubmitBehavior = true
};
page.Controls.Add(button);
Thirdly, let's capture the output. For this we use HttpServerUtility.Execute method:
Executes the handler for the specified
virtual path in the context of the
current request. A
System.IO.TextWriter captures output
from the executed handler and a
Boolean parameter specifies whether to
clear the
System.Web.HttpRequest.QueryString and
System.Web.HttpRequest.Form
collections.
Here is the code:
using (StringWriter output = new StringWriter())
{
HttpContext.Current.Server.Execute(page, output, false);
context.Response.ContentType = "text/plain";
context.Response.Write(output.ToString());
}
The result will be:
<input type="submit" name="btnSumbit" value="TextButton" id="btnSumbit" />
In addition I can recommend ScottGu's article Tip/Trick: Cool UI Templating Technique to use with ASP.NET AJAX for non-UpdatePanel scenarios. Hope, you could find a lot of useful there.
Another option is to host the ASP.NET HTTP pipeline in your process, render the page to a stream and read the HTML you need to send from the HttpListenerContext.Response.OutputStream stream after the page has been processed.
This article has details: http://msdn.microsoft.com/en-us/magazine/cc163879.aspx
Related
I load a piece of html which contains something like:
<em> < input type="text" value="Untitled" name="ViewTitle" id="ViewTitle" runat="server"> </em>
into my control. The html is user defined, do please do not ask me to add them statically on the aspx page.
On my page, I have a placeholder and I can use
LiteralControl target = new LiteralControl ();
// html string contains user-defined controls
target.text = htmlstring
to render it property. My problem is, since its a html piece, even if i know the input box's id, i cannot access it using FindControl("ViewTitle") (it will just return null) because its rendered as a text into a Literal control and all the input controls were not added to the container's control collections. I definitely can use Request.Form["ViewTitle"] to access its value, but how can I set its value?
Jupaol's method is the prefer way of adding dynamic control to a page.
If you want to insert string, you can use ParseControl.
However, it doesn't cause compilation for some controls such as PlaceHolder.
Your process is wrong, you are rendering a control to the client with the attribute: runat="server"
This attribute only works if the control was processed by the server, you are just rendering as is
Since your goal is to add a TextBox (correct me if I'm wrong), then why don't you just add a new TextBox to the form's controls collection???
Something like this:
protected void Page_Init(object sender, EventArgs e)
{
var textbox = new TextBox { ID="myTextBoxID", Text="Some initial value" };
this.myPlaceHolder.Controls.Add(textbox);
}
And to retrieve it:
var myDynamicTextBox = this.FindControl("myTextBoxID") as TextBox;
I have created several working examples and they are online on my GitHub site, feel free to browse the code
Asp.net
A.aspx
I'm using JQuery to access an ashx file which loads control ( ascx ) which contains a GridView. The control content is being injected to the page...
When I do this:
StringWriter writer = new StringWriter();
HttpContext.Current.Server.Execute(page, writer, false);
string output = writer.ToString();
It tells me that the GridView must be placed in a form section.
So I've created my Page
public class MyPage: Page
{
public override void VerifyRenderingInServerForm(Control control)
{
//base.VerifyRenderingInServerForm(control);
}
}
and inside of it I override this method. I'm using my page and everything is fine.
The question is why ? Why does it have to be in a form? It doesn't have any inputs !
Also, if my ascx contains only <asp:Label ( runatServer) everything is fine and it doesn't require placing it in a Form.
What am I missing ?
It must not be inside of a form, but the only one who knows are you. This exception is also a way to prevent nasty errors and provide a clear error message. Only controls that can postback need to be nested in a HtmlForm control.
http://msdn.microsoft.com/en-us/library/system.web.ui.page.verifyrenderinginserverform%28v=VS.100%29.aspx
We are rendering usercontrols dynamically like this:
public string RenderControl(string pathcontrol)
{
string html;
var page = new Page();
var control = page.LoadControl(path);
page.Controls.Add(control);
// do stuff to the control (give it some data to work on)
using (var writer = new StringWriter())
{
HttpContext.Current.Server.Execute(page, writer, false);
html = writer.ToString();
}
return html;
}
This lets us the same user controls when rendering pages normally as we do when rendering responses to ajax calls. However, when adding controls which themselves contain a scriptmanagerProxy we run into the problem that the newed up Page object doesn't contain either a ScriptManager or the HtmlForm in which the ScriptManager needs to run.
Is there any way around this?
Yours
Andreas
As others have said you can add a ScriptManger dynamically easily enough [ Add ScriptManager to Page Programmatically? if your Page object is complete.
Can you try using BuildManager.CreateInstanceFromVirtualPath() to create the Page object instead? You issue may be how you create that object. There's a bit more to creating a new page than newing up the Page object.
Eg.
Page page
= BuildManager.CreateInstanceFromVirtualPath("~/Test.aspx", typeof(Page))
See also http://www.west-wind.com/weblog/posts/120530.aspx for a little more background.
Can you do something like this:
page.Form.Controls.AddAt(0, New ScriptManager())
Edit: I think you'd also need to add your control to the page's form, not just to the page itself, right? It's my understanding that the form is created with the page, but if not you should be able to just do:
page.Form = new HtmlForm()
You may also need to do something like:
page.Controls.Add(page.Form)
Sure, the trick is to add it in a page's Init event handler. You can use:
Page.Init += delegate {
// check for script manager
if( ScriptManager.GetCurrent(Page) == null ) {
ScriptManager m = new ScriptManager();
m.ScriptMode = ScriptMode.Release;
Page.Form.Controls.AddAt(0, m);
}
}
I'd recommend avoiding dynamically adding forms to your page if you can. For example, the above code snippet assumes a form is already present on the page.
Update
Sure, thanks for pointing that out Andreas. Here's an update. So, there is no setter for Page.Form - but you are correct in that you can add a new HtmlForm to the Controls collection. Once added, the Page.Form property is no longer null. That will allow you to add the ScriptManager dynamically as seen above. Here is a code sample that shows this working (ASPX file is a simple page without a server side form):
public partial class Pages_Test_DynamicFormSample : Page {
protected void Page_Init(object sender, EventArgs e) {
Controls.Add( new HtmlForm() );
ScriptManager m = new ScriptManager();
m.ScriptMode = ScriptMode.Release;
Form.Controls.AddAt(0, m);
}
protected void Page_Load(object sender, EventArgs e) {
// ScriptManager test
var t1 = new System.Web.UI.WebControls.TextBox();
var t2 = new System.Web.UI.WebControls.TextBox();
Form.Controls.Add( t1 );
Form.Controls.Add( t2 );
ScriptManager.GetCurrent(Page).SetFocus( t2 );
}
}
Enjoy - btw, setting the ScriptManager's ScriptMode to Release obviously isn't required. We do it just to avoid some JavaScript bugs found in the Debug version of the ASP.NET script runtime.
Is there any way through which I can get HTML of my current page. By current page I mean let's say I am working on Default.aspx and want to get HTML by providing a button on it.
How to get it.
EDITED in response to clarification of the requirements
You can override the page's render method to capture the HTML source on the server-side.
protected override void Render(HtmlTextWriter writer)
{
// setup a TextWriter to capture the markup
TextWriter tw = new StringWriter();
HtmlTextWriter htw = new HtmlTextWriter(tw);
// render the markup into our surrogate TextWriter
base.Render(htw);
// get the captured markup as a string
string pageSource = tw.ToString();
// render the markup into the output stream verbatim
writer.Write(pageSource);
// remove the viewstate field from the captured markup
string viewStateRemoved = Regex.Replace(pageSource,
"<input type=\"hidden\" name=\"__VIEWSTATE\" id=\"__VIEWSTATE\" value=\".*?\" />",
"", RegexOptions.IgnoreCase);
// the page source, without the viewstate field, is in viewStateRemoved
// do what you like with it
}
Not sure why you want what you want, but... this is off the top of my head, i.e. I didn't try this code.
Add a client-side onclick to your button to show markup and do something like this:
function showMarkup() {
var markup = "<html>" + document.getElementsByTagName("html")[0].innerHTML + "</html>";
alert(markup); // You might want to show a div or some other element instead with the markup variable as the inner text because the alert might get cut off.
}
If you need this rendered markup posted back to the server for some reason, store the encoded markup in a hidden input and post that back. You can register the script below on the server-side using ClientScriptManager.RegisterOnSubmitStatement . Here's the cleint-side code.
var markup = escape("<html>" + document.getElementsByTagName("html")[0].innerHTML + "</html>");
var hiddenInput = $get('hiddenInputClientId');
if (hiddenInput) {
hiddenInput.value = markup;
}
Hope that helps,
Nick
I'm still not sure what your objective is with this. But if you want the total rendered output of the page then your probably better of looking at some client side code as this would be run once the server has returned the fully rendered HTML.
Otherwise you could proably catch the page unload event and do something with the rendered content there.
More info needed on what you want from this.
I am writing a custom HTTP handler to provide an edit form for a grid using existing user controls. Basically, my handler creates the page, form, header, and other controls necessary to be able to render the existing user controls properly and at the end of ProcessRequest, I use Server.Execute to execute the page that was dynamically created. I am doing this because the solution where this resides is a user controls project and there are no pages, nor can we add any. This needs to be reusable for several projects.
This works great up until the point where the user controls added to this "page" require the usage of the postback mechanism. In the user control Page.IsPostBack is always false and control events (like a button click) are not handled. It is obvious that I am missing some critical piece from how a typical ASP.NET page works. The Page class is just an implementation of an IHttpHandler, but there is a lot of code that I don't think should be necessary to get the basic functionality to work here.
Any ideas?
Here's the basic code from my base HTTP handler. I have other classes that inherit from this base handler to add the actual user controls to the form of the page.
public void ProcessRequest(HttpContext context) {
context.Response.ContentType = "text/html";
HtmlGenericControl htmlPage = GetHtml();
AddTitle();
htmlPage.Controls.Add(_head);
HtmlGenericControl htmlBody = GetBody();
_form.Action = context.Request.Url.ToString();
_form.Method = "POST";
htmlBody.Controls.Add(_form);
htmlPage.Controls.Add(htmlBody);
AddAjaxManager();
AddScriptManager();
_page.Controls.Add(htmlPage);
//_page.ProcessRequest(context);
context.Response.CacheControl = "No-Cache";
context.Server.Execute(_page, context.Response.Output, true);
}
public bool IsReusable { get { return false; } }
To make this work, I inherited from Page instead of implementing IHttpHandler. You do still need to build out the entire HTML of the page, but you get all the wonderfulness (or not) of the ASP.NET WebForms page lifecycle when you do this.