CrossPagePostback in new window...
Hey All,
As you may know to postback a page to another page i..e
Cross Page Postback you set the PostBackUrl to the page you
would like to post the page to. But what if you would like
to post back the page and have it open in a new window. I
thought easy just set the forms target before it does the
postback and presto. But this is not posible as when you
overrides the target the nexttime you postback on that page
it will still open in a new window and also as the action of
the page is overriden for the Cross page postback to work
nothing works anymore.
To fix this I knew I had
to somehow remember the current action before a postback,
change the target, post the form, reset action and target.
This has prooved painful. I have created a custom Button
control and found that the scripts etc for doing the
crosspage postback are added in the AddAttributesToRender
method. I have overriden this event and changed it so that
it adds the scripts I would like. And then call the
base.AddAttributesToRender method. Problem with this is the
base method will add its script again, I think the only way
to stop this is do a complete override of the
AddAttributesToRender method and basically replicate what
the current control does. I am just doing this as a test at
the moment so am not doing this.
The other trick
is to make sure the form is doing a client side script
postback, this helps in being able to change the target and
reset it back again. Because I originally was not doing this
and could not get it working.
The code below
works and in my tests I could not find an issue using it.
Cross page postbacks in a new window work fine and
subsuquent postbacks also work ok. If anyone has any better
ideas on howto override this script another way please let
me know....
Code:
public class CustomButton : Button {
protected override void AddAttributesToRender(HtmlTextWriter
writer) {
System.Text.StringBuilder
currentScript = new
System.Text.StringBuilder();
// Get the current
onclientclick script.
currentScript.Append(this.OnClientClick);
// Get the script from our attributes and then remove the
onclick
// attribute.
if
(base.Attributes["onclick"] != null) {
currentScript.Append(base.Attributes["onclick"]);
base.Attributes.Remove("onclick");
}
// Get postback options, and make
sure we are doing a client script submit,
// this helped in fixing the issue of changing the action
and reverting back,
// granted it needs js
but the cross page postback needs js anyway.
PostBackOptions opt = this.GetPostBackOptions();
opt.ClientSubmit = true;
// Get the
postback script.
string postback =
this.Page.ClientScript.GetPostBackEventReference(opt,
false);
if (postback !=
null) {
if (this.PostBackUrl != string.Empty)
{
// Remember the current
action.
currentScript.Append("var
oldAction = document.forms[0].action;");
// Change target to a new form.
currentScript.Append("document.forms[0].target='_blank';");
}
// Add the
postback script.
currentScript.Append(postback + ";");
if
(this.PostBackUrl != string.Empty) {
// Reset target back to self.
currentScript.Append("document.forms[0].target='_self';");
// Reset the action.
currentScript.Append("document.forms[0].action=oldAction;");
// Return false to stop page submitting.
currentScript.Append("return false;");
}
}
// Add our
new onclick attribute.
if(currentScript.Length > 0)
writer.AddAttribute("onclick", currentScript.ToString());
base.AddAttributesToRender(writer);
}
protected override void
Render(HtmlTextWriter writer) {
// Render
using our custom decerator which allows us to emit the
onclick
// that the base button control
adds...
base.Render(new
ButtonWriter(writer));
}
private class ButtonWriter : HtmlTextWriter {
internal ButtonWriter(HtmlTextWriter writer)
: base(writer) {
}
public
override void AddAttribute(HtmlTextWriterAttribute key,
string value) {
if (key ==
HtmlTextWriterAttribute.Onclick)
return;
base.AddAttribute(key,
value);
}
public override
void AddAttribute(string name, string value) {
base.AddAttribute(name, value);
}
}
}
Update:
Thanks to this blog I found on
creating a decorator to hook into rendering:
http://haacked.com/archive/2006/01/18/usingadecoratortohookintoawebcontrolsrenderingforbetterxhtmlcompliance.aspx
I can do something like this:
private class ButtonWriter : HtmlTextWriter {
internal ButtonWriter(HtmlTextWriter writer)
: base(writer) {
}
public
override void AddAttribute(HtmlTextWriterAttribute key,
string value) {
if (key ==
HtmlTextWriterAttribute.Onclick)
return;
base.AddAttribute(key,
value);
}
}
So
when AddAttribute(HtmlTextWriterAttribute key, string value)
is called which looking at reflector that is what the base
button does I ignore any onclick attribute. In my code I use
the AddAttribute(string name, string value) signature so it
will still be added. Not perfect yet but closer. And then
just override the render method to make use of our new
decorator:
protected override void
Render(HtmlTextWriter writer) {
base.Render(new ButtonWriter(writer));
}
We now emit the old client script code and only have
our custom code. The code above has been updated to reflect
the new CustomButton...
---
Thanks
Stefan