MSDN Magazin > Home > Ausgaben > 2008 > February >  Cutting Edge: Anpassen von Steuerelementen mit ...
Cutting Edge
Anpassen von Steuerelementen mit AJAX-Extendern, Teil 2
Dino Esposito

Codedownload verfügbar unter: CuttingEdge2008_02.exe (434 KB)
Browse the Code Online
Im vergangenen Monat habe ich erklärt, wie ASP.NET-Eingabesteuerelemente wie z. B. Textfelder und Schaltflächen durch AJAX-Extender-Steuerelemente verbessert werden können. In diesem Monat werde ich unter Verwendung von Microsoft® .NET Framework 3.5 und der neuesten Version von ASP.NET, das zu dem Zeitpunkt, an dem dieser Artikel geschrieben wurde, als Beta 2-Version vorlag, weitere erweiterte Features hinzufügen, wie z. B. maskiertes Bearbeiten und automatische Vervollständigung. Darüber hinaus werde ich das neueste verfügbare Build des ASP.NET AJAX Control Toolkit verwenden. Informationen dazu, wie Sie die erforderlichen Softwaretoolkits beziehen können, finden Sie in der „Cutting Edge“-Ausgabe des vorigen Monats (siehe msdn.microsoft.com/msdnmag/issues/08/01/CuttingEdge).

Der Bedarf an maskierter Bearbeitung
In HTML können Eingabedaten lediglich über das <input>-Tag akzeptiert werden. In ASP.NET wird das Eingabetag einfach durch das TextBox-Steuerelement umschlossen. Dieses Steuerelement ist allerdings mit dem Problem behaftet, dass es nicht einschränkt, was ein Benutzer eingeben kann. Doch mit nur wenig JavaScript-Code können Sie unerwünschten Text herausfiltern. Dieses Thema habe ich im letzten Monat behandelt. In diesem Monat werde ich das maskierte Bearbeiten hinzufügen, das es ermöglicht, Zeichen während ihrer Eingabe herauszufiltern und sie auch in einem kulturspezifischen Format anzeigen zu lassen. Maskiertes Bearbeiten kann zum Filtern, zum Prüfen, zur automatischen Formatierung und zur Lokalisierung verwendet werden. Sie kann auch auf verschiedene alltägliche Datentypen wie z. B. Datumsangaben, Währungsangaben, Uhrzeitangaben, Postleitzahlen, Telefonnummern, Sozialversicherungsnummern oder USt-Nummern angewendet werden. Der MaskedEdit-Extender ist eine im AJAX Control Toolkit enthaltene freie Komponente, die an ein TextBox-Steuerelement angefügt werden kann und Ihnen dann ermöglicht, in einer Reihe häufiger Szenarios die Eingabe zu kontrollieren.

Der MaskedEdit-Extender
Im AJAX Control Toolkit unterstützt der MaskedEdit-Extender einige Datenformate, die vom MaskEditType-Enumerationstyp angegeben werden:
public enum MaskedEditType
{
    None,
    Date,
    Number,
    Time,
    DateTime
}
Sie können den Extender dazu verwenden, Zahlen, Datumsangaben, Uhrzeiten und Datum/Uhrzeit-Angaben einzugeben. Der Extender entscheidet auf Grundlage der bestehenden Kultureinstellungen über die Form seiner Ausgabe. Der folgende Codeausschnitt veranschaulicht die typische Verwendungsweise des MaskedEdit-Extenders bei einem Textfeld, das ein Datum akzeptiert:
<asp:TextBox runat="server" ID="TextBox1" />
<act:MaskedEditExtender ID="MaskedEditExtender1" runat="server"
    TargetControlID="TextBox1"                 
    Mask="99/99/9999"
    MaskType="Date" />
Abbildung A aufgelistet sind. Eine Eingabemaske wird vorrangig durch zwei Eigenschaften definiert: Mask und MaskType. Mask hat den Standardwert "" und gibt die Maske der Zeichen an, die der Extender akzeptieren kann. MaskType hat den Standardwert "" und gibt unter Verwendung eines der Werte, die durch die MaskedEditType-Enumeration definiert werden, den Maskentyp an.

Eigenschaft

Standardwert

Beschreibung

AcceptAMPM

False

Boolesche Eigenschaft, die angibt, ob ein AM/PM-Symbol verwendet werden soll.

AcceptNegative

Kein

Gibt an, ob ein Negativzeichen (-) möglich ist. Mögliche Werte werden durch die MaskedEditShowSymbol-Enumeration definiert: None, Left, Right.

AutoComplete

True

Boolesche Eigenschaft, die angibt, ob leere Maskenzeichen, die vom Benutzer nicht angegeben wurden, automatisch eingelesen werden sollen.

AutoCompleteValue

""

Gibt das Standardzeichen an, das verwendet werden soll, wenn die automatische Vervollständigung aktiviert ist.

Century

1900

Gibt an, welches Jahrhundert verwendet werden soll, wenn eine Datumsmaske nur zwei Stellen für die Jahresangabe besitzt.

ClearMaskOnLostFocus

True

Boolesche Eigenschaft, die angibt, ob die Maske entfernt werden soll, wenn das Textfeld den Eingabefokus verliert.

ClearTextOnInvalid

False

Boolesche Eigenschaft, die angibt, ob der Inhalt des Textfelds gelöscht werden soll, wenn der Benutzer einen unzulässigen Text eingibt.

ClipboardEnabled

True

Boolesche Eigenschaft, die angibt, ob Kopieren und Einfügen über die Zwischenablage möglich sein soll.

ClipboardText

Die Sicherheitseinstellungen Ihres Browser lassen die automatische Ausführung von Einfügeoperationen nicht zu.

Gibt den Eingabeaufforderungstext an, der verwendet werden soll, wenn etwas aus der Zwischenablage eingefügt wird.

CultureName

""

Dient zum Abrufen und Festlegen des Namens der zu verwendenden Kultur.

DisplayMoney

Kein

Gibt an, ob das Währungssymbol angezeigt werden soll. Mögliche Werte werden durch die MaskedEditShowSymbol-Enumeration definiert: None, Left, Right.

ErrorTooltipCssClass

""

Dient zum Abrufen und Festlegen der CSS-Klasse für die QuickInfo-Meldung.

ErrorTooltipEnabled

False

Boolesche Eigenschaft, die angibt, ob eine QuickInfo-Meldung angezeigt werden soll, wenn sich der Mauszeiger über einem Textfeld mit unzulässigem Inhalt befindet.

Filtered

""

Dient zum Abrufen und Festlegen der Liste der für den Maskentyp gültigen Zeichen, wenn der Platzhalter „C“ angegeben ist.

InputDirection

LeftToRight

Gibt die Texteingaberichtung an. Mögliche Werte werden durch die MaskedEditInputDirection-Enumeration definiert: LeftToRight, RightToLeft.

Mask

""

Gibt die für den Extender akzeptable Maske von Zeichen an.

MaskType

""

Gibt unter Verwendung eines der Werte, die durch die MaskedEditType-Enumeration definiert werden, den Maskentyp an.

MessageValidatorTip

True

Boolesche Eigenschaft, die angibt, ob eine Hilfsmeldung angezeigt werden soll, wenn der Benutzer etwas ins Textfeld eingibt.

OnBlurCssNegative

""

Dient zum Abrufen und Festlegen der CSS-Klasse, die verwendet wird, wenn das Textfeld den Eingabefokus verliert und einen negativen Wert enthält.

OnFocusCssClass

""

Dient zum Abrufen und Festlegen der CSS-Klasse, die verwendet wird, wenn das Textfeld den Eingabefokus erhält.

OnFocusCssNegative

""

Dient zum Abrufen und Festlegen der CSS-Klasse, die verwendet wird, wenn das Textfeld den Eingabefokus erhält und einen negativen Wert enthält.

OnInvalidCssClass

""

Dient zum Abrufen und Festlegen der CSS-Klasse, die verwendet wird, wenn der Text nicht gültig ist.

PromptCharacter

_

Dient zum Abrufen und Festlegen des Aufforderungszeichens, das für nicht angegebene Maskenzeichen verwendet wird.

UserDateFormat

Kein

Gibt ein bestimmtes Datumsformat an. Mögliche Werte werden durch die MaskedEditUserDateFormat-Enumeration definiert.

UserTimeFormat

Kein

Gibt ein bestimmtes Zeitformat an. Mögliche Werte werden durch die MaskedEditUserTimeFormat-Enumeration definiert.

Die MaskType-Eigenschaft teilt dem Extender mit, dass das Zielsteuerelement einen bestimmten Datentyp akzeptiert. Die Mask-Eigenschaft (vom Typ „String“) gibt die Sequenz von Zeichen an, die eine gültige Eingabe für das Textfeld darstellt. So sind beispielsweise sowohl „12/6/07“ als auch „12-09-2007“ gültige Datumsangaben, verwenden aber beide unterschiedliche Eingabemasken.
Um eine Maske zu erstellen, verwenden Sie vordefinierte Symbole als Platzhalter. Die Liste der unterstützten Symbole ist in Abbildung 1 zu sehen. Zum Beispiel hat die Maske „999,999.99“ zur Folge, dass Ihr Code eine Zahl mit einem Dezimaltrennzeichen und höchstens einem Tausendertrennzeichen akzeptiert. Abbildung 2 zeigt die endgültige Benutzeroberfläche, die von einem mit einem maskierten Editor erweiterten Textfeld präsentiert wird. Das Aussehen des Währungssymbols wird durch die DisplayMoney-Eigenschaft gesteuert, und jedes Zeichen, das der Benutzer eingeben muss, wird durch ein Eingabeaufforderungszeichen repräsentiert. Das Standardzeichen für die Eingabeaufforderung ist der Unterstrich, aber dies können Sie durch die PromptCharacter-Eigenschaft ändern.

Symbol Beschreibung
9 Ein numerisches Zeichen
L Ein Buchstabe
$ Ein Buchstabe oder ein Leerzeichen
C Ein durch die Filtered-Eigenschaft definiertes benutzerdefiniertes Zeichen, bei dem zwischen Groß- und Kleinschreibung unterschieden wird
A Ein Buchstabe oder ein benutzerdefiniertes Zeichen, der bzw. das durch die Filtered-Eigenschaft definiert wird
N Eine Ziffer oder ein benutzerdefiniertes Zeichen, die bzw. das durch die Filtered-Eigenschaft definiert wird
? Ein beliebiges Zeichen
/ Ein Datumstrennzeichen gemäß der aktuellen Kultur
: Ein Uhrzeittrennzeichen gemäß der aktuellen Kultur
. Ein Dezimalstellentrennzeichen gemäß der aktuellen Kultur
, Ein Tausendertrennzeichen gemäß der aktuellen Kultur
\ Ein ESC-Zeichen
{ Das Anfangstrennzeichen für die Wiederholung von Masken
} Das Endetrennzeichen für die Wiederholung von Masken
Abbildung 2 Der MaskedEdit-Extender in Aktion (Klicken Sie zum Vergrößern auf das Bild)
Für Datumsangaben können Sie neben den in der MaskedEditUserDateFormat-Enumeration enthaltenen vordefinierten Formaten, die im Folgenden gezeigt werden, auch zusätzliche Eigenschaften wie z. B. AcceptAMPM, Century und sogar ein benutzerdefiniertes Benutzerformat verwenden:
public enum MaskedEditUserDateFormat
{
    None,
    DayMonthYear,
    DayYearMonth,
    MonthDayYear,
    MonthYearDay,
    YearDayMonth,
    YearMonthDay
}
Viele der Einstellungen, die die vom MaskedEdit-Extender angewendete Formatierung beeinflussen, werden von der aktuellen Kultur abgeleitet. Die CultureName-Eigenschaft gibt die Kultur an, die angewendet werden soll. Beachten Sie, dass diese Einstellung die Kultur, die über das UICulture-Attribut in der @Page-Direktive definiert wurde, außer Kraft setzt.

Überprüfen der maskierten Eingabe
Während der maskierte Extender dynamische Formatierungsfunktionen bereitstellt, stellt eine weitere Komponente – das maskierte Validierungssteuerelement – sicher, dass jeder eingegebene Text zum erwarteten Typ zurückanalysiert werden kann:
  <act:MaskedEditValidator
      ID="MaskedEditValidator1" runat="server"
      ControlExtender="MaskedEditExtender1"
      ControlToValidate="TextBox2"
      IsValidEmpty="False"
      EmptyValueMessage="Number is required "
      InvalidValueMessage="Number is invalid" /> 
Abbildung B aufgelistet. Die Eigenschaft „Text“ eines maskierten Textfelds gibt formatierten Text zurück. Bei einem Datum gibt die Eigenschaft etwas in der Form von „02/04/2007“ zurück, und bei einem Zahleneingabefeld gibt sie einen Text wie etwa „1,200.00“ zurück. Das Währungssymbol ist in der Eigenschaft „Text“ nicht enthalten, obwohl es für den Benutzer auf der Seite angezeigt wird.

Eigenschaft

Beschreibung

AcceptAMPM

Gibt an, ob die AM/PM-Angabe bei einem Uhrzeitwert akzeptiert werden soll oder nicht.

ConTRolToValidate

Gibt die ID des zu validierenden Textfelds an.

ConTRolExtender

Gibt die ID des mit dem Textfeld verknüpften MaskedEditExtender-Steuerelements an.

ClientValidationFunction

Dient zum Abrufen und Festlegen des Namens der Client-Javascript-Funktion, die für die benutzerdefinierte Prüfung verwendet wird.

EmptyValueBlurredText

Dient zum Abrufen und Festlegen der Meldung, die angezeigt wird, wenn das Textfeld nicht den Eingabefokus besitzt und leer ist.

EmptyValueMessage

Dient zum Abrufen und Festlegen der Meldung, die angezeigt wird, wenn das Textfeld den Eingabefokus besitzt, aber leer ist.

InitialValue

Dient zum Abrufen und Festlegen des anfänglichen Werts des Textfelds.

InvalidValueMessage

Dient zum Abrufen und Festlegen der Meldung, die angezeigt wird, wenn das Textfeld den Eingabefokus besitzt, aber sein Inhalt unzulässig ist.

InvalidValueBlurredMessage

Dient zum Abrufen und Festlegen der Meldung, die angezeigt wird, wenn das Textfeld nicht den Eingabefokus besitzt, aber sein Inhalt unzulässig ist.

IsValidEmpty

Gibt an, ob das Textfeld leer bleiben darf.

MaximumValue

Dient zum Abrufen und Festlegen des Maximalwerts der Eingabe.

MaximumValueBlurredMessage

Dient zum Abrufen und Festlegen der Meldung, die angezeigt wird, wenn der Maximalwert überschritten wird und das Textfeld nicht den Fokus besitzt.

MaximumValueMessage

Dient zum Abrufen und Festlegen der Meldung, die angezeigt wird, wenn der Maximalwert überschritten wird und das Textfeld den Fokus besitzt.

MinimumValue

Dient zum Abrufen und Festlegen des Minimalwerts der Eingabe.

MinimumValueBlurredText

Dient zum Abrufen und Festlegen der Meldung, die angezeigt wird, wenn der Minimalwert unterschritten wird und das Textfeld nicht den Fokus besitzt.

MinimumValueMessage

Dient zum Abrufen und Festlegen der Meldung, die angezeigt wird, wenn der Minimalwert unterschritten wird und das Textfeld den Fokus besitzt.

ValidationExpression

Dient zum Abrufen und Festlegen des regulären Ausdrucks, der zum Überprüfen der Eingabe verwendet wird.

TooltipMessage

Dient zum Abrufen und Festlegen der Meldung, die angezeigt wird, wenn das Textfeld den Eingabefokus besitzt.

Wie können Sie also den Wert, der von der Eigenschaft „Text“ zurückgegeben wird, in den logischen Datentyp (Datum oder Dezimalwert) analysieren? Bei den Typen „DateTime“ und „Decimal“ können Sie die statische Parse-Methode verwenden. Sie müssen allerdings darauf achten, welche Kultur Sie verwenden. So kann beispielsweise „02/04/2007“ sowohl der vierte Februar (US-amerikanische Kultur) als auch der zweite April (europäische Kultur) sein. Es kann allerdings nicht garantiert werden, dass die Kulturen, die auf der Eingabeseite und der Serverseite verwendet werden, übereinstimmen. Es besteht also das Risiko, dass der Benutzer das Datum gemäß der europäischen Kultur eingibt, es aber als Datum der US-amerikanischen Kultur verarbeitet wird. Es kann jedoch noch schlimmer kommen: Wenn beispielsweise der Wert 1200 unter Verwendung von Dezimal- und Tausendertrennzeichen der italienischen Kultur in ein numerisches Textfeld eingegeben wird, wird möglicherweise ein Ausnahmefehler ausgelöst, weil der Parser des Typs „Decimal“ standardmäßig zur US-amerikanischen Kultur zurückwechselt. Ich werde Ihnen zeigen, wie Sie diese Probleme vermeiden können.
Hierbei ist ausschlaggebend, daran zu denken, dass Extender automatisch auf die Kultur „en-US“ zurückschalten, es sei denn, die CultureName-Eigenschaft wird explizit festgelegt. Auf dem Server schaltet das System standardmäßig auf den Wert zurück, den die UICulture-Eigenschaft in der Page-Klasse hat.
Rufen Sie zuerst in Ihrer codebehind-Klasse ein Objekt namens „CultureInfo“ ab, das die Kultur angibt, die für die Benutzeroberfläche verwendet wird. Sie können wie folgt vorgehen:
string culture = "en-us";
if (!String.IsNullOrEmpty(MaskedEditExtender1.CultureName))
    culture = MaskedEditExtender1.CultureName;
CultureInfo info = new CultureInfo(culture);
Rufen Sie danach die Parse-Methode auf, und geben Sie dabei auf Grundlage der ausgewählten Kultur einen Formatanbieter an:
NumberFormatInfo numberInfo = info.NumberFormat;
DateTimeFormatInfo dateInfo = info.DateTimeFormat;
DateTime when = DateTime.Parse(TextBox1.Text, dateInfo);
decimal amount = Decimal.Parse(TextBox2.Text, numberInfo);
Abbildung 3 zeigt, wie sich dieselbe Seite verhält, wenn für die Eingabe unterschiedliche Kulturen verwendet werden.
Abbildung 3 Zurückanalysieren von Daten zu .NET-Typen bei Verwendung unterschiedlicher Kulturen (Klicken Sie zum Vergrößern auf das Bild)

Automatische Vervollständigung von Textfeldern
Die automatische Vervollständigung ist ein Feature, mit dem Sie sicher vertraut sind. Es sagt anhand der ersten Zeichen, die der Benutzer eingibt, das vollständige Wort voraus. Internet Explorer zeichnet einen Verlauf aller Eingaben in der Adressleiste und den Formularfeldern auf, um Werte für die automatische Vervollständigung bereitzustellen.
Dieses Feature beruht natürlich vollständig auf dem Browser und kann für <input>- und <form>-Tags ein- oder ausgeschaltet werden, indem das autocomplete-Attribut ein- oder ausgeschaltet wird. Obwohl das autocomplete-Attribut kein standardmäßiges HTML-Attribut ist, wird es heutzutage von fast allen Browsern unterstützt.
Der AutoComplete-Extender im AJAX Control Toolkit stellt dasselbe Verhalten für Textfeldsteuerelemente bereit, überlässt aber dem Entwickler die Verantwortung für die gesamte Logik, mit der dem Benutzer Vorschläge unterbreitet werden. Der Extender erstellt einen Fensterbereich, der einer Dropdownliste ähnelt, und positioniert ihn direkt unter dem Textfeld. Sie können den Fensterbereich nach Belieben gestalten und animieren. Hier ist der Code zum Zuordnen eines Autocomplete-Extenders zu einem Textfeld:
<act:AutoCompleteExtender runat="server" ID="AutoComplete1"
    TargetControlID="TextBox1"  
    EnableCaching="true"
    MinimumPrefixLength="1"
    ServicePath="Suggestions.asmx" 
    ServiceMethod="GetSuggestions" />
Der Extender wird an einen Webdienst gebunden, der die Wörter zum Füllen der Liste bereitstellt. Die MinimumPrefixLength-Eigenschaft teilt dem Steuerelement mit, wann es den Webdienst aufrufen soll. Der bereits eingegebene Text wird als Eingabe für die angegebene Webdienstmethode verwendet. Die Antwort wird dazu verwendet, die Dropdownliste zu füllen. Zwischenspeicherung kann ebenfalls aktiviert werden. Dies hat zur Folge, dass bei mehrmaliger Eingabe desselben Präfixes der Webdienst nur ein einziges Mal aufgerufen wird. Ferner haben Sie in Abhängigkeit von der Art, in der der Webdienst seine Daten abruft, auch die Möglichkeit, Zwischenspeicherung auf dem Server zu aktivieren, um sich mehrere zusätzliche Zugriffe auf eine Datenbank oder einen anderen Remotedatenspeicher zu ersparen.
Abbildung C zu sehen. Hier muss angemerkt werden, dass andere Extender zusätzlich zu den hier aufgelisteten Eigenschaften noch weitere Eigenschaften besitzen. Es gibt beispielsweise eine Eigenschaft namens „TargetControlID“, mit der die ID des erweiterten Steuerelements abgerufen und festgelegt werden kann, sowie eine Eigenschaft namens „Enabled“, die es ermöglicht, die Funktionen des Extenders programmgesteuert ein- oder auszuschalten, und die standardmäßig auf „True“ gesetzt ist. Ferner gibt es eine Eigenschaft namens „BehaviorID“, die den Namen des clientseitigen JavaScript-Objekts festlegt, von dem das erweiterte Verhalten bereitgestellt wird. Zum Schluss muss ich noch zwei weitere interessante Eigenschaften nennen: ClientState und EnableClientState. ClientState ist eine Zeichenfolgeneigenschaft, die den Clientstatus des Extenders enthält. Der Status wird über ein ausgeblendetes Feld gespeichert, dessen Name mit der ClientStateFieldID-Eigenschaft festgelegt werden kann. EnableClientState ist eine boolesche Eigenschaft, die steuert, ob der Clientstatus aktiviert wird.

Eigenschaft

Beschreibung

Animations

Legt Animationen fest, die beim Ein- und Ausblenden des Flyout abgespielt werden sollen.

CompletionInterval

Dient zum Abrufen und Festlegen der Anzahl von Millisekunden, nach deren Ablauf der Extender über den gebundenen Webdienst Vorschläge erhält.

CompletionListCssClass

Gibt die CSS-Klasse an, die zum Gestalten des Flyout der Vervollständigungsliste verwendet werden soll.

CompletionListHighlightedItemCssClass

Gibt die CSS-Klasse an, die zum Gestalten des hervorgehobenen Eintrags im Flyout der Vervollständigungsliste verwendet werden soll.

CompletionListItemCssClass

Gibt die CSS-Klasse an, die zum Gestalten des Eintrags im Flyout der Vervollständigungsliste verwendet werden soll.

CompletionSetCount

Dient zum Abrufen und Festlegen der Anzahl von Vorschlägen, die vom gebundenen Webdienst abgerufen werden sollen. Standardeinstellung: 10.

ContextKey

Zeichenfolgeneigenschaft, die alle seiten- oder benutzerspezifischen Informationen angibt, die an den gebundenen Webdienst übergeben werden sollen.

DelimiterCharacters

Gibt ein oder mehrere Zeichen an, die der Extender verwendet, um den Textfeldinhalt mit Token zu versehen. Der Webdienst verwendet das letzte dieser Token, um Vorschläge zu unterbreiten. Standardmäßig nicht festgelegt.

EnableCaching

Boolesche Eigenschaft, die angibt, ob clientseitiges Zwischenspeichern aktiviert ist. Standardeinstellung: True.

FirstRowSelected

Boolesche Eigenschaft, die angibt, ob die erste Option in der Liste automatisch ausgewählt werden soll. Standardeinstellung: False.

MinimumPrefixLength

Dient zum Abrufen und Festlegen des Minimalwerts von Zeichen im Textfeldpuffer, die den gebundenen Webdienst auslösen. Standardeinstellung: 3.

ServiceMethod

Dient zum Abrufen und Festlegen des Namens der Methode, die im gebundenen Webdienst aufgerufen werden soll.

ServicePath

Dient zum Abrufen und Festlegen der URL des gebundenen Webdiensts.

UseContextKey

Boolesche Eigenschaft, die angibt, ob der Wert der ContextKey-Eigenschaft verwendet werden soll, sofern ein Wert festgelegt wurde. Standardeinstellung: False.


Erstellen eines Autocomplete-Webdiensts
Ein Webdienst, der mit dem AutoComplete-Extender arbeitet, ist ein ASP.NET AJAX-Skriptdienst. Er sieht fast genauso aus wie ein normaler ASP.NET-Webdienst, unterscheidet sich aber dadurch, dass seine Klasse mit dem ScriptService-Attribut versehen werden muss. Wenn Sie einen Webdienst einsetzen, dem dieses Attribut fehlt, löst jede Anforderung des zugeordneten ASMX-Endpunkts eine HTTP 500-Fehlermeldung aus. Obwohl für den Endbenutzer keine Schnittstelle mit Ausnahmen angezeigt wird (der Extender wird immer diskret behoben), wird auch kein Flyout mit Vorschlägen angezeigt:
[ScriptService]
public class SuggestionService : WebService 
{
   ...
}
Beachten Sie, dass der Skriptdienst das WebService-Attribut zwar nicht benötigt, es aber trotzdem verwenden kann, um Namespaceinformationen bereitzustellen.
Der Name jeder öffentlichen Methode der Skriptdienstklasse, die mit dem WebMethod-Attribut gekennzeichnet ist, kann der ServiceMethod-Eigenschaft im Extender zugewiesen werden. Eine Methode, die Vorschläge unterbreitet, muss die folgende Signatur besitzen:
[WebMethod]
public string[] GetSuggestions(string prefixText, int count)
Das erste Argument ist der Präfixtext zum Generieren von Vorschlägen. Er passt den aktuellen Inhalt des Textfelds an, und seine Länge ist nicht kleiner als der Wert der MinimumPrefixLength-Eigenschaft. Der count-Parameter gibt an, wie viele Vorschläge unterbreitet werden sollen. Der Wert des count-Parameters wird vom Wert der CompletionSetCount-Eigenschaft abgeleitet.
Wenn Sie vorhaben, den Kontextschlüssel zu nutzen, bei dem es sich um eine String-Eigenschaft handelt, die alle seiten- oder benutzerspezifischen Informationen angibt, die an den gebundenen Webdienst übergeben werden, dann sollten Sie für alle Webdienstmethoden, die Sie verwenden möchten, eine Möglichkeit zum Außerkraftsetzen bereitstellen. Hier ist die Signatur:
public string[]GetSuggestions(
    string prefixText, int count, string contextKey) 
{ 
   ...
}
Der Rückgabewert wird immer in ein Zeichenfolgenarray gepackt. Aufgrund des ScriptService-Attributs erfolgt jede Kommunikation zwischen Server und Client über JSON-Zeichenfolgen (JavaScript Object Notation).
Sie können alle von Webdienstmethoden unterstützten Attribute nutzen, um die Leistung des Diensts zu verbessern. So kann beispielsweise durch das CacheDuration-Attribut des WebMethod-Attributs der Dienst angewiesen werden, die Antwort des Methodenaufrufs für eine festgelegte Zeitspanne zwischenzuspeichern. Sie können auch den Sitzungszustand aktivieren, falls die Logik der Methode dies unbedingt erfordert.
Abbildung 4 zeigt einen Beispieldienst, der Vorschläge zum Kundennamen unterbreitet, der aus der Northwind-Datenbank bezogen wird. Jedes Mal einen Webdienst aufzurufen, sobald ein paar Zeichen eingegeben wurden, hört sich im Hinblick auf die Leistung nach einer sehr schlechten Idee an. Es kommen jedoch einige Faktoren ins Spiel, die die automatische Vervollständigung in Bezug auf die Leistung „erschwinglicher“ machen, als Sie vielleicht denken. Das clientseitige Zwischenspeichern für den AutoComplete-Extender basiert auf dem lokalen Speicher und erfolgt nie über ein ausgeblendetes Feld. Die mit einem bestimmten Präfix verknüpfte Vervollständigungsliste wird in einem internen Array gespeichert, wobei das Präfix als Schlüssel verwendet wird. Danach wird sie immer dann abgerufen, wenn dieses Präfix im Textfeldpuffer isoliert ist. Dies hat zur Folge, dass dann, wenn der Benutzer dasselbe Präfix mehrmals eingibt, keine neue Anforderung über das Netzwerk zum Back-End-Dienst gesendet wird.
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Data;
using System.Data.SqlClient;
using System.Web.Script.Services;

[WebService(Namespace = "MsdnMag.Articles")]
[ScriptService]
public class SuggestionService : System.Web.Services.WebService 
{
    [WebMethod]
    public string[] GetSuggestions(string prefixText, int count) 
    {
        DataView data = GetData();
        data = FilterData(data, prefixText);

        int totalCount = data.Count;
        if (data.Count > count)
            totalCount = count;
        string[] suggestions = new string[totalCount];

        int i = 0;
        foreach (DataRowView row in data)
        {
            suggestions[i++] = (string) row["companyname"];
            if (i >= count)
                break;
        }

        return suggestions;
    }

    private DataView GetData()
    {
        DataView view = 
            (DataView)HttpContext.Current.Cache["Suggestions"];
        if (view == null)
        {
            SqlDataAdapter adapter = new SqlDataAdapter(
                "SELECT * FROM customers", "...");
            DataTable table = new DataTable();
            adapter.Fill(table);
            view = table.DefaultView;

            HttpContext.Current.Cache["Suggestions"] = view;
        }

        return view;
    }

    private DataView FilterData(DataView view, string prefix)
    {
        view.RowFilter = String.Format("companyname LIKE '{0}%'", 
            prefix);
        return view;
    }
}

Das clientseitige Zwischenspeichern ist wesentlich effektiver als das serverseitige Zwischenspeichern, das Ihnen einige Zugriffe auf die Datenbank, aber keine Zugriffe auf den Webserver ersparen würde. In dem Code, der in Abbildung 4 gezeigt wird, lädt der Dienst alle Kunden in der Datenbank und speichert diese Liste im ASP.NET-Zwischenspeicher. Danach ruft er die Liste der Kunden dort ab und filtert die Namen heraus, die dem Präfix entsprechen.
Wie bereits erwähnt, muss der Webdienst, den Sie zum Unterbreiten von Vorschlägen verwenden, ein ASP.NET AJAX-Skriptdienst sein, der über JSON mit dem Client kommuniziert. Sie müssen entscheiden, ob der Dienst auch für SOAP-Clients öffentlich verfügbar sein soll. ASP.NET AJAX-Dienste unterstützen standardmäßig beide Funktionen und können daher sowohl mit JSON als auch mit SOAP arbeiten. Sie können SOAP-Clients deaktivieren, indem Sie der web.config-Datei der Hostanwendung Folgendes hinzufügen:
<system.web>
  <webServices>
    <protocols>
      <clear />
    </protocols>
  </webServices>
</system.web>
Mit ASP.NET 3.5 können Sie den AutoComplete-Extender unter Verwendung eines Dienstendpunkts (SVC) auch an einen WCF-Dienst (Windows® Communication Foundation) binden. Der Extender verwendet für den Aufruf den folgenden JavaScript-Code:
Sys.Net.WebServiceProxy.invoke( 
   this.get_servicePath(), 
   this.get_serviceMethod(), 
   false, 
   params,
   Function.createDelegate(this, this._onMethodComplete),
   Function.createDelegate(this, this._onMethodFailed),
   text);
Bei der WebServiceProxy-Klasse in der Microsoft AJAX-Clientbibliothek kommt es lediglich darauf an, dass der angegebene Endpunkt Anforderungen, deren Anforderungsheader „content-type“ auf „application/json; charset=utf-8“ gesetzt ist, erfolgreich verarbeiten kann. Um einen WCF-Dienst für die Unterbreitung von Vorschlägen zu erstellen, benötigen Sie eine Klasse, die der folgenden ähnelt:
[ServiceContract(Namespace = "MsdnMag.Services")]
[AspNetCompatibilityRequirements(
     RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MySuggestionService
{
    [OperationContract]
    public string[] GetSuggestions(string prefixText, int count)
    {
      ...
    }
}
Zusätzlich benötigen Sie einen SVC-Endpunkt wie diesen:
<%@ ServiceHost Language="C#"  
                Service="MySuggestionService" 
                CodeBehind="~/App_Code/WcfSuggestions.cs" %>
Um den Dienst zu registrieren, können Sie unter Verwendung des Bindungsmodells „webHttpBinding“ dem Abschnitt „serviceModel“ der web.config-Datei einen expliziten Endpunkt hinzufügen. Sie können aber auch das Factory-Attribut in der @ServiceHost-Direktive angeben und es auf die folgende Klasse verweisen lassen:
System.ServiceModel.Activation.WebScriptServiceHostFactory 

Gestalten des AutoComplete-Extenders
Der AutoComplete-Extender unterstützt Eigenschaften, durch die Sie die CSS-Klassen festlegen können, die zum Gestalten von Teilen des Steuerelements verwendet werden sollen. Insbesondere können Sie die gesamte Flyoutregion einschließlich der Rahmen und der Hintergrundfarbe gestalten. Sie können auch für alle Elemente und das ausgewählte Element unterschiedliche Einstellungen verwenden. Für das alternative Element ist kein vordefinierter Stil vorgegeben. Standardmäßig ist das erste Element in der Dropdown-Vervollständigungsliste nicht ausgewählt. Sie können aber die FirstRowSelected-Eigenschaft verwenden, um die automatische Auswahl des ersten Elements zu erzwingen.
Die Vervollständigungsliste wird angezeigt, sobald die festgelegte Anzahl an Millisekunden verstrichen ist. Dies wird über die CompletionInterval-Eigenschaft gesteuert und ist standardmäßig auf 1 Sekunde eingestellt. Sobald diese Zeitspanne verstrichen ist, ruft der Extender den Dienst auf, um die Liste der Vorschläge abzurufen.
Sie können die Animations-Eigenschaft verwenden, um den Vorgang, der die Liste anzeigt, ein wenig zu beleben. Sie können Animationen für zwei Ereignisse definieren: OnShow und OnHide. Animationen werden durch den Animation-Extender und das zugehörige Framework durchgeführt (siehe das Beispiel in Abbildung 5).
<Animations>
    <OnShow>
        <Sequence>
            <OpacityAction Opacity="0" />
            <HideAction Visible="true" />
            <Parallel Duration=".4">
                <FadeIn />
            </Parallel>
        </Sequence>
    </OnShow>
    <OnHide>
        <Parallel Duration=".4">
            <FadeOut />
        </Parallel>
    </OnHide>
</Animations>

Das Animationsskript blendet die Vervollständigungsliste langsam ein und aus. Der OpacityAction-Knoten legt die Transparenz fest, und HideAction zeigt die Vervollständigungsliste und andere visuelle Effekte an. Zum Schluss führen die Aktionen „FadeIn“ und „FadeOut“ den Ein- und Ausblendevorgang durch. Abbildung 6 zeigt eine animierte Vervollständigungsliste.
Abbildung 6 Der AutoComplete-Extender in Aktion (Klicken Sie zum Vergrößern auf das Bild)

Inkrementelle Suche in Listen
Haben Sie schon einmal versucht, in einer langen Liste einen bestimmten Eintrag zu finden, zum Beispiel den Namen eines bestimmten Landes in einer Liste aller Länder dieser Welt? Wenn Sie schnell genug sind, um mehrere Buchstaben hintereinander einzugeben, erhalten Sie evtl. das gewünschte Land, aber wenn Sie nur wenige Millisekunden zögern, wird der nächste von Ihnen eingegebene Buchstabe zum ersten Buchstaben des Suchbegriffs. Die ListSearch-Extender behebt diese Einschränkung. Beachten Sie, dass der Extender nur auf ASP.NET-Web- und HTML-Steuerelemente angewendet wird und nicht für reguläre HTML-Elemente wie z. B. SELECT oder OPTION verwendet werden kann.
Deshalb liegt der Hauptvorteil des ListSearch-Extenders darin, dass er Ihnen ermöglicht, nach Einträgen in einem Listensteuerelement zu suchen, indem Sie etwas eingeben, und dabei verhindert, dass Ihre Suchzeichenfolge nach wenigen Sekunden verloren geht. Stattdessen bleibt Ihre Suchzeichenfolge vorhanden, bis Sie entweder die ESC-Taste drücken oder ein Postback zum Server senden (dies gilt sowohl für vollständige als auch für partielle Postbacks). Jedes weitere Zeichen, das Sie eingeben, wird an die Suchzeichenfolge angehängt und dazu verwendet, die Liste der Einträge weiter einzuengen. Ein ListSearch-Extender wird folgendermaßen verwendet:
<act:ListSearchExtender ID="ListSearchExtender1" runat="server"
     TargetControlID="DropDownList1" 
     PromptCssClass="Prompt" />
Der Extender besitzt drei Haupteigenschaften: PromptText, PromptCssClass und PromptPosition. Jede dieser drei Eigenschaften bezieht sich auf den Eingabeaufforderungstext, also die Zeichenfolge, die in der Umgebung des Zielsteuerelements angezeigt wird, um visuelles Feedback zu dem Text zu liefern, der gesucht wird. Der Standardwert für PromptText ist „Type to search“. Dieser Text wird in der Regel direkt oberhalb des Ziellistensteuerelements angezeigt, sobald das Steuerelement den Fokus erhält. Mit der PromptCssClass-Eigenschaft kann der Name der CSS-Klasse, die auf die Eingabeaufforderungsmeldung angewendet werden soll, sowohl abgerufen als auch eingestellt werden. Der Eingabeaufforderungstext wird durch den Suchtext ersetzt, der vom Benutzer eingegeben wird. Die ESC-Taste und die Entf-Taste spielen hierbei eine wichtige Rolle. Die ESC-Taste löscht die aktuelle Suche und setzt die Liste auf den standardmäßig ausgewählten Eintrag zurück. Die Entf-Taste entfernt das zuletzt eingegebene Zeichen aus dem Suchtext und aktualisiert die Auswahl in der Liste. Darüber hinaus funktioniert die Animations-Eigenschaft so, wie ich dies eben im Zusammenhang mit dem AutoComplete-Extender beschrieben habe.

Fliegende Kontextmenüs
Es ist gut, den Benutzern Optionen für genau das anzubieten, was sie gerade tun. Vor einigen Jahren stellten die QuickInfos eine enorme Verbesserung dar, weil sie während der Arbeit zusätzliche Informationen zur Funktion eines bestimmten Steuerelements bereitstellten. Das Auftauchen von Dynamic HTML hat HTML-QuickInfos möglich gemacht.
Heute macht es der HoverMenu-Extender möglich, neben jedem beliebigen ASP.NET-Serversteuerelement einen Flyoutbereich anzuzeigen. Einer der häufigsten Verwendungszwecke dieses Extenders besteht darin, Kontextmenüs anzuzeigen, die nicht durch Klicken mit der rechten Maustaste, sondern durch Mausbewegungsereignisse aktiviert werden.
Der HoverMenu-Extender wird aktiviert, sobald der Benutzer den Mauszeiger über das Steuerelement bewegt. Dadurch wird neben dem Steuerelement das festgelegte Popupfenster angezeigt, wobei die genaue Position programmgesteuert bestimmt werden kann. Zusätzlich kann auf das Steuerelement ein CSS-Stil angewendet werden, um es als aktivierbar zu markieren.
Abbildung 7 zeigt ein Optionsfeldmenü, das Vorschläge zu dem Text bietet, der in das Textfeld eingefügt werden kann. Hier ist die Deklaration für den zugehörigen Extender:
Abbildung 7 Der HoverMenu-Extender in Aktion (Klicken Sie zum Vergrößern auf das Bild)
<act:HoverMenuExtender ID="HoverMenu1" runat="server"
    TargetControlID="TextBox1" 
    PopupControlID="Panel1" />
Das Panel1-Steuerelement enthält die Liste der Optionsfelder. Um sicherzustellen, dass jede Auswahl ins Textfeldzielsteuerelement gelangen kann, definieren Sie in der Optionsfeldliste ein SelectedIndexChanged-Ereignis und umschließen alles mit einem UpdatePanel-Steuerelement. (Weitere Details finden Sie im Quellcode im Download zu diesem Artikel.)
Der HoverMenu-Extender besitzt neben PopupControlID und TargetControlID noch weitere Eigenschaften. Die HoverCssClass-Eigenschaft gibt die CSS-Klasse an, die auf das Zielsteuerelement angewendet werden soll, wenn das Hovermenü angezeigt wird. PopupPosition gibt die Position des Popupfensters in Bezug auf das Zielsteuerelement an und kann die Werte „center“, „top“, „right“, „left“ und „bottom“ haben. Die Eigenschaften „OffsetX“ und „OffsetY“ legen horizontale und vertikale Abweichungen von der vorgesehenen Position fest. PopDelay legt die Verzögerung zwischen dem DOM-Ereignis und dem Anzeigen des Popupfensters fest. Die Standardeinstellung beträgt 100 Millisekunden.
Das DOM-Ereignis, das den HoverMenu-Extender auslöst, ist das mouseover-Ereignis. Der HoverMenu-Extender unterscheidet sich nicht sehr vom Popup-Extender. Die Hauptunterschiede liegen im Aktivierungsmechanismus und im Auslöser. Der Popup-Extender wird aktiviert, wenn der Benutzer den Fokus auf ein bestimmtes Zielsteuerelement setzt. Um den HoverMenu-Extender zu aktivieren, muss der Benutzer lediglich den Mauszeiger über das Steuerelement bewegen.

Zusammenfassung
Je vielfältiger und interaktiver Ihre Website werden soll, desto mehr JavaScript werden Sie benötigen. Obwohl sich manuell codiertes JavaScript für einfache Features eignet, reicht es oft nicht aus, um höhere Stufen der Interaktivität zu erzielen. Für diese komplizierteren Features benötigen Sie unbedingt Extender und die Microsoft AJAX-Clientbibliothek (siehe die Ausgabe von „Cutting Edge“ vom Dezember 2007 unter msdn.microsoft.com/msdnmag/issues/07/12/cuttingedge).
Mit dieser Ausgabe von „Cutting Edge“ endet meine Besprechung der Extender im Microsoft AJAX Control Toolkit. Das Toolkit enthält allerdings noch viel mehr als das, was ich hier behandelt habe. Wenn Sie wissen möchten, was ich damit meine, besuchen Sie asp.net/ajax.

Senden Sie Fragen und Kommentare für Dino Esposito (in englischer Sprache) an cutting@microsoft.com.

Dino Esposito ist Mentor bei Solid Quality Learning und Autor von „Introducing Microsoft ASP.NET AJAX“ (Microsoft Press, 2007). Er lebt in Italien und ist ein weltweit gefragter Referent bei Branchenveranstaltungen. Sie erreichen ihn unter der Adresse cutting@microsoft.com. Seinen Blog finden Sie unter weblogs.asp.net/despos.

Page view tracker