MSDN Magazin > Home > Ausgaben > 2007 > September >  ASP.NET: ScriptManager aktiviert AJAX in Ihren ...
ASP.NET
ScriptManager aktiviert AJAX in Ihren Webanwendungen
Ben Rush

Themen in diesem Artikel:
  • Die Rolle von ScriptManager in ASP.NET AJAX
  • Implizite und explizite Verwendung von ScriptManager
  • Unterstützung von Webdiensten in ASP.NET AJAX
  • Der Lebenszyklus der ASP.NET AJAX-Seite
In diesem Artikel werden folgende Technologien verwendet:
ASP.NET AJAX
Heutzutage kann der Benutzer einer Website genauso verantwortlich für ihren Inhalt, ihre Ausrichtung und ihren Erfolg sein wie der Herausgeber. Websites für Social Networking, Blogs, Onlinefotojournale und Wikis sind nur einige der neuen Arten von Websites, die täglich entstehen, und das ist erst der Anfang. Ihre Website kann über den besten Inhalt verfügen, doch wenn die Benutzer nicht an ihrem Wachstum und ihrer Entwicklung teilhaben können, wird sie schnell nutzlos werden.
Als Entwickler ist es Ihre Aufgabe, die Ihnen zur Verfügung stehenden Tools zu verwenden, um die Anforderungen des durchschnittlichen Benutzers zu erfüllen. Der beste Ausgangspunkt zur überzeugenderen Gestaltung einer Website ist der Webbrowser, da dieser Teil der Website dem Benutzer am nächsten ist. Leider ist es nicht immer einfach, die vielen Features zu nutzen, die in aktuellen Webbrowsern vorhanden sind. Das Schreiben von Code, der mit dem Browser interagiert, ist recht mühsam, da es eine Unzahl von Kombinationen aus Browser und Betriebssystem gibt, sodass praktisch keine Einheitlichkeit besteht. Es wäre hilfreich, eine einzelne Plattform zum parallelen Erstellen umfassender Client- und Webanwendungen zu haben, damit die Arbeit zur Perfektionierung der Endbenutzererfahrung in einer Browserumgebung nicht für eine andere dupliziert werden muss.
ASP.NET AJAX wurde von Microsoft veröffentlicht, um dieser Anforderung bei der Entwicklung von Webanwendungen gerecht zu werden. Ziel dieses Artikels ist es, Ihre Kenntnisse bezüglich einer zentralen Komponente von ASP.NET AJAX zu erweitern, die als ScriptManager-Steuerelement bezeichnet wird, und aufzuzeigen, wie damit die erweiterte Programmierung von ASP.NET AJAX erzielt wird. ScriptManager ist ein serverseitiges Steuerelement in Ihrem Webformular, das den Kern von ASP.NET AJAX aktiviert. Seine Hauptfunktion ist die Vermittlung aller anderen ASP.NET AJAX-Steuerelemente im Webformular und das Hinzufügen der richtigen Skriptbibliotheken zum Webbrowser, sodass der Clientteil von ASP.NET AJAX funktionieren kann. Oft wird ScriptManager zum Registrieren anderer Steuerelemente, Webdienste und Clientskripts verwendet.
Als serverseitiges Steuerelement reagiert ScriptManager auf Ereignisse im Lebenszyklus der ASP.NET-Seite und verwendet diese Ereignisse, um die Aktivitäten aller Steuerelemente, Optionen und Codeelemente zu koordinieren, die von ASP.NET AJAX eingesetzt werden. ScriptManager bindet ein bestimmtes Ereignis ein, wird benachrichtigt, wenn es stattfindet, und konfiguriert abhängig von der Umgebung einige Einstellungen. Dieser Prozess wiederholt sich mehrmals während des Renderingzyklus Ihrer ASP.NET-Seite. Die konfigurierten Einstellungen sind jedoch oft genau die Einstellungen, die zur nahtlosen Verwendung von ASP.NET AJAX erforderlich sind.
In diesem Artikel werden zuerst einige Hauptfeatures von ASP.NET AJAX vorgestellt, die vom ScriptManager-Steuerelement für Sie aktiviert werden, bevor der Lebenszyklus des Steuerelements auf dem Server erörtert wird. Wenn Sie die Interna von ScriptManager verstehen, werden Sie die für die Entwicklung von Webanwendungen bereitgestellten Optionen mehr zu schätzen wissen und erfahren, wie Sie diese optimal nutzen können.
Zuerst geht es um die Skripterstellung, die ein zentrales Element von ASP.NET AJAX ist. Tatsächlich hängt die gesamte Funktionalität von ASP.NET AJAX von den Skriptbibliotheken ab. Anschließend werden einige Features der AJAX-Unterstützung in ASP.NET AJAX und die Interaktion mit Webdiensten vorgestellt, bevor es schließlich um die Authentifizierung geht. In den einzelnen Erörterungen wird dargelegt, wie die Optionen mithilfe von ScriptManager angepasst werden können.

Skripterstellung mit ScriptManager
Der Codeblock in Abbildung 1 stellt die Standardmethode zum Definieren einer Klasse in ASP.NET AJAX dar. Die Interna der Clientskriptbibliothek gehen über den Umfang dieses Artikel hinaus, doch zusammenfassend sehen die allgemeinen Schritte, die zum Erstellen einer Klasse auf der Grundlage der ASP.NET AJAX-Skripterweiterungen notwendig sind, wie folgt aus:
  1. Registrieren des Namespace bei ASP.NET AJAX.
  2. Erstellen einer Konstruktormethode.
  3. Erstellen des Klassenprototyps durch Ausfüllen der Membermethoden und ihrer Funktionalität.
  4. Registrieren der Klasse bei ASP.NET AJAX.
  5. Benachrichtigen des von ScriptManager hinzugefügten Clientskripts, dass Sie das Ende der Typdefinition erreicht haben (der Aufruf von Sys.Application.notifyScriptLoaded).
Diese Klasse stellt Funktionen nur für den Client zur Verfügung. Doch mithilfe des ScriptManager-Steuerelements können Sie eine viel interessantere Seite der Skripterstellung in ASP.NET AJAX nutzen, wobei Ihre Klasse Funktionen sowohl für JavaScript auf dem Client als auch für Microsoft® .NET Framework-Code auf dem Server verfügbar machen kann. Wenn beispielsweise ein Verbraucher Ihres Steuerelements wie folgt eine Eigenschaft für eine Instanz festlegen würde:
public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        this.MyCustomButton.AlertMessage = "Hey, stop clicking me!"; 
    }
}
wäre es intuitiv, wenn es ebenfalls möglich wäre, wie folgt über Skript im Browser mit dieser Steuerelementinstanz zu interagieren:
<script type="text/javascript">
    function get_text()
    {
        var mb = $find("MyCustomButton");
        var text = mb.get_AlertMessage(); 
        // do something with the text
    }
</script>
Beachten Sie, dass derselbe Name (MyCustomButton) als Verweis auf das Steuerelement sowohl auf dem Server als auch auf dem Client verwendet wird. Die Interaktion mit der AlertMessage-Eigenschaft erfolgt ebenfalls so, als ob ihr Wert nahtlos die Server-/Clientgrenze überquert hat. Dies erscheint ganz natürlich, aber erst mit ASP.NET AJAX kann auf so ein einheitliches Server-/Clientprogrammiermodell zugegriffen werden. Zuvor wäre dafür sehr viel benutzerdefinierter Code erforderlich gewesen. Heute ist dieses einheitliche Server-/Clientparadigma in ASP.NET AJAX integriert und wird umfassend unterstützt.
Dieser engen Server-/Clientintegration liegen zwei neue Schnittstellen zugrunde: IScriptControl und IExtenderControl. Zum Verwenden dieser Schnittstellen erben Sie Ihr ASP.NET-Websteuerelement von ihnen, implementieren Sie die erforderlichen Schnittstellenmethoden, und registrieren Sie dann das Steuerelement bei dem ScriptManager-Steuerelement der Seite. Das folgende Gerüst für serverseitigen Steuercode implementiert beispielsweise die erforderlichen Methoden von IScriptControl:
class CustomButton : Button, IScriptControl
{
    IEnumerable<ScriptReference> IScriptControl.GetScriptReferences()
    {
        ...
    }

    IEnumerable<ScriptDescriptor> IScriptControl.GetScriptDescriptors()
    {
        ...
    }
}
Die Methoden „GetScriptDescriptors“ und „GetScriptReferences“ geben an das ScriptManager-Steuerelement alle erforderlichen Informationen zurück, um Ihren Steuercode logisch als ein Server- und Clientobjekt darzustellen. Wie Sie bereits gesehen haben, ist das Endergebnis eine Erfahrung, bei der Objektinstanzen die Server-/Clientgrenze überschreiten.
GetScriptReferences gibt eine Liste von Skriptdateien zurück, die von Ihrem Steuercode benötigt werden. Eine der Skriptdateien, die zurückgegeben werden muss, enthält die Definition Ihres Steuerelements als Skriptklasse, also die Clientskriptversion Ihres serverseitigen Steuerelements.
GetScriptDescriptors andererseits gibt so genannte ScriptDescriptors zurück – Objekte, die die Eigenschaften und Ereignisse Ihrer Clientklasse beschreiben. ScriptDescriptors halten praktisch Metadaten, einschließlich Eigenschaften und ihrer zugeordneten Werte, für Ihre Clientklasse fest.
Abbildung 2 zeigt ein vollständigeres Beispiel des Serversteuerelements, das zuvor skizziert wurde. Hier sehen Sie die ausgefüllten Textkörper der GetScriptDescriptors- und GetScriptReferences-Methode.
private string _alertMessage = null;

public string AlertMessage
{
    get { return _alertMessage; } set { alertMessage = value; }
}

public IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
    ScriptControlDescriptor descriptor = new ScriptControlDescriptor(
        "CustomButton", this.ClientID);
    descriptor.AddProperty("AlertMessage", this._alertMessage);
    return new ScriptDescriptor[] { descriptor };
}

public IEnumerable<ScriptReference> GetScriptReferences()
{
    ScriptReference reference = new ScriptReference(
        "MyCustomContent.JScript1.js", "MyCustomContent");
    return new ScriptReference[] { reference };
}

Im Text für GetScriptDescriptors wird ein ScriptDescriptor-Objekt instanziiert. Der Konstruktor für ScriptDescriptor nimmt den Namen für das beschriebene Steuerelement – CustomButton – an. Dann werden ScriptDescriptor die AlertMessage-Eigenschaft und ihr Wert (festgehalten von dem privaten Member „_alertMessage“) hinzugefügt. Die Eigenschaft, mit der vom Clientcode aus interagiert werden soll (AlertMessage), wurde jetzt für ASP.NET AJAX beschrieben.
Beachten Sie, dass im Text von GetScriptReferences ein ScriptReference-Objekt erstellt und zurückgegeben wird, das auf eine JS-Datei verweist. In dieser JS-Datei wird der clientseitige Code für dieses Steuerelement und folglich die gesamte Clientfunktionalität verwaltet.
Jetzt kann das Steuerelement bei ScriptManager registriert werden, damit ScriptManager das Steuerelement erkennt (dieser Schritt wird später noch näher erläutert). Beim Rendern der Seite reagiert das ScriptManager-Steuerelement auf verschiedene Ereignisse, bezüglich derer es benachrichtigt wird, und ruft GetScriptDescriptors und GetScriptReferences auf. Diese beiden Methoden geben dann an das ScriptManager-Steuerelement ScriptDescriptors und ScriptReferences zurück, die alle notwendigen Informationen zum Anbinden des Clientobjekts an seine Serverentsprechung enthalten. In dem hier verwendeten Beispiel beschreibt ScriptDescriptor ein benutzerdefiniertes Steuerelement namens „CustomButton“ mit einer AlertMessage-Eigenschaft. In ScriptDescriptor ebenfalls enthalten ist der Wert der Eigenschaft, die in GetScriptDescriptors festgelegt wurde.
Das ScriptManager-Steuerelement erhält außerdem einen Verweis auf eine externe JS-Datei namens „MyCustomContent.JSScript1.js“. Der Verweis auf die Skriptdatei hat die Form eines von GetScriptReferences zurückgegebenen ScriptReference-Objekts. Der Prototyp und die Fußzeile für die Clientklasse des Steuerelements in der JS-Datei wird wie in Abbildung 3 dargestellt definiert.
CustomButton.prototype = {
    initialize : function() {
        // initialize the base
        CustomButton.callBaseMethod(this,'initialize');
        
        this._onclick = 
            Function.createDelegate(this, this._onclick);
        
        Sys.UI.DomEvent.addHandler(
            this.get_element(), 'click', this._onclick); 
    },
    dispose : function() {
        // release the handlers
        $clearHandlers(this.get_element());

        // call to the base to do its dispose
        CustomButton.callBaseMethod(this,'dispose');
    },
    get_AlertMessage: function(){
        return this._alertMessage; 
    },
    set_AlertMessage: function(value){
        this._alertMessage = value; 
        return; 
    },
    _onclick: function(e){
        alert(this._alertMessage); 
        return; 
    }
}
CustomButton.registerClass("CustomButton",Sys.UI.Control); 
if(typeof(Sys)!=='undefined') Sys.Application.notifyScriptLoaded();

Wie Sie sehen, hat diese Clientklasse get_- und set_-Methoden für die AlertMessage-Eigenschaft. Das ScriptManager-Steuerelement legt den Wert dieser Eigenschaft darauf fest, was Sie auf dem Server durch das ScriptDescriptor-Objekt angegeben haben. Da AlertMessage vom Servercode verfügbar gemacht wurde, spiegelt sich das, worauf diese Eigenschaften festgelegt wurde, jetzt in der im Clientobjekt verfügbar gemachten AlertMessage-Eigenschaft wider.

AJAX und ScriptManager
Viele Entwickler beginnen bei erstmaliger Verwendung von ASP.NET AJAX mit dem UpdatePanel-Steuerelement. Wenn ein ScriptManager-Steuerelement auf der Seite enthalten ist und UpdatePanel Steuerelemente enthält, können die Steuerelemente innerhalb von UpdatePanel asynchron durch die Funktionen von AJAX aktualisiert werden. Auf der Designeroberfläche von Visual Studio® sieht das Setup in der Regel wie in Abbildung 4 aus.
Abbildung 4 Ein einfaches asynchrones Steuerelement 
Es könnte kaum einfacher sein. Wenn jemand auf die Schaltfläche in diesem Setup klickt, löst das Button-Steuerelement ein Postbackereignis aus, das vom UpdatePanel-Steuerelement empfangen wird. UpdatePanel sendet das Postbackereignis erneut als partielles Postback, und der Inhalt wird asynchron aktualisiert (ohne dass der Browser die Seite vollständig neu lädt).
Es gibt jedoch viele interessante Szenarios, in denen Ihren Erwartungen nicht entsprochen wird, sodass einiges durcheinander gerät. Es gibt beispielsweise eine neue und faszinierende Möglichkeit, Skriptverweise mithilfe des UpdatePanel-Steuerelements und AJAX zu unterbrechen. Früher hätten Sie der Seite infolge eines vollständigen Postbacks beispielsweise folgendes Skript hinzugefügt:
protected void Button1_Click(object sender, EventArgs e)
{
    Page.ClientScript.RegisterStartupScript(
        this.GetType(),"myscript","alert('hello world!');");
}
Diese neue Methode kann jedoch unter ASP.NET AJAX unterbrochen werden. Warum? Weil ClientScriptManager kein Steuerelement ist, das partielle Postbacks und partielles Rendering versteht. Daher wird Skriptcode, der bei ihm registriert ist, nicht in die Seitenantwort auf ein partielles Postback aufgenommen.
Stattdessen würden Sie ScriptManager nutzen, wenn die Schaltfläche Clientskript in die Seitenausgabe einführen soll:
protected void Button1_Click(object sender, EventArgs e)
{
    ScriptManager.RegisterStartupScript(
        this,this.GetType(),"myscript","alert('hello world!');",true);
}
Das Ergebnis ist gleich, aber die Methode ist etwas anders. Tatsächlich sind die Methoden des ClientScriptManager-Steuerelements jetzt im ScriptManager-Steuerelement als statische Methoden (z. B. RegisterStartupScript) enthalten. Wenn Sie ASP.NET AJAX verwenden und über partielle Postbacks mit Skript arbeiten wollen, müssen Sie jetzt stattdessen die vom ScriptManager-Steuerelement verfügbar gemachten Methoden verwenden.
Ein weiteres erweitertes Fallszenario ist das Steuerelementsetup in Abbildung 5. Unter normalen Umständen würde beim Klicken auf LinkButton ein vollständiges Postback des Webformulars erfolgen. Doch was geschieht, wenn Sie auf LinkButton klicken wollen und gleichzeitig UpdatePanel asynchron aktualisiert werden soll? Anders ausgedrückt: Sie möchten auf LinkButton klicken, wobei sich UpdatePanel so verhalten soll, als ob LinkButton darin enthalten wäre. Dazu verwenden Sie eine Methode im ScriptManager-Steuerelement mit der Bezeichnung „RegisterAsyncPostBackControl“:
Abbildung 5 Asynchrone Verwendung von LinkButton 
protected void Page_Load(object sender, EventArgs e)
{
    // register the LinkButton1 control as one that can 
    // cause partial postbacks. 
    ScriptManager1.RegisterAsyncPostBackControl(LinkButton1);
}
Das Klicken auf LinkButton führt jetzt zur asynchronen Aktualisierung von UpdatePanel, ganz so, als sei das LinkButton-Steuerelement tatsächlich in UpdatePanel enthalten.
Als Beispiel für umgekehrtes Verhalten könnten Sie auch ein Element in einem UpdatePanel die Aktualisierung der gesamten Seite veranlassen lassen (ein vollständiges Postback). Dazu verwenden Sie eine andere Methode im ScriptManager, die als RegisterPostBackControl bezeichnet wird:
protected void Page_Load(object sender, EventArgs e)
{
    // Button1 will cause a full post-back no matter
    // where it is on the page.  
    ScriptManager1.RegisterPostBackControl(Button1);
}
Dieser Code führt dazu, dass das Steuerelement, Button1, ein vollständiges Postback der Seite durchführt, selbst wenn sich Button1 innerhalb eines UpdatePanel befindet.
Jetzt geht es noch einen Schritt weiter. Sie wissen nun, wie die Steuerelemente, die das partielle Postbackereignis des UpdatePanel-Steuerelements auslösen, mithilfe von ScriptManager angepasst werden. Wie können Sie jedoch ein Steuerelement irgendwo auf der Seite – völlig außerhalb von UpdatePanel – aktualisieren, wenn UpdatePanel aktualisiert wird? Dies ist ebenfalls mithilfe des ScriptManager-Steuerelements möglich.
Mithilfe der RegisterDataItem-Methode von ScriptManager lassen sich Steuerelemente oder Daten außerhalb eines UpdatePanel mühelos aktualisieren. Mit RegisterDataItem können zusätzliche Daten Ihrer Wahl zum Client gesendet werden, wenn ein UpdatePanel-Steuerelement Daten zurückgibt. Die Daten können von einem Skript genutzt werden, das Sie auf dem Client schreiben.
Angenommen, Sie haben beispielsweise eine Situation wie die Steuerelemente in Abbildung 6. In diesem Beispiel soll Label mit dem Wert aktualisiert werden, auf den im Kalendersteuerelement geklickt wurde. Dies mag einfach scheinen, aber das Kalendersteuerelement befindet sich in UpdatePanel, was für Label nicht zutrifft. Was muss geschehen, damit Label von UpdatePanel aktualisiert wird? Die Antwort ist einfach: Wenn RegisterDataItem im serverseitigen Code verwendet wird, können zusätzliche Daten zum clientseitigen Code gesendet werden. Der Client kann die von RegisterDataItem gesendeten Daten nutzen und Label damit aktualisieren:
Abbildung 6 Aktualisieren von Steuerelementen außerhalb eines UpdatePanel 
protected void Calendar1_SelectionChanged(object sender, EventArgs e)
{
    ScriptManager1.RegisterDataItem(
        Label1, Calendar1.SelectedDate.ToShortDateString()); 
}
RegisterDataItem verwendet das Steuerelement, das aktualisiert werden soll, als erstes Argument und die Rohdaten, mit denen das Steuerelement aktualisiert werden soll, als zweites Argument. Das ScriptManager-Steuerelement empfängt die übergebenen Daten, wrappt sie und sendet sie als Teil seiner Antwort auf das partielle Postbackereignis zum Client. In Ihrem Clientcode würden Sie die vom ScriptManager-Steuerelement gesendeten Daten nach Abschluss des Ereignisses folgendermaßen abrufen:
<script type="text/javascript">
    Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(
        PageLoadingHandler); 
    function PageLoadingHandler(sender,args){
        var dataItems = args.get_dataItems(); 
        if($get('Label1')!==null){
            $get('Label1').innerHTML = dataItems['Label1']; 
        }
        return; 
    }
</script>    
Sehen Sie sich den Skriptcode an. Er führt mehrere Schritte durch. Er lässt sich über die PageRequestManager-Clientklasse für das pageLoading-Ereignis registrieren. Als Nächstes implementiert er PageLoadingHandler, den Ereignishandler für das pageLoading-Ereignis. Er ruft eine Name/Wert-Auflistung von Datenelementen aus dem zweiten Parameter (args) ab. Schließlich ruft er den gewünschten Wert mithilfe des Namens des Steuerelements ab, den Sie als erstes Argument für RegisterDataItem auf dem Server angegeben haben.

Webdienste und ScriptManager
Die Leichtigkeit, mit der Sie jetzt asynchron mit Webdiensten über Skript in ASP.NET AJAX interagieren sowie die Antwort (einschließlich Fehlern) behandeln und verarbeiten können, bietet Ihnen viele Möglichkeiten, Ihre Seiten wirklich nützlich und einzigartig zu gestalten. Von Punkt zu Punkt – Browser zu Server – nutzen Sie mit ASP.NET AJAX die aktuellen Technologien des neuen Web auf ganz intuitive Art.
Das ScriptManager-Steuerelement dient gewissermaßen als Registrierungsstelle für alle Dienste, die Sie in Ihrer ASP.NET AJAX-Anwendung benötigen. Abbildung 7 zeigt das Eigenschaftenmenü für das ScriptManager-Steuerelement, wie es in Visual Studio angezeigt wird.
Abbildung 7 ScriptManager-Eigenschaften 
Sie sehen, dass die Scripts-Auflistung hervorgehoben wurde. Darunter befindet sich zudem eine Auflistung für Services (Webdienste). Sie müssen bei ScriptManager alle Dienste registrieren, mit denen Sie von Ihrem Clientcode aus interagieren möchten. Um dem ScriptManager-Steuerelement einen Dienstverweis hinzuzufügen, erweitern Sie einfach die Services-Auflistung und fügen wie in Abbildung 8 dargestellt einen Verweis hinzu.
Abbildung 8 Hinzufügen eines Webdienstverweises (Klicken Sie zum Vergrößern auf das Bild)
Was genau hat dies zur Folge? Die vollständigen Einzelheiten werden gleich erläutert, doch wenn Sie die Quelle für eine Seite anzeigen, die über ScriptManager auf einen Dienst verweist, könnte ihr Inhalt wie folgt aussehen:
<script src="WebService.asmx/jsdebug" type="text/javascript"></script>
Dieser externe Verweis wurde von ScriptManager zur Seitenausgabe hinzugefügt, weil der Webdienst dort registriert ist. Beachten Sie, dass /jsdebug an den Namen des Webdiensts angehängt ist. Bei einer Veröffentlichungsversion wäre nur /js angehängt. Wenn die ASP.NET AJAX-Pipeline eine Anforderung für diesen Dienst mit dem angehängten /js-Text sieht, gibt sie eine Skriptklasse zurück, die die Methoden des Webdiensts umschließt (dies wird als Proxyskript bezeichnet). Der Text aller Methoden in dieser Proxyskriptklasse führt einen asynchronen Aufruf der entsprechenden Webdienstmethode durch. Daher rufen Sie zum Aufrufen der Webdienstmethoden einfach die entsprechende Methode in der Skriptklasse auf, die für Sie vom ASP.NET AJAX-Webdienstframework erstellt wurde. Es ist wirklich ganz einfach.
Wenn beispielsweise ein Dienst mit dem Namen „WebService“ wie folgt eine Methode namens „Add“ verfügbar macht,
[WebMethod]
public int Add(int a, int b) { return a + b; } 
könnten Sie diese mit folgendem Skript aufrufen:
function CallAdd()
{
    // method will return immediately
    // processing done asynchronously
    WebService.Add(0,6, OnMethodSucceeded, OnMethodFailed);
}
In diesem Beispiel umschließt die automatisch generierte Skriptproxyklasse, die Ihnen vom Framework übergeben wurde, die Webdienstmethodenaufrufe, sodass Sie mit ihr über ein Skript interagieren können. WebService.Add ruft folglich die entsprechende Webdienstmethode (Add) auf.
Beim Aufrufen eines Webdiensts werden in der Regel zwei Rückrufe definiert: einer für den Erfolgsfall und ein anderer für den Fehlerfall. Da der Webdienstaufruf asynchron ausgeführt wird, sind Rückrufe erforderlich, um zu erfahren, wie der Aufruf tatsächlich abgeschlossen wurde. Die folgenden Methoden implementieren beispielsweise einen Erfolgs- beziehungsweise einen Fehlerrückruf:
function OnMethodSucceeded(result, eventArgs)
{
    var label = Sys.UI.DomElement.getElementById('Label1');
    var sb = new Sys.StringBuilder(); 
    sb.append('You got back a value of '); 
    sb.append(result.toString()); 
    label.innerHTML = sb.toString();  
}

function OnMethodFailed(error)
{
    alert(error.get_message()); 
}
Die Anforderung wird gesendet, und das Ergebnis wird asynchron vom Webdienst empfangen, sodass dies im Endeffekt der Verwendung des UpdatePanels-Steuerelements recht ähnlich ist. Inhalte können mit den vom Webdienst empfangenen Daten aktualisiert werden, ohne dass ein vollständiges Browserpostback erforderlich ist. In dem zuvor dargestellten Beispielcode wird Label1 asynchron mit dem Ergebnis des Webdienstaufrufs von WebMethod Add aktualisiert. Das Endergebnis könnte wie in Abbildung 9 dargestellt aussehen. Es kommt nicht zu einem vollständigen Postback, und das Endergebnis ist absolut nahtlos.
Abbildung 9 Die aktualisierte Beschriftung 

Authentifizierung und Personalisierung
Für erweiterte Features von ASP.NET AJAX wie Authentifizierung und Personalisierung werden ebenfalls Webdienste verwendet. Der Authentifizierungsdienst in ASP.NET AJAX implementiert zwei Methoden: eine zum Anmelden eines Benutzers und eine zum Abmelden:
Sys.Services.AuthenticationService.login
Sys.Services.AuthenticationService.logout
In ASP.NET AJAX wird die Formularauthentifizierung verwendet, bei der der Benutzer angemeldet wird, wenn ein gültiges Formularauthentifizierungscookie durch den Webserver in die Sitzung des Browsers eingefügt wird. Die Art und Weise, in der die Cookiedaten in die Sitzung in ASP.NET AJAX eingefügt werden, ist dieselbe wie über ein vollständiges Postback, sodass kein physischer Unterschied bei den verwendeten Mechanismen besteht. Nachdem das Cookie in die Sitzung des Browsers eingefügt wurde, wird der Client beim Server authentifiziert und kann eingeschränkte Seiten und Inhalte anzeigen.
Die Möglichkeit, einen bestimmten Benutzer mit Clientskript zu authentifizieren und ihn an- oder abzumelden, bietet interessante Möglichkeiten für hochgradig interaktive, benutzerbasierte Systeme. Ein Beispiel für das Anmelden eines Benutzers mit AuthenticationService und Skript könnte so aussehen:
<script type="text/javascript">
    function MyMethod(username, password)
    {
        Sys.Services.AuthenticationService.login(username,
            password,false,null,null,null,null,"User Context"); 
    }
</script>
Die Schritte zur Aktivierung der Authentifizierung in Ihrer AJAX-Anwendung sind gut dokumentiert (siehe ajax.asp.net), sodass hier nicht näher darauf eingegangen werden muss. Wie Sie sehen, lässt sich die Formularauthentifizierung, wenn sie erst einmal aktiviert ist, leicht mit Skript implementieren.
Wenn der standardmäßige ASP.NET-Authentifizierungsdienst Ihre Anforderungen nicht erfüllt, können Sie einen eigenen erstellen und ihn beim ScriptManager-Steuerelement registrieren. Die für einen Webdienst zum Implementieren der Authentifizierungsfunktionalität erforderlichen Methoden sind hier dargestellt:
[WebMethod]
public bool Login(string userName, 
    string password, bool createPersistentCookie)
{
    ... // code to check user credentials and log user in
}

[WebMethod]
public void Logout()
{
    ... // code to log user out. 
}
Nach Ausfüllen Ihres Implementierungscodes müssen Sie Ihren neuen Authentifizierungsdienst beim ScriptManager-Steuerelement registrieren. Nach der Registrierung Ihres neuen Authentifizierungsdiensts nutzt der Code, der zuvor auf dem Client mit den Standardauthentifizierungsdiensten von ASP.NET AJAX interagiert hat, jetzt stattdessen Ihren Dienst. Daher ist auf der Clientskriptseite keine Änderung erforderlich, damit Ihr eigener benutzerdefinierter Authentifizierungswebdienst verwendet wird.
Hier ist ein Beispiel für das deklarative Registrieren als Authentifizierungsdienst über ScriptManager:
<asp:ScriptManager ID="ScriptManager1" 
  runat="server" >
    <AuthenticationService 
  Path="WebService.asmx" />
</asp:ScriptManager>
Ohne an dieser Stelle auf die Implementierung einzugehen, können Sie auch Ihre eigenen Profildienste in ASP.NET AJAX nutzen. Ein Profildienst kann genau wie ein Authentifizierungsdienst über das ScriptManager-Steuerelement registriert werden. Die Profildienste bieten Ihnen die Möglichkeit, eine Website auf einen bestimmten Benutzer zuzuschneiden. Da dies über das Clientskript erfolgt, ist die Wirkung für den Benutzer intuitiv und nahtlos. Beispiele sind auf ajax.asp.net verfügbar.

Funktionsweise
Das ScriptManager-Steuerelement hat zwei grundlegende Phasen. In der ersten Phase überprüft es, ob die Umgebung die Vielzahl der Features von ASP.NET AJAX unterstützen kann, und richtet alles Notwendige zur Unterstützung ein. In der zweiten Phase führt es die asynchrone Kommunikation mit dem auf dem Client ausgeführten Code durch, damit das Skript die notwendigen Seitenupdates durchführen kann. Da es sich um ein serverseitiges Steuerelement handelt und die Webprogrammierung in ASP.NET ereignisgesteuert ist, besteht der Kern des ScriptManager-Steuerelements darin, wie es sich für Ereignisse registriert und auf diese reagiert. Ein Überblick über die Ereignisse, auf die es reagiert, ist in Abbildung 10 dargestellt.
Abbildung 10 Definieren einer Klasse in ASP.NET AJAX 

Registrieren von Objekten mit ScriptManager
Sie können Skript- und Dienstverweise deklarativ mit ScriptManager hinzufügen. Sie können sie jedoch auch programmgesteuert über die Eigenschaften „Services“ und „Scripts“ im ScriptManager-Steuerelement hinzufügen. Zudem können Sie spezielle Daten auswählen, die an den Client gesendet werden sollen, und Steuerelemente entwickeln, die in Verbindung mit ScriptManager als spezielle Skriptsteuerelemente fungieren. Für alle diese Features ist es erforderlich, dass Sie auf Ihr Objekt mit dem ScriptManager-Steuerelement verweisen.
Wozu verwendet ScriptManager nun alle diese Verweise? Wenn eine Seite, die ein ScriptManager-Steuerelement und ASP.NET AJAX-Funktionen hostet, erstmals von einem Browser angefordert wird, wird in der Initialisierungsphase der untergeordneten Steuerelemente der Seite die OnInit-Methode von ScriptManager aufgerufen. OnInit führt eine Reihe wichtiger Schritte durch, einschließlich einer Überprüfung, dass sich nur ein ScriptManager-Steuerelement auf der Seite befindet und ob für die Seite derzeit ein partielles Postback durchgeführt wird. Doch der wichtigste von OnInit durchgeführte Schritt ist die Registrierung für eine Reihe von Ereignissen wie InitComplete, PreRenderComplete und PreRender, die von der Hostseite generiert werden. Das ScriptManager-Steuerelement muss wissen, wann diese Ereignisse auf seiner übergeordneten Seite stattfinden, weil die Funktion von ASP.NET AJAX von der Nutzung dieser Seitenereignisse abhängt.
Das entscheidende Seitenereignis für das Laden von Skript- und Dienstverweisen in den Browser ist das PreRenderComplete-Ereignis der Seite. Der Handler für dieses Ereignis ist OnPagePreRenderComplete (siehe Abbildung 11), und er ist eine Methode des ScriptManager-Steuerelements selbst.
private void OnPagePreRenderComplete(object sender, EventArgs e) 
{
    if (!IsInAsyncPostBack) 
    {
        if (SupportsPartialRendering) 
        {
            IPage.ClientScript.GetPostBackEventReference(
                new PostBackOptions(this, null, null, false, 
                false, false, false, true, null));
        }

        RegisterGlobalizationScriptBlock();
        RegisterScripts();     
        RegisterServices();
    }
    else RegisterScripts();
}

OnPagePreRenderComplete verarbeitet alle Skripts und Dienste, die Sie beim ScriptManager-Steuerelement bis zum PreRenderComplete-Ereignis der Seite registriert haben (einschließlich Skripts, die von Skriptsteuerelementen benötigt werden, Skripts, auf die von ScriptManagerProxy-Steuerelementen verwiesen wird usw.). Wenn die Seite kein partielles Postback durchläuft, wird ein Globalisierungsskriptblock zusammen mit allen Ihren Skripts und Diensten registriert. Wenn andererseits ein partielles Postback stattfindet, werden nur Skripts registriert. Skripts müssen sowohl für partielle als auch für vollständige Postbacks registriert werden, weil das ScriptManager-Steuerelement die Möglichkeit bietet, jederzeit Skript aufzunehmen.
Alle Ihre Skripts und Dienste werden während dieser Phase registriert, doch was bedeutet das? Wie wird die Registrierung des Skripts oder Verweises in eine Ausgabe auf der Seite übersetzt?
Denken Sie daran, dass Sie noch immer die erste Seitenanforderung und daher keine partiellen Postbacks verarbeiten. Somit können Sie immer noch ClientScriptManager zum Registrieren von Skripts verwenden. RegisterScripts beispielsweise wiederholt jedes Skript, das beim ScriptManager-Steuerelement registriert wurde, und übergibt es dann an die standardmäßige ClientScriptManager-Instanz der Seite. Beim Rendern der Seite wird ClientScriptManager aufgerufen, und die Skriptverweise werden der Seitenausgabe genau wie in der Zeit vor ASP.NET AJAX hinzugefügt.
RegisterScripts wird während eines asynchronen Postbacks (aufgrund der in Abbildung 11 dargestellten „else“-Klausel) ebenfalls aufgerufen. Die Methode ruft zwar immer noch ClientScriptManager auf, doch da ClientScriptManager nicht weiß, was bei einem asynchronen Postback zu tun ist, geschieht nichts. Stattdessen ruft RegisterScripts eine interne Methode (RegisterScriptIncludeInternal) auf, die den Skriptverweis zur späteren Verwendung in ein internes Array einfügt.
Wie sieht es mit Webdienstverweisen aus? Wie Sie wissen, wird durch Hinzufügen eines Webdienstverweises zu Ihrem ScriptManager-Steuerelement ein Verweis auf ein Skript zu der Seite hinzugefügt. Dieser Skriptverweis bewirkt, dass der Browser eine Anforderung bei der angegebenen URL durchführt. Das ASP.NET AJAX-Framework prüft die Klasse, die Ihren Webdienst implementiert, und gibt eine Proxyskriptklasse zurück, die Sie im Clientcode nutzen können. Der Browser lädt diese automatisch generierte Proxyklasse herunter, bei der es sich um Ihren Webdienstverweis handelt.
Zum Generieren des Skriptverweises für die automatisch generierte Proxyklasse durchläuft RegisterScripts alle vorhandenen Webdienstverweise und ruft schließlich folgende Methode auf:
private string GetProxyPath(Control containingControl, bool debug) 
{
    if (debug)
        return GetServicePath(containingControl, true) + 
            RestHandlerFactory.ClientDebugProxyRequestPathInfo;
    else
        return GetServicePath(containingControl, true) + 
            RestHandlerFactory.ClientProxyRequestPathInfo;
}
Der von GetProxyPath generierte Skriptverweis wird der Seite genau wie normale Skriptverweise hinzugefügt: durch Verwenden von ClientScriptManager für die erste Seitenanforderung und Einfügen des Verweises in ein internes Array.
Worum handelt es sich bei diesem internen Array? Eigentlich ist es eine Reihe von Arrays. Wie Sie wissen, verwendet ScriptManager für die erste Seitenanforderung die traditionelle Methode, bei der der ClientScriptManager-Klasse für die Seite einfach Skript- und Dienstverweise hinzugefügt werden. Doch die Verwendung von ClientScriptManager funktioniert nicht während eines partiellen Postbacks. Stattdessen muss ScriptManager eine andere Methode verwenden, und dazu dienen die internen Arrays. Wie Sie sehen werden, ist die Antwort auf ein partielles Postback ein wohlgeformter, leicht analysierbarer Datenblock, der vom Clientframework geprüft und verwendet wird. Das Render-Ereignis des ScriptManager-Steuerelements wird aufgerufen und ruft dann wiederum eine Membermethode (ProcessScriptRegistration) oder noch eine weitere Klasse (PageRequestManager) auf (siehe Abbildung 12).
private void ProcessScriptRegistration(HtmlTextWriter writer) 
{
    owner.ScriptRegistration.RenderActiveArrayDeclarations(
        updatePanelsToRefresh, writer);
    owner.ScriptRegistration.RenderActiveScripts(
        updatePanelsToRefresh, writer);
    owner.ScriptRegistration.RenderActiveSubmitStatements(
        updatePanelsToRefresh, writer);
    owner.ScriptRegistration.RenderActiveExpandos(
        updatePanelsToRefresh, writer);
    owner.ScriptRegistration.RenderActiveHiddenFields(
        updatePanelsToRefresh, writer);
    owner.ScriptRegistration.RenderActiveScriptDisposes(
        updatePanelsToRefresh, writer);
}

Hier wird der Skriptverweis in der Seitenausgabe für ASP.NET AJAX gerendert. Jede Methode übernimmt den HtmlTextWriter, der ihr für die Render-Phase übergeben wird, und schreibt den notwendigen Inhalt hinein. Jede Methode (z. B. RenderActiveScripts) verweist auf das jeweilige interne Array, das zuvor gefüllt wurde.

Einsetzen von AJAX in ASP.NET AJAX
Wenn Sie die HTTP-Debuggerproxyanwendung „Fiddler“ verwenden (fiddlertool.com/fiddler), können Sie den gesamten Webdatenverkehr verfolgen, der durch Internet Explorer® auf Ihrem Computer fließt. Navigieren Sie zu den AJAX-Beispielen auf ajax.asp.net, und beobachten Sie mit Fiddler, was geschieht. Sie können sehen, dass jedes Mal, wenn Sie ein partielles Postbackereignis aufrufen, ein HTTP POST-Paket auftritt. Das HTTP POST-Paket enthält die üblichen HTTP-Header, aber zusätzlich einen Header, den Sie möglicherweise noch nie gesehen haben:
x-microsoftajax: Delta=true
Dieser Header ist sehr wichtig. Wenn das ScriptManager-Steuerelement diesen Header erkennt, fügt es nicht passiv Verweise in die Seitenausgabe ein, sondern prüft die im Formular-POST-Paket gesendeten Daten und rendert eine Antwort an den Client in einem Format, das das Clientskript versteht.
Neben dem Schreiben von Dienst- und Skriptverweisen in die erste Seitenausgabe initialisiert das ScriptManager-Steuerelement auch die clientseitigen Funktionen. Beim erstmaligen Rendern stellt das ScriptManager-Steuerelement sicher, dass zwei Startskripts bei ClientScriptManager registriert sind. Ein Skript ruft eine Methode auf, die die clientseitige Laufzeit initialisiert. Das andere Skript initialisiert die clientseitige Version der PageRequestManager-Klasse, die bereits erörtert wurde. Zum Verständnis von AJAX ist das zweite Initialisierungsskript (das Skript, das PageRequestManager initialisiert) am wichtigsten.
Dieses Clientskript wird mithilfe einer Methode, die RenderPageRequestManagerScript heißt, in die Seitenausgabe geschrieben. Damit die Methode einfacher zu lesen ist, wurde ein großer Teil des Grundlagencodes entfernt, aber im Allgemeinen sieht sie so aus:
internal void RenderPageRequestManagerScript(HtmlTextWriter writer) 
{
    ...
    writer.Write(_owner.UniqueID);
    ...
    writer.Write(_owner.IPage.Form.ClientID);
    ...
    RenderUpdatePanelIDsFromList(writer, _allUpdatePanels);
    ...
    writer.Write(GetAsyncPostBackControlIDs(true));
    ...
    writer.Write(GetPostBackControlIDs(true));
    ...
}
Beachten Sie, wie sie UpdatePanels, PostBackControl-IDs und AsyncPostBackControl-IDs in die Seite schreibt. Dies sind alles Steuerelemente, die an der ASP.NET AJAX-Funktionalität beteiligt sein müssen. Die clientseitige PageRequestManager-Klasse ist für das Nachverfolgen aller Ereignisse verantwortlich, die von den bei ScriptManager registrierten Steuerelementen generiert werden. Die RenderPageRequestManagerScript-Methode initialisiert die PageRequestManager-Klasse, die auf dem Client mit genau den Steuerelementen angewendet wird, die von ihr überwacht werden sollen.
Wenn ein Postbackereignis auf dem Client ausgelöst wird, stellt PageRequestManager fest, ob dieses von einem der Steuerelemente verursacht wurde, die von dem in RenderPageRequestManagerScript geschriebenen Skript identifiziert wurden. Wenn dies der Fall war, bricht PageRequestManager das Postbackereignis ab und verpackt es neu. Die neu verpackten Daten aus dem Postbackereignis werden dann mithilfe der Clientklasse Sys.Net.WebRequest zum Server übertragen (diese ist öffentlich und kann von Ihrem clientseitigen Code verwendet werden). Der Header „x-microsoftajax“: Delta=true wird in der POST-Anforderung festgelegt, die über Sys.Net.WebRequest an den Server gesendet wird.
Auf der Serverseite wurde nun das ScriptManager-Steuerelement instanziiert und in die Steuerelementstruktur der Seite geladen. ScriptManager ist ein serverseitiges Steuerelement und wird daher über die Daten im Formular-POST-Paket durch LoadPostData benachrichtigt. Dabei handelt es sich um eine Methode von ASP.NET, die es einzelnen Steuerelementen ermöglicht, das Formular-POST-Paket per Filterung nach relevanten Informationen zu durchsuchen. (Beachten Sie, dass es sich um ein Standardereignis handelt, das nicht speziell für ASP.NET AJAX gilt.) Im Ereignisdiagramm in Abbildung 10 sehen Sie, dass LoadPostData sofort nach InitComplete auf der Seite erfolgt. In LoadPostData identifiziert das ScriptManager-Steuerelement die Steuerelemente, die das Formular-POST-Paket verursacht haben (einschließlich UpdatePanel, in dem sich das Steuerelement befunden hat, sofern zutreffend). Die Identität des Steuerelements, das das Postbackereignis verursacht hat, wird zur späteren Verwendung eingebunden.
Bisher erkennt das ScriptManager-Steuerelement, dass ein partielles Postback stattfindet, und hat die Steuerelemente identifiziert, die das Postback verursacht haben. Jetzt können Sie die Antwort für den Client erstellen. Das ScriptManager-Steuerelement überschreibt die standardmäßige Render-Methode für seine Hostseite vollständig und übernimmt das Rendern Ihrer ASP.NET-Seite. Bisher haben Sie das ScriptManager-Steuerelement einfach als ein Feature gesehen, mit dem verschiedene Einstellungen Ihrer ASP.NET AJAX-Anwendung angepasst werden können. Jetzt sehen Sie, dass Sie ihm diese verschiedenen Optionen geben, weil es zu Ihrer neuen Seitenklasse wird, und zwar mit einem eigenen Format zum Rendern von Steuerelementen im Seitenausgabedatenstrom.
Tatsächlich sorgt ScriptManager dafür, dass das standardmäßige Rendering für zwei Objekte überschrieben wird: zuerst für die Seite selbst und dann für das Webformular auf der Seite. Wenn das ASP.NET-Seitenframework daher die Seite zum Rendern auffordert, wird die eigene interne Implementierung von ScriptManager aufgerufen. Durch das Wissen, welche Steuerelemente das Postback verursacht haben, und die Kenntnis ihrer Zuordnungen (handelt es sich um ein UpdatePanel mit anderen Steuerelementen, oder ist die Quelle des Postbackereignisses irgendwie mit einem UpdatePanel verknüpft?) weiß das ScriptManager-Steuerelement, welche untergeordneten Steuerelemente zum Rendern aufgefordert und welche ignoriert werden sollten. Das Standardverhalten für eine Seitenklasse in ASP.NET besteht darin, dass jedes untergeordnete Steuerelement zum Rendern aufgefordert wird, während ScriptManager nur die als notwendig erachteten dazu auffordert.
Es sollte auch beachtet werden, dass das ScriptManager-Steuerelement beim überschriebenen Rendering der Formularobjekte alle zusätzlichen Daten verarbeitet und an den Client sendet, die Sie bei ihm über die RegisterDataItem-Methode registriert haben. Deshalb ist es wichtig, dass Ihre Daten zum Senden an den Client bereit sind, wenn das Formularobjekt aufgefordert wird, sich für den Client zu rendern. Die Daten, die an den Client gesendet werden sollen, werden in einem unaufbereiteten oder durch JSON (JavaScript Object Notation) serialisierten Format codiert.
Schließlich ruft das Clientframework die asynchrone Antwort vom Server ab und analysiert Daten. Das ScriptManager-Steuerelement hat in die Antwort alle IDs der Steuerelemente und neues Markup gepackt, sodass das Clientframework einfach Skripterstellungsvorgänge auf Grundlage des Dokumentobjektmodells des Browsers durchführen kann, um den Seiteninhalt zu aktualisieren. Da dieser gesamte Prozess asynchron stattgefunden hat, wird der Browser schnell und automatisch aktualisiert, und die Webseite wird benutzerfreundlicher.

Befolgen Sie das Skript
ASP.NET AJAX ist eine leistungsfähige Technologie. Um viele der ASP.NET AJAX-Features in Ihrer Webanwendung nutzen zu können, müssen Sie das ScriptManager-Steuerelement wie in diesem Artikel dargestellt einsetzen.
Das ScriptManager-Steuerelement handhabt viele Einzelheiten der ASP.NET AJAX-Implementierung für Sie. Wie Sie jetzt wissen, ist die Präsenz des ScriptManager-Steuerelements häufig in Instanzen spürbar, in denen das Standardverhalten eines Steuerelements wie UpdatePanel nicht genau Ihren Wünschen entspricht. Neben Skripterstellungsfeatures und AJAX unterstützt das ScriptManager-Steuerelement auch anspruchsvolle Features wie Authentifizierung und Personalisierung.
Skripts und Dienste müssen vom PreRenderComplete-Ereignis der Seite beim ScriptManager-Steuerelement registriert werden. Sie können der Seite einen Skriptverweis während vollständiger oder partieller Postbacks hinzufügen. Andere Objekte werden aufgefordert, alle ihre Skript- und Dienstverweise an das reale ScriptManager-Steuerelement zu übergeben, wenn Skripts registriert werden.
Schließlich wird ASP.NET AJAX auf dem Client durch Überschreiben der standardmäßigen Renderingimplementierung für seine Hostseite implementiert. Das ScriptManager-Steuerelement steuert nun den Renderingzyklus der Seite und sendet eine Antwort an den Client, die leicht analysiert und zum Aktualisieren der Elemente im Browser verwendet werden kann. Durch Überschreiben der standardmäßigen Seitenwiedergabe können die richtigen Steuerelemente für den Client in einem speziellen Format dargestellt werden, einschließlich Daten, die bei ScriptManager registriert sind.

Ben Rush ist Microsoft .NET-Berater für den mittleren Westen der USA und auf ASP.NET-Technologien spezialisiert. Sie können unter ben-rush.net/blog mehr über Ben erfahren.

Page view tracker