February 2007 - Posts

Sys.WebForms.PageRequestManagerParserErrorException - what it is and how to avoid it

If you've used the Microsoft ASP.NET AJAX UpdatePanel control, there's a good chance you've hit the "Sys.WebForms.PageRequestManagerParserErrorException" error.

What's a PageRequestManagerParserErrorException?

The UpdatePanel control uses asynchronous postbacks to control which parts of the page get rendered. It does this using a whole bunch of JavaScript on the client and a whole bunch of C# on the server. Asynchronous postbacks are exactly the same as regular postbacks except for one important thing: the rendering. Asynchronous postbacks go through the same life cycles events as regular pages (this is a question I get asked often). Only at the render phase do things get different. We capture the rendering of only the UpdatePanels that we care about and send it down to the client using a special format. In addition, we send out some other pieces of information, such as the page title, hidden form values, the form action URL, and lists of scripts.

As I mentioned, this is rendered out using a special format that the JavaScript on the client can understand. If you mess with the format by rendering things outside of the render phase of the page, the format will be messed up. Perhaps the most common way to do this is to call Response.Write() during Page's Load event, which is something that page developers often do for debugging purposes.

The client ends up receiving a blob of data that it can't parse, so it gives up and shows you a PageRequestManagerParserErrorException. Here's an example of what the message contains:

---------------------------
Microsoft Internet Explorer
---------------------------
Sys.WebForms.PageRequestManagerParserErrorException: The message received from the server could not be parsed. Common causes for this error are when the response is modified by calls to Response.Write(), response filters, HttpModules, or server trace is enabled.

Details: Error parsing near 'Hello, World!106|upd'.
---------------------------
OK  
---------------------------

If you ask me, this error message is not all that bad. After all, I'm the one that made it :) The details indicate what was being parsed when it decided to give up. You can see the part of the text from my Response.Write(), and immediately after that is part of the special format I keep mentioning.

Why do I keeping getting a PageRequestManagerParserErrorException?

Well, chances are you're doing one of the things mentioned in the error message. Here are the most common reasons and why they don't work:

  1. Calls to Response.Write():
    By calling Response.Write() directly you are bypassing the normal rendering mechanism of ASP.NET controls. The bits you write are going straight out to the client without further processing (well, mostly...). This means that UpdatePanel can't encode the data in its special format.
  2. Response filters:
    Similar to Response.Write(), response filters can change the rendering in such a way that the UpdatePanel won't know.
  3. HttpModules:
    Again, the same deal as Response.Write() and response filters.
  4. Server trace is enabled:
    If I were going to implement trace again, I'd do it differently. Trace is effectively written out using Response.Write(), and as such messes up the special format that we use for UpdatePanel.
  5. Calls to Server.Transfer():
    Unfortunately, there's no way to detect that Server.Transfer() was called. This means that UpdatePanel can't do anything intelligent when someone calls Server.Transfer(). The response sent back to the client is the HTML markup from the page to which you transferred. Since its HTML and not the special format, it can't be parsed, and you get the error.

How do I avoid getting a PageRequestManagerParserErrorException?

To start with, don't do anything from the preceding list! Here's a matching list of how to avoid a given error (when possible):

  1. Calls to Response.Write():
    Place an <asp:Label> or similar control on your page and set its Text property. The added benefit is that your pages will be valid HTML. When using Response.Write() you typically end up with pages that contain invalid markup.
  2. Response filters:
    The fix might just be to not use the filter. They're not used very often anyway. If possible, filter things at the control level and not at the response level.
  3. HttpModules:
    Same as response filters.
  4. Server trace is enabled:
    Use some other form of tracing, such as writing to a log file, the Windows event log, or a custom mechanism.
  5. Calls to Server.Transfer():
    I'm not really sure why people use Server.Transfer() at all. Perhaps it's a legacy thing from Classic ASP. I'd suggest using Response.Redirect() with query string parameters or cross-page posting.

Another way to avoid the parse error is to do a regular postback instead of an asynchronous postback. For example, if you have a button that absolutely must do a Server.Transfer(), make it do regular postbacks. There are a number of ways of doing this:

  1. The easiest is to simply place the button outside of any UpdatePanels. Unfortunately the layout of your page might not allow for this.
  2. Add a PostBackTrigger to your UpdatePanel that points at the button. This works great if the button is declared statically through markup on the page.
  3. Call ScriptManager.RegisterPostBackControl() and pass in the button in question. This is the best solution for controls that are added dynamically, such as those inside a repeating template.

Summary

I hope I've answered a lot of questions here and not angered too many of you. We're looking at ways to improve some of these situations in the next version of ASP.NET, but of course there are no guarantees. If you avoid changing the response stream, you're good to go. If you absolutely must change the response stream, simply don't do asynchronous postbacks.

 

Posted by Eilon with 265 comment(s)
Filed under: , , ,

ASP.NET AJAX JavaScript Class Browser (take 3)

By popular demand, I am releasing another update to the ASP.NET AJAX Class Browser. The new features:

  1. Support for ASP.NET AJAX 1.0
  2. Added icons to TreeView to indicate interface/class/enum/flags

I tested it on these browsers:

  • IE 6.0
  • Firefox 2.0.0.1
  • Safari 2.0.4 (on Mac OS X)
  • Opera 9.10

Safari and Opera don't really like the CSS-based scrolling, but otherwise work just fine.

Please see my previous post, ASP.NET AJAX JavaScript Class Browser (take 2), on some other features, such as how to include the Preview scripts in the list.

Download here (17KB).

Posted by Eilon with 9 comment(s)
Filed under: , , ,

Attributes to consider applying when writing a custom control

Almost every custom control has at least one additional public property, and that public property as well as the control itself should probably have at least a few attributes applied to them. Attributes tell the designer host (Visual Studio) or the parser (ASP.NET) interesting things about your control that might not be evident from just its name and its type. Launch any decent class browser tool and you'll see that every control that shipped in ASP.NET and ASP.NET AJAX has several attributes on it as well as their properties and events.

By applying the proper set of attributes you can significantly increase the usefulness of your control in several ways. For example, the DescriptionAttribute provides helpful text to the person designing the page. The ValidationPropertyAttribute is required when the person designing the page wants to validate the value of your control. Following is a list of the most useful and important attributes you can apply to your control and its properties and events.

Control attributes:

  • ControlValueProperty
     - Used by data source parameters to get the "intrinsic" value of the control. For example, DropDownList's "intrinsic" value is its SelectedValue property.
  • DefaultEvent
     - Set the event for which to create an event handler when double clicking the control in the designer.
  • DefaultProperty
     - Set the default selected property in the designer's property grid.
  • NonVisualControl
     - Hide the control at design time when "Non Visual Controls" is unchecked from the View menu.
  • ParseChildren
     - The full name is really "parse children as properties".
     - Set to true if the inner contents of the control represent properties as opposed to child controls.
     - True by default on controls deriving from WebControl; false by default otherwise.
  • PersistChildren
     - The full name is really "persist child controls".
     - Set to true if the designer should persist child controls as the inner contents.
     - False by default on controls deriving from WebControl; true by default otherwise.
     - 99.9% of the time PersistChildren has the opposite value of ParseChildren.
  • SupportsEventValidation
     - Indicates that the control's client-side calls to __doPostBack() should be validated on the server for security purposes.
  • Themable
     - Indicates that by default all the control's properties can be customized via themes. Individual properties can also be marked with this attribute to override the behavior.
     - True by default for controls that derive from WebControl.
  • ValidationProperty
     - Required when a control can be validated.
     - Somewhat similar to the ControlValueProperty in that they often point at the same property.
  • ViewStateModeById
     - Indicates that viewstate should be loaded based on control IDs as opposed to being loaded based on the index of the control in the child controls collection.

Property attributes:

  • Bindable
     - Indicates at design time only whether the property should appear by default in the Edit Databindings dialog.
  • Browsable
     - Indicates whether the property is visible in the property grid at design time.
  • Category
     - Determines in which category the property will appear when the property grid is in category mode.
  • DefaultValue
     - Get-only property: Since get-only properties are never persisted anyway, no default value is needed.
     - Get/Set value type property: Must be set.
     - Get/Set reference type property: Must be null so that it shows up as non-bold in the property grid.
  • Description
     - Determines the help text that will show in the property grid's lower help panel.
  • DesignerSerializationVisibility
     - Controls whether the property is persisted in the markup (see also PersistenceMode).
     - Use this to prevent get/set properties from being persisted at all.
  • EditorBrowsable
     - Affects whether the property appears in Intellisense.
  • IDReferenceProperty
     - Specifies that the property represents a control ID, and optionally the type of the target control. Not used by Visual Studio 2005.
  • MergableProperty
     - Affects whether the property shows up in the property grid when multiple controls are selected.
     - If the property is Browsable and is a reference type then set Mergable=false (except immutable reference types, such as string).
  • PersistenceMode
     - Controls how the property is persisted in the markup.
     - Simple-valued properties should use the default, which is Attribute.
     - Collection, template, and complex (e.g. styles) should use InnerProperty.
     - InnerDefaultProperty should never be used since it causes compatibility problems. For example, if in the next version of your control you want another inner property, it won't work properly.
     - EncodedInnerDefaultProperty should also rarely be used for similar reasons as InnerDefaultProperty.
     - In ASP.NET 2.0 support was added for strings to be InnerProperties, which is good for large multi-line string values, such as XmlDataSource's Data property.
  • Themable
     - Overrides the value of the attribute on the control to determine whether the property can be customized via themes.

Event attributes:

  • Browsable
     - Same as properties.
  • Category
     - Same as properties.
  • Description
     - Same as properties.
  • EditorBrowsable
     - Same as properties.

 

Posted by Eilon with 4 comment(s)
Filed under: ,
More Posts