Anfügen von C#-Code an DOM-Ereignisse mit Blazor-Ereignishandlern

Abgeschlossen

Die meisten HTML-Elemente machen Ereignisse verfügbar, die ausgelöst werden, wenn etwas Signifikantes passiert, z. B. wenn das Laden einer Seite abgeschlossen wurde, ein*e Benutzer*in auf eine Schaltfläche geklickt hat oder sich der Inhalt eines HTML-Elements geändert hat. Eine App kann ein Ereignis auf verschiedene Arten behandeln:

  • Die App kann das Ereignis ignorieren.
  • Die App kann einen in JavaScript geschriebenen Ereignishandler ausführen, um das Ereignis zu verarbeiten.
  • Die App kann einen in C# geschriebenen Blazor-Ereignishandler ausführen, um das Ereignis zu verarbeiten.

In dieser Lerneinheit sehen Sie sich detailliert die dritte Option an, also wie Sie einen Blazor-Ereignishandler in C# zum Verarbeiten eines Ereignisses erstellen.

Behandeln eines Ereignisses mit Blazor und C#

Jedes Element im HTML-Markup einer Blazor-App unterstützt viele Ereignisse. Die meisten dieser Ereignisse entsprechen den DOM-Ereignissen, die in regulären Webanwendungen verfügbar sind, aber Sie können auch benutzerdefinierte Ereignisse erstellen, die durch Schreiben von Code ausgelöst werden. Um ein Ereignis mit Blazor zu erfassen, schreiben Sie eine C#-Methode, die das Ereignis verarbeitet, und binden das Ereignis dann mit einer Blazor-Anweisung an die Methode. Bei einem DOM-Ereignis hat die Blazor-Anweisung denselben Namen wie das entsprechende HTML-Ereignis, z. B. @onkeydown oder @onfocus. Beispielsweise enthält die mithilfe der Blazor Server-App generierte Beispiel-App den folgenden Code auf der Seite Counter.razor. Auf dieser Seite wird eine Schaltfläche angezeigt. Wenn der Benutzer die Schaltfläche auswählt, löst das @onclick-Ereignis die IncrementCount-Methode aus, die einen Zähler erhöht, der angibt, wie oft auf die Schaltfläche geklickt wurde. Der Wert der Zählervariablen wird vom <p>-Element auf der Seite angezeigt:

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

Viele Ereignishandlermethoden verwenden einen Parameter, der zusätzliche Kontextinformationen bereitstellt. Dieser Parameter wird auch als EventArgs-Parameter bezeichnet. Beispielsweise übergibt das @onclick-Ereignis Informationen darüber, auf welche Schaltfläche der Benutzer geklickt hat, oder ob er gleichzeitig mit dem Klicken auf die Schaltfläche eine Taste wie STRG oder ALT gedrückt hat, in einem MouseEventArgs-Parameter. Sie müssen diesen Parameter nicht angeben, wenn Sie die Methode aufrufen. Er wird automatisch von der Blazor-Runtime hinzugefügt. Sie können diesen Parameter im Ereignishandler abfragen. Der folgende Code erhöht den im vorigen Beispiel gezeigten Zähler um fünf, wenn der Benutzer gleichzeitig mit dem Klicken auf die Schaltfläche die STRG-TASTE drückt:

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>


@code {
    private int currentCount = 0;

    private void IncrementCount(MouseEventArgs e)
    {
        if (e.CtrlKey) // Ctrl key pressed as well
        {
            currentCount += 5;
        }
        else
        {
            currentCount++;
        }
    }
}

Andere Ereignisse stellen unterschiedliche EventArgs-Parameter zur Verfügung. Beispielsweise übergibt das @onkeypress-Ereignis einen KeyboardEventArgs-Parameter, der angibt, welche Taste der Benutzer gedrückt hat. Wenn Sie diese Informationen nicht benötigen, können Sie bei DOM-Ereignissen den EventArgs-Parameter in der Ereignisbehandlungsmethode auslassen.

Grundlegendes zur Ereignisbehandlung in JavaScript im Vergleich zur Ereignisbehandlung mit Blazor

Eine herkömmliche Webanwendung verwendet JavaScript, um Ereignisse zu erfassen und zu verarbeiten. Sie erstellen eine Funktion als Teil eines <script>-Elements aus HTML und sorgen dann dafür, dass diese Funktion aufgerufen wird, wenn das Ereignis auftritt. Im Vergleich zum vorherigen Blazor-Beispiel zeigt der folgende Code ein Fragment von einer HTML-Seite, das einen Wert immer dann erhöht und das Ergebnis anzeigt, wenn ein*e Benutzer*in die Schaltfläche Klicken auswählt. Der Code verwendet für den Zugriff auf das DOM die jQuery-Bibliothek.

<p id="currentCount">Current count: 0</p>

<button class="btn btn-primary" onclick="incrementCount()">Click me</button>

<!-- Omitted for brevity -->

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
    var currentCount = 0;

    function incrementCount() {
        currentCount++;
        $('#currentCount').html('Current count:' + currentCount);
    }
</script>

Neben den syntaktischen Unterschieden in den beiden Versionen des Ereignishandlers sollten Sie die folgenden funktionalen Unterschiede beachten:

  • JavaScript stellt dem Namen des Ereignisses kein @-Zeichen voran. Es handelt sich nicht um eine Blazor-Anweisung.
  • Im Blazor-Code geben Sie den Namen der Ereignisbehandlungsmethode an, wenn Sie sie an ein Ereignis anfügen. In JavaScript schreiben Sie eine Anweisung, die die Ereignisbehandlungsmethode aufruft. Sie geben runde Klammern und alle erforderlichen Parameter an.
  • Vor allem wird der JavaScript-Ereignishandler im Browser auf dem Client ausgeführt. Wenn Sie eine Blazor Server-App erstellen, wird der Blazor-Ereignishandler auf dem Server ausgeführt und aktualisiert den Browser nur dann mit allen Änderungen, die an der Benutzeroberfläche vorgenommen werden, wenn der Ereignishandler abgeschlossen wird. Darüber hinaus ermöglicht der Blazor-Mechanismus einem Ereignishandler den Zugriff auf statische Daten, die von Sitzungen gemeinsam genutzt werden, was beim JavaScript-Modell nicht der Fall ist. Die Behandlung einiger häufig auftretender Ereignisse wie @onmousemove kann jedoch dazu führen, dass die Benutzeroberfläche langsam wird, da sie einen Netzwerkroundtrip zum Server erfordern. Möglicherweise ziehen Sie es vor, solche Ereignisse im Browser mithilfe von JavaScript zu behandeln.

Wichtig

Sie können das DOM mithilfe von JavaScript-Code aus einem Ereignishandler sowie mithilfe von C#-Blazor-Code manipulieren. Blazor verwaltet jedoch eine eigene Kopie des DOM, die bei Bedarf zum Aktualisieren der Benutzeroberfläche verwendet wird. Wenn Sie JavaScript- und Blazor-Code verwenden, um dieselben Elemente im DOM zu ändern, besteht das Risiko, dass das DOM beschädigt wird und der Datenschutz und die Sicherheit der Daten in Ihrer Web-App möglicherweise gefährdet werden.

Asynchrones Behandeln von Ereignissen

Standardmäßig sind Blazor-Ereignishandler synchron. Wenn ein Ereignishandler einen potenziell zeitintensiven Vorgang ausführt, z. B. das Aufrufen eines Webdiensts, wird der Thread, in dem der Ereignishandler ausgeführt wird, blockiert, bis der Vorgang abgeschlossen ist. Dies kann zu einer schlechten Reaktion auf der Benutzeroberfläche führen. Um dem entgegenzuwirken, können Sie eine Ereignishandlermethode als asynchron festlegen. Verwenden Sie das C#-Schlüsselwort async. Die Methode muss ein Task-Objekt zurückgeben. Sie können dann den await-Operator innerhalb der Ereignishandlermethode verwenden, um alle zeitintensiven Aufgaben in einem separaten Thread zu initiieren und den aktuellen Thread für andere Arbeiten freizugeben. Wenn eine zeitintensive Aufgabe abgeschlossen wird, wird der Ereignishandler fortgesetzt. Der folgende Beispielereignishandler führt eine zeitaufwändige Methode asynchron aus:

<button @onclick="DoWork">Run time-consuming operation</button>

@code {
    private async Task DoWork()
    {
        // Call a method that takes a long time to run and free the current thread
        var data = await timeConsumingOperation();

        // Omitted for brevity
    }
}

Hinweis

Ausführliche Informationen zum Erstellen asynchroner Methoden in C# finden Sie unter Asynchrone Programmierszenarios.

Verwenden eines Ereignisses zum Festlegen des Fokus auf ein DOM-Element

Auf einer HTML-Seite kann der Benutzer mit der Tab-Taste zwischen Elementen wechseln, wobei sich der Fokus natürlich in der Reihenfolge bewegt, in der die HTML-Elemente auf der Seite angezeigt werden. In einigen Fällen müssen Sie diese Sequenz möglicherweise überschreiben und die Benutzer*innen zwingen, ein bestimmtes Element aufzusuchen.

Die einfachste Möglichkeit, diese Aufgabe auszuführen, ist die Verwendung der FocusAsync-Methode. Hierbei handelt es sich um eine Instanzmethode eines ElementReference-Objekts. ElementReference sollte auf das Element verweisen, auf das Sie den Fokus festlegen möchten. Sie kennzeichnen einen Elementverweis mit dem Attribut @ref und erstellen in Ihrem Code ein gleichnamiges C#-Objekt.

Im folgenden Beispiel legt der Ereignishandler @onclick für das <button>-Element den Fokus auf das <input>-Element fest. Der @onfocus-Ereignishandler des <input>-Elements zeigt die Meldung „Received focus“ (Fokus empfangen) an, wenn das Element den Fokus erhält. Der Verweis auf das <input>-Element erfolgt über die Variable InputField im Code:

<button class="btn btn-primary" @onclick="ChangeFocus">Click me to change focus</button>
<input @ref=InputField @onfocus="HandleFocus" value="@data"/>

@code {
    private ElementReference InputField;
    private string data;

    private async Task ChangeFocus()
    {
        await InputField.FocusAsync();
    }

    private async Task HandleFocus()
    {
        data = "Received focus";
    }

Die folgende Abbildung zeigt das Ergebnis, wenn der Benutzer die Schaltfläche auswählt:

Screenshot der Webseite, nachdem der/die Benutzende auf die Schaltfläche geklickt hat, um den Fokus auf das Eingabeelement festzulegen.

Hinweis

Eine App sollte den Fokus nur aus einem speziellen Grund auf ein bestimmtes Steuerelement lenken, z. B. um den Benutzer aufzufordern, Eingaben nach einem Fehler zu ändern. Verwenden Sie das Festlegen des Fokus nicht, um die Benutzer*innen zu zwingen, in einer festen Reihenfolge durch die Elemente auf einer Seite zu navigieren. Das kann für Benutzer*innen sehr frustrierend sein, die Elemente erneut besuchen möchten, um die Eingabe zu ändern.

Schreiben von Inlineereignishandlern

C# unterstützt Lambdaausdrücke. Mit einem Lambdaausdruck können Sie eine anonyme Funktion erstellen. Ein Lambdaausdruck ist nützlich, wenn Sie über einen einfachen Ereignishandler verfügen, den Sie an keiner anderen Stelle auf einer Seite oder in einer Komponente wiederverwenden müssen. In dem zu Beginn dieser Lerneinheit gezeigten anfänglichen Beispiel mit der Klickanzahl, können Sie die IncrementCount-Methode entfernen und stattdessen den Methodenaufruf durch einen Lambdaausdruck ersetzen, der dieselbe Aufgabe ausführt:

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="() => currentCount++">Click me</button>

@code {
    private int currentCount = 0;
}

Hinweis

Ausführliche Informationen zur Funktionsweise von Lambdaausdrücken finden Sie unter Lambdaausdrücke und anonyme Funktionen.

Dieser Ansatz ist auch nützlich, wenn Sie für eine Ereignisbehandlungsmethode andere Argumente angeben möchten. Im folgenden Beispiel akzeptiert die HandleClick-Methode einen MouseEventArgs-Parameter auf dieselbe Weise wie ein gewöhnlicher Ereignishandler für Klicks, akzeptiert aber auch einen Zeichenfolgenparameter. Die Methode verarbeitet das Click-Ereignis wie zuvor, zeigt aber außerdem die Meldung an, dass der Benutzer die STRG-TASTE gedrückt hat. Der Lambdaausdruck ruft die HandleCLick-Methode auf, wobei der MouseEventArgs-Parameter (mouseEvent) und eine Zeichenfolge übergeben werden.

@page "/counter"
@inject IJSRuntime JS

<h1>Counter</h1>

<p id="currentCount">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick='mouseEvent => HandleClick(mouseEvent, "Hello")'>Click me</button>

@code {
    private int currentCount = 0;

    private async Task HandleClick(MouseEventArgs e, string msg)
    {
        if (e.CtrlKey) // Ctrl key pressed as well
        {
            await JS.InvokeVoidAsync("alert", msg);
            currentCount += 5;
        }
        else
        {
            currentCount++;
        }
    }
}

Hinweis

In diesem Beispiel wird die JavaScript-Funktion alert verwendet, um die Meldung anzuzeigen, da es in Blazor keine entsprechende Funktion gibt. Sie verwenden JavaScript-Interop, um JavaScript aus Blazor-Code heraus aufzurufen. Die Details dieser Methode sind Gegenstand eines gesonderten Moduls.

Außerkraftsetzen von DOM-Standardaktionen für Ereignisse

Mehrere DOM-Ereignisse verfügen über Standardaktionen, die ausgeführt werden, wenn das Ereignis auftritt, unabhängig davon, ob ein Ereignishandler für dieses Ereignis verfügbar ist. Beispielsweise zeigt das @onkeypress-Ereignis für ein <input>-Element immer das Zeichen an, das der Taste entspricht, die der oder die Benutzer*in gedrückt hat, und verarbeitet das Drücken der Taste. Im nächsten Beispiel wird das @onkeypress-Ereignis verwendet, um die Eingabe des Benutzers in Großbuchstaben umzuwandeln. Wenn der Benutzer außerdem ein @-Zeichen eingibt, zeigt der Ereignishandler eine Warnung an:

<input value=@data @onkeypress="ProcessKeyPress"/>

@code {
    private string data;

    private async Task ProcessKeyPress(KeyboardEventArgs e)
    {
        if (e.Key == "@")
        {
            await JS.InvokeVoidAsync("alert", "You pressed @");
        }
        else
        {
            data += e.Key.ToUpper();
        }
    }
}

Wenn Sie diesen Code ausführen und die @-Taste drücken, wird die Warnung angezeigt, aber das @-Zeichen wird auch der Eingabe hinzugefügt. Das Hinzufügen des @-Zeichens ist die Standardaktion des Ereignisses.

Screenshot der Benutzereingabe mit dem @-Zeichen.

Wenn Sie die Anzeige dieses Zeichens im Eingabefeld unterdrücken möchten, können Sie die Standardaktion wie folgt mit dem preventDefault-Attribut des Ereignisses außer Kraft setzen:

<input value=@data @onkeypress="ProcessKeyPress" @onkeypress:preventDefault />

Das Ereignis wird weiterhin ausgelöst, aber nur die vom Ereignishandler definierten Aktionen werden ausgeführt.

Einige Ereignisse in einem untergeordneten Element im DOM können Ereignisse in ihren übergeordneten Elementen auslösen. Im folgenden Beispiel enthält das <div>-Element einen @onclick-Ereignishandler. Das <button>-Element in <div> verfügt über einen eigenen @onclick-Ereignishandler. Darüber hinaus enthält <div> ein <input>-Eingabe:

<div @onclick="HandleDivClick">
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
    <input value=@data @onkeypress="ProcessKeyPress" @onkeypress:preventDefault />
</div>

@code {
    private async Task HandleDivClick()
    {
        await JS.InvokeVoidAsync("alert", "Div click");
    }

    private async Task ProcessKeyPress(KeyboardEventArgs e)
    {
        // Omitted for brevity
    }

    private int currentCount = 0;

    private void IncrementCount(MouseEventArgs e)
    {
        // Omitted for brevity
    }
}

Wenn der oder die Benutzer*in beim Ausführen der App auf ein Element (oder einen leeren Bereich) in dem Bereich klickt, der von dem <div>-Element eingenommen wird, wird die HandleDivClick-Methode ausgeführt und zeigt eine Meldung an. Wenn der Benutzer die Schaltfläche Click me auswählt, wird die IncrementCount-Methode ausgeführt, gefolgt von HandleDivClick. Das @onclick-Ereignis wird entlang der DOM-Struktur nach oben weitergegeben. Wenn <div> Teil eines anderen Elements war, das ebenfalls das @onclick-Ereignis behandelt hat, würde dieser Ereignishandler ebenfalls ausgeführt werden usw., bis zum Stamm der DOM-Struktur. Sie können diese Aufwärtsweitergabe von Ereignissen mit dem stopPropagation-Attribut eines Ereignisses wie hier gezeigt abbrechen:

<div @onclick="HandleDivClick">
    <button class="btn btn-primary" @onclick="IncrementCount" @onclick:stopPropagation>Click me</button>
    <!-- Omitted for brevity -->
</div>

Verwenden von EventCallback zum komponentenübergreifenden Verarbeiten von Ereignissen

Eine Blazor-Seite kann eine oder mehrere Blazor-Komponenten enthalten, und Komponenten können in einer übergeordnet/untergeordnet-Beziehung geschachtelt sein. Ein Ereignis in einer untergeordneten Komponente kann eine Ereignishandlermethode in einer übergeordneten Komponente mithilfe von EventCallback auslösen. Ein Rückruf verweist auf eine Methode in der übergeordneten Komponente. Die untergeordnete Komponente kann die Methode durch Aufrufen des Rückrufs ausführen. Dieser Mechanismus ähnelt der Verwendung von delegate, um auf eine Methode in einer C#-Anwendung zu verweisen.

Ein Rückruf kann einen einzelnen Parameter annehmen. EventCallback ist ein generischer Typ. Der type-Parameter gibt den Typ des Arguments an, das an den Rückruf übergeben wird.

Betrachten Sie beispielsweise das folgende Szenario. Sie möchten eine Komponente mit dem Namen TextDisplay erstellen, die es dem Benutzer ermöglicht, eine Eingabezeichenfolge einzugeben, und die diese Zeichenfolge in irgendeiner Weise transformiert. Sie könnten sie in Großbuchstaben, Kleinbuchstaben, gemischte Groß-/Kleinschreibung umwandeln, Zeichen herausfiltern oder einen anderen Typ von Transformation ausführen. Wenn Sie jedoch den Code für die TextDisplay-Komponente schreiben, wissen Sie nicht, wie der Transformationsprozess aussehen wird, und sollten diesen Vorgang stattdessen auf eine andere Komponente verschieben. Der folgende Code zeigt die TextDisplay-Komponente. Die Eingabezeichenfolge wird in Form eines <input>-Elements bereitgestellt, das es dem oder der Benutzer*in ermöglicht, einen Textwert einzugeben.

@* TextDisplay component *@
@using WebApplication.Data;

<p>Enter text:</p>
<input @onkeypress="HandleKeyPress" value="@data" />

@code {
    [Parameter]
    public EventCallback<KeyTransformation> OnKeyPressCallback { get; set; }

    private string data;

    private async Task HandleKeyPress(KeyboardEventArgs e)
    {
        KeyTransformation t = new KeyTransformation() { Key = e.Key };
        await OnKeyPressCallback.InvokeAsync(t);
        data += t.TransformedKey;
    }
}

Die TextDisplay-Komponente verwendet ein EventCallback-Objekt namens OnKeyPressCallback. Der Code in der HandleKeypress-Methode ruft den Rückruf auf. Der @onkeypress-Ereignishandler wird jedes Mal ausgeführt, wenn eine Taste gedrückt wird, und ruft die HandleKeypress-Methode auf. Die HandleKeypress-Methode erstellt mithilfe der Taste, die der Benutzer gedrückt hat, ein KeyTransformation-Objekt und übergibt dieses Objekt als Parameter an den Rückruf. Der KeyTransformation-Typ ist eine einfache Klasse mit zwei Feldern:

namespace WebApplication.Data
{
    public class KeyTransformation
    {
        public string Key { get; set; }
        public string TransformedKey { get; set; }
    }
}

Das key-Feld enthält den vom Benutzer eingegebenen Wert, und das TransformedKey-Feld nimmt den transformierten Wert des Schlüssels nach dessen Verarbeitung auf.

In diesem Beispiel ist das EventCallback-Objekt ein component-Parameter, und sein Wert wird angegeben, wenn die Komponente erstellt wird. Diese Aktion wird von einer anderen Komponente namens TextTransformer ausgeführt:

@page "/texttransformer"
@using WebApplication.Data;

<h1>Text Transformer - Parent</h1>

<TextDisplay OnKeypressCallback="@TransformText" />

@code {
    private void TransformText(KeyTransformation k)
    {
        k.TransformedKey = k.Key.ToUpper();
    }
}

Die TextTransformer-Komponente ist eine Blazor-Seite, die eine Instanz der TextDisplay-Komponente erstellt. Sie füllt den OnKeypressCallback-Parameter mit einem Verweis auf die TransformText-Methode im Codeabschnitt der Seite auf. Die TransformText-Methode verwendet das als Argument bereitgestellte KeyTransformation-Objekt und füllt die TransformedKey-Eigenschaft mit dem Wert aus, der in der Key-Eigenschaft gefunden wurde, und konvertiert ihn dabei in Großbuchstaben. Das folgende Diagramm veranschaulicht den Steuerungsflow, wenn ein*e Benutzer*in einen Wert in das <input>-Feld in der TextDisplay-Komponente eingibt, die von der TextTransformer-Seite angezeigt wird:

Diagramm des Steuerelementflusses mit einem EventCallback in einer untergeordneten Komponente.

Der Vorteil dieses Ansatzes besteht darin, dass Sie die TextDisplay-Komponente mit jeder Seite verwenden können, die einen Rückruf für den OnKeypressCallback-Parameter bereitstellt. Es besteht eine vollständige Trennung zwischen Anzeige und Verarbeitung. Sie können die TransformText-Methode für jeden anderen Rückruf ändern, der der Signatur des EventCallback-Parameters in der TextDisplay-Komponente entspricht.

Sie können einen Rückruf direkt an einen Ereignishandler binden, ohne eine Zwischenmethode zu verwenden, wenn der Rückruf mit dem entsprechenden EventArgs-Parameter eingegeben wird. Beispielsweise könnte eine untergeordnete Komponente auf einen Rückruf verweisen, der Mausereignisse wie @onclick wie folgt behandeln kann:

<button @onclick="OnClickCallback">
    Click me!
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}

In diesem Fall verwendet der EventCallback einen Parameter vom Typ MouseEventArgs, sodass er als Handler für das Ereignis @onclick angegeben werden kann.

Überprüfen Sie Ihr Wissen

1.

Welches Feature sollten Sie verwenden, um Ereignisse zwischen Blazor-Komponenten zu übergeben?

2.

Welche Methode wird verwendet, um die Standardaktion in einem HTML-DOM-Element zu überschreiben?