Introduction
I, like many of you, have had a need from time to time to create a postback in ASP.NET using JavaScript. When I searched for a solution online, I found many posts advocating the use of __doPostBack
, Microsoft's JavaScript function created to post back to the server. There are several problems with this, however, including:
- It is not documented by Microsoft, so it might not be supported in future versions.
- It skips client-side validation by the ASP.NET validators.
- It doesn't work with
UpdatePanel
s in ASP.NET AJAX.
You can get around the second and third items on the list without too much difficulty, if you're willing to duplicate Microsoft's code in your own functions, and write even more code that may or may not be supported by Microsoft in future versions. However, there is a little-known object available in the .NET framework called "PostBackOptions
" that is worth discussing. It is a little bit of a pain to use, so I have wrapped it in a custom control in order to limit the amount of manual coding necessary to use the object.
Using the Code
Control Properties
CausesValidation
- Just like a button, this specifies whether a call to this function causes the page to be validated.
ValidationGroup
- Just like a button, this specifies an optional validation group.
FunctionName
- This is the JavaScript function that will be called in order to post back to the server.
JavaScriptNamespaces
- This is a list of namespaces that help prevent function name collisions.
Postback
- This is a delegate that allows you to run code when the postback code is called.
PostbackButton
- This is a private button that the user won't see, but is necessary for PostBackOptions
to work.
JavaScriptNamespaces
Before we get to the code itself, I may need to explain a bit of unconventional JavaScript usage. Writing maintainable, readable code should be important to every programmer of every language, and writing JavaScript should be no different. A full explanation of writing maintainable JavaScript is beyond the scope of this article, but I strongly suggest simulating namespaces in your code to prevent name clashes and to better organize your functions. I do so here, so a bit of an explanation may be necessary. In order to make this happen, here's the code I wrote for the control:
public class JavaScriptPostback : CompositeControl
{
protected override void Render(HtmlTextWriter writer)
{
String functionName = JavaScriptNamespaces.Equals(String.Empty) ?
FunctionName : String.Concat(JavaScriptNamespaces, ".", FunctionName);
writer.Write("<script type='text/javascript'>");
AddNamespaces(JavaScriptNamespaces, writer);
writer.Write(String.Format("{0} = function() {1}", functionName, "{"));
writer.Write(this.Page.ClientScript.GetPostBackEventReference(options));
writer.Write("};");
writer.Write("</script>");
}
private void AddNamespaces(String originalNamespaces,
System.Web.UI.HtmlTextWriter output)
{
StringBuilder namespaces = new StringBuilder();
if (!originalNamespaces.Equals(String.Empty))
{
String[] namespaceList = originalNamespaces.Split('.');
foreach (String nspace in namespaceList)
{
if (namespaces.Length > 0)
namespaces.Append(".");
namespaces.Append(nspace);
output.WriteLine(String.Concat("if (typeof(",
namespaces.ToString(), ") === 'undefined') { ",
namespaces.ToString(), " = {}; }"));
}
}
}
}
If I were to use "MyCompany.Custom" as my JavaScriptNamespaces
, and "PostBack" as my FunctionName
, this JavaScript would be rendered on the page:
<script type='text/javascript'>
if (typeof(MyCompany) === 'undefined')
MyCompany = {};
if (typeof(MyCompany.Custom) === 'undefined')
MyCompany.Custom = {};
if (typeof(MyCompany.Custom.PostBack) === 'undefined')
MyCompany.Custom.PostBack = function()
{ };
</script>
What's happening here? I check for the existence of an object called "MyCompany", and if it doesn't exist, I created it as an (empty) object to serve as a container for the other "namespaces" and functions. The same thing happens for "MyCompany.Custom". After checking for the existence of "MyCompany.Custom.PostBack", I create and assign an "anonymous function" to that object. In order to call the function, you can just call "MyCompany.Custom.PostBack()". For a more thorough explanation, please consult David Flanagan's book on JavaScript, called "JavaScript: A Definitive Guide", published by O'Reilly. This is one of the few books I've seen that describes how to use the language rather than simply telling you what's available.
CompositeControl
The custom control inherits from CompositeControl
, since a button and some custom JavaScript are both part of the control. I could have made this control inherit from the Button
class instead, and overridden Render()
to also render the necessary JavaScript, but there would have been a lot of properties available to the control that would be useless to the user, so I chose to use CompositeControl
instead. For further information about the CompositeControl
, please consult Microsoft's documentation.
PostBackOptions
Here is the remaining code in Render()
:
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
PostBackOptions options = new PostBackOptions(PostbackButton);
options.PerformValidation = this.CausesValidation;
options.ValidationGroup = this.ValidationGroup;
this.Page.ClientScript.RegisterForEventValidation(options);
}
There really isn't much needed to use the class. It needs to know what validation options you are using, and the code:
this.Page.ClientScript.GetPostBackEventReference(options);
returns a JavaScript function that will post back to the server.
It is important to note that calling base.Render(writer)
isn't strictly necessary. The reason I do so is to render the button on the page, which allows the UpdatePanel
to find the control.
Handling the Postback
Now we need the ability to specify what happens when the control of the application returns to the server. We can do that by creating a delegate:
public event EventHandler Postback;
void PostbackButton_Click(object sender, EventArgs e)
{
if (Postback != null)
Postback(sender, e);
}
When we create the PostBackOptions
object, we pass in a private PostbackButton
, which is an instance of a Button
object. This is needed by the PostBackOptions
object to provide context for the JavaScript code. Since we are hiding this button from the programmer, we'll need to find another way to get the event information to him or her. One way to do that is to create our own delegate, and when the PostbackButton
delegate is called, call ours. In order to handle this event, write code similar to what you would have for a button click event:
protected void Page_Load(object sender, EventArgs e)
{
MyPostBackControl.Postback += new EventHandler(MyPostBackControl_PostBack);
}
void MyPostBackControl_PostBack(object sender, EventArgs e)
{
}
That's it. As you can see, the source code is available for download at the top of this article. The project itself is in Visual Studio 2008, but you should be able to use the code in a 2005 projects with no trouble. If you have any questions, comments, or improvements, please don't hesitate to contact me.