UpdatePanel och GetPostBackEventReference

UpdatePanel uppför sig ibland underligt när man använder kontroller med egen rendering inuti. Microsoft ger den något kryptiska ledtråden:

All other controls work inside UpdatePanel controls. However, in some circumstances, a control might not work as expected inside an UpdatePanel control. These circumstances include the following:

  • Registering script by calling registration methods of the ClientScriptManager control.

  • Rendering script or markup directly during control rendering, such as by calling the Write method.

Antag att vi har en egen kontroll med kod som:

  1. C#
  1. protected override void Render( HtmlTextWriter writer )
  2. {
  3. writer.WriteBeginTag( "div" );
  4. writer.WriteAttribute( "onclick", Page.ClientScript.GetPostBackEventReference( this, "SomeArgument" ) );
  5. writer.Write( ">" );
  6. writer.Write( "..." );
  7. writer.WriteEndTag( "div" );
  8. }

Om vi använder en sådan kontroll inuti en UpdatePanel kommer den mycket riktigt inte att uppföra sig "as expected". Postbacken kommer att uppdatera hela sidan, inte bara innehållet i vår UpdatePanel.

Varför blir det så?

Det första som händer är att koden ovan genererar markup enligt:

  1. C#
  1. <div onclick="__doPostBack( 'ctl07','SomeArgument' )">...</div>

Funktionen __doPostBack postar normalt hela sidan. Ajax-scriptmanagern har dock inkluderat kod som ersätter __doPostBack med sin egen variant som så småningom hamnar i funktionen:

  1. C#
  1. function Sys$WebForms$PageRequestManager$_doPostBack(eventTarget, eventArgument) {
  2. ...
  3. var clientID = this._uniqueIDToClientID(eventTarget);
  4. var postBackElement = document.getElementById(clientID);
  5. ...

Parametern eventTarget har i vårt fall värdet "ctl07". Variabeln clientID kommer att ha samma värde (_uniqueIDToClientID byter ut lite specialtecken för att översätta mellan server och client-id:n). Däremot kommer postbackElement inte att hittas i dokumentet. Detta gör att koden går vidare och postar med orginalhanteraren.

Det krävs en djupare undersökning än det finns plats för här för att utreda varför AJAX-ramverket kräver ett motsvarande element i DOM:en, men om man är mest intresserad av att få det att fungera är lösningen att ändra koden till:

  1. C#
  1. protected override void Render( HtmlTextWriter writer )
  2. {
  3. writer.WriteBeginTag( "div" );
  4. writer.WriteAttribute( "id", ClientID );
  5. writer.WriteAttribute( "onclick", Page.ClientScript.GetPostBackEventReference( this, "SomeArgument" ) );
  6. writer.Write( ">" );
  7. writer.Write( "..." );
  8. writer.WriteEndTag( "div" );
  9. }

Markupen kommer nu att innehålla ett id motsvarande postbackens mottagare och vi kommer att få en asynkron postback.

2008-09-18