The problem is that a common practice in ASP.NET is to have a <form runat="server"> tag "high up" in the HTML, usually in the Master page. HTML doesn't allow overlapping <form> tags, so my PayPal button won't work. Since the higher level <form> is in the Master page, it makes removing it pretty messy. As far as I know, there's no built-in "hide this tag, but render the tag's contents" feature for controls. In the past I've maintained two master pages: the normal one and a "no form" version, but I've since discovered a more elegant solution.
I created a class called GhostForm that has a property RenderFormTag. When RenderFormTag is set to false, it doesn't render the <form> tag or closing tag, but does render all of the contents. This works perfectly so I can have my <form runat="server"> high up for most pages, but I can fine-tune it when I need forms posting to other sites or multiple forms.
Here's the code for GhostForm:
using System; using System.Web.UI; using System.Web.UI.HtmlControls; /// <summary> /// This is a special form that can _not_ render the actual form tag, but always render the contents /// </summary> public class GhostForm : System.Web.UI.HtmlControls.HtmlForm { protected bool _render; public bool RenderFormTag { get { return _render; } set { _render = value; } } public GhostForm() { //By default, show the form tag _render = true; } protected override void RenderBeginTag(HtmlTextWriter writer) { //Only render the tag when _render is set to true if (_render) base.RenderBeginTag(writer); } protected override void RenderEndTag(HtmlTextWriter writer) { //Only render the tag when _render is set to true if (_render) base.RenderEndTag(writer); } }
49 comments:
Hi Jeremy,
I'm having the well-known PayPal button/form problem. Would you have a code sample of how to use your class?
Sure... Just throw that GhostForm.cs in your App_Code folder (if this is .NET 2.0+) then in your master page files you can do something like this:
...
<%@ Register TagPrefix="ns" Namespace="MyNameSpace" %>
...
<ns:GhostForm id="_mainForm" runat="server">
Content is king!
</ns:GhostForm>
Then in your codebehind you can do this:
_mainForm.RenderFormTag = false;
Thanks a million... now I won't have to mess with nested master pages :-)
Is there a step missing here? How does the page know how to get to what's in GhostForm.cs after you put it in App_Code? When I try to build a test project I am getting an "unknown server tag" error.
Hey Jeff... Anything in App_Code is accessible to all the pages in your site. Just put your GhostForm.cs in a namespace (for example, you can call it MyNameSpace) then this line at the top of your ASPX page will register the namespace so you can use the GhostForm tag:
<%@ Register TagPrefix="ns" Namespace="MyNameSpace" %>
I found a simpler way (no coding files, etc). Make the button invoke some javascript.
Check out this link
http://www.acxede.net/hack_12_add_a_paypal_button_to_your_office_live_basics_page
I just removed the form tag and made the submit button call the javacript that submits to paypal.
Ah, got it working, thanks. I was thrown by the fact that the ns:GhostForm tag got the red underline of doom in the VS editor.
if you remove the FORM tags of paypal how does the method of the 'buy now' buttons still working , how does it knows to post to paypal servers?
I'm using latest version of VS2005. Recently I had problems using the App_Code folder in situations like this before and hacked it:
http://www.codersbarn.com/post/ASPNET-20-Guest-Book-Application.aspx
This should not be necessary. App_Code is supposed to work with Web projects as well as websites. Create a non-system folder, put the class in there and reference it your code-behind. If that doesn't work, create a new library project with the custom class, and add new project to your solution. I'd be curious to know if this is a VS bug...
Thanks, this is great.
Its not working here. I get this message:
The name 'mainForm' does not exist in the current context
mainForm is the form ID in my MasterPage. This message occours when i call the following:
mainForm.RenderFormTag = false;
Any help? Can someone post an entire source code, please?
I'll create a sample over the weekend using Jeremy's GhostForm class.
Anthony,
CodersBarn.com
i cant wait! it will be really helpful!
Great stuff Jeremy. Extremely elegant solution - all great things are usually simple but require a genius. Cheers mate.
Hey, simple solution. Don't remove the form from the master page. Just put an empty form tag just prior to the Paypal button. Don't know why it works, but it does!
Anyone have a VB.Net example of how to use this??
I'm using a Web Application, not a Web Project.
Thanks
Ok wait - I found this solution using javascript that is SOOOOOOO brilliantly simple compared to any of this MasterPage and Ghostform lunacy that I can't believe this isn't the top search returned on Google for this issue:
http://www.nerdymusings.com/LPMArticle.asp?ID=29
Basically you just need to remove the form tags from the Paypal HTML and then replace the image button with an A link around an image. In the A link you have javascript that resets "theForm" which is ASP.NET's main form to point to Paypal instead of back to your page.
David
David,
The reason we didn't do this initially is because of the browser's need to have JavaScript enabled for this to work. There are all types of JavaScript hacks out there but the challenge was to find an elegant way around the form problem with ASP.NET...
Anthony :-)
This was extremely useful! Thanks! At least they say that .NET 3.5 supports programmatically setting the action.
Just so I'm clear you are using the
ns:GhostForm id="_mainForm" runat="server"
in place of where your
form id="form1" runat="server"
would normally be? Right?
Chad: Yes, That's correct!
I am trying to use your class. First I converted it to VB.Net then I compiled it to a DLL. These were two necessary steps for the project I am working on. Next I added the DLL to the target web project. Finally, I added the following code to the web control I am working with...
<gf:GhostForm id="mainForm" runat="server" RenderFormTag="False">
</gf:GhostForm>
I also added the
Dim mainForm As New GhostForm.GhostForm
mainForm.RenderFormTag = False
Per your suggestion in the Page_Load Event. However I get the following error when trying render the page...
InnerException: A page can have only one server-side Form tag.
so apparently it is recognizing the control, but not setting the property.
Any suggestions?
Wow thanks, You saved me from so much trouble, A customer asked me for a paypal button and do somework with the DB at the same time and he asked for it as quick as possible. Now its possible :-D
Thank you.
Whats your license on this? Is it free for all? Public Domain and all that? This looks liek a really nice elegant solution.
Andrew: There is no license. It's free for all! It's only like 2 lines of code, so feel free to use it as you wish for whatever you wish! If you want to give someone money, donate it to a local animal shelter :)
Hey Andrew,
Don't be giving Jeremy ideas!!
Anthony ;-)
Works great! Thanks for the nice solution.
Great work Jeremey. You're an asset to the community.
For all those using the Javascript work around, be mindful of Anti-Virus applications. AV may thing it's a phishing form.
Works great for me in all browsers except IE8. Has anyone else encountered problems in IE8? I wonder if it is related to the removal of the form tag or if I have something else wrong.
This is a totally "server side" implementation, so I can't imagine this has anything to do with your IE8 flog. I'd look at the generated HTML to track down the problem.
Hi guys,
Seems like a very ekegent solution. However when I put the register tag and then form tag code in my Master Page, it stops going into Design mode. Here is the exact Error message:
Error Creating the control _mainForm
Unable to cast object of type 'System.Web.UI.Design.HtmlIntrinsicControlDesigner' to type 'System.Web.UI.Design.ControlDesigner'.
However I have tested it and does run and there is no form tag on the page in Browser.
Jeremy please Help or anyone please.....................
This works for me like a charm, just get rid of the [form] junk paypal gives you and put in the code instead...
You can even change the button id for different subscriptions in code...
protected void Page_Load(object sender, EventArgs e)
{
this.Form.Action = "https://www.sandbox.paypal.com/cgi-bin/webscr";
this.Form.Method = "post";
}
Do you have a vb version of it? :)
I don't have a VB version, but it's like 4 lines of code, so I'm sure you could translate it without much trouble.
I don't understand why people don't spend the extra 5 minutes and post the entire source code?
I put the GhostForm class in app_code, then put %@ Register TagPrefix="ns" Namespace="GhostForm" % in the masterpage, then put ns:GhostForm id="form1" runat="server" /ns:GhostForm in place of the form tags.
I keep getting Parser Error Message: Unknown server tag 'ns:GhostForm'.
What am I missing?
Use a Web application and add the GhostForm class and reference it with a Using statement:
protected void Page_Load(object sender, EventArgs e)
{
...
// Workaround for PayPal form problem
GhostForm mainForm = new GhostForm();
mainForm.RenderFormTag = false;
}
This works for me too.
protected void Page_Load(object sender, EventArgs e)
{
this.Form.Action = "https://www.sandbox.paypal.com/cgi-bin/webscr";
this.Form.Method = "post";
}
Just a note for those thinking of using this method:
protected void Page_Load(object sender, EventArgs e)
{
this.Form.Action = "https://www.sandbox.paypal.com/cgi-bin/webscr";
this.Form.Method = "post";
}
BE CAREFUL: because every control that does a postback on your page will then post to the URL you set as the form action, not just your PayPal button.
AS someone said, adding an empty "form /form" tag before the actual form did the trick :)
Outstanding, 10x
Hi,
I am trying to do this and followed all of the comments in this thread and I am getting this error:
The base class includes the field 'mainForm', but its type (NameSpace.GhostForm) is not compatible with the type of control (NameSpace.GhostForm).
Any ideas?
Hi,
I am trying to use the GhostForm but got the same error that Braden got.
Did anyone solve it??
I have added GhostForm to my page but it's still saying that an aspx page can have only one form..
Can anyone tell me what's the problem
1.
Add GhostForm class to your App_code folder.
2. add <%@ Register TagPrefix="ns" Namespace="MyNameSpace" %> to your master page
3. Replace your form tag on master page with the following code
<ns:GhostForm id="_mainForm" runat="server">
3. on the actual page (default.aspx?) find the form control from code behind eg.
GhostForm mainForm = Page.Form as GhostForm;
mainForm.RenderFormTag = false;
Now you can send the request to paypal.
I am having the same problem as Braden, has anyone come up with a solution?
Thanks for this, it works perfectly - exactly what I was looking for!
The empty form trick solved my problem too...
This solution works very well, but there is a limitation it seems. When I need to do a postback, there is no form tag to support it, so it fails.
However, you can successfully render a dynamically-generated page using data-bound controls as long as you do not need a postback.
The empty form trick worked for me as well!
Post a Comment