C#-code koppelen aan DOM-gebeurtenissen met Blazor-gebeurtenis-handlers
- 5 minuten
In de meeste HTML-elementen worden gebeurtenissen weergegeven die worden geactiveerd wanneer er iets belangrijks gebeurt. Wanneer een pagina is geladen, klikt de gebruiker op een knop of wordt de inhoud van een HTML-element gewijzigd. Een app kan een gebeurtenis op verschillende manieren afhandelen:
- De app kan de gebeurtenis negeren.
- De app kan een gebeurtenis-handler uitvoeren die is geschreven in JavaScript om de gebeurtenis te verwerken.
- De app kan een Blazor-gebeurtenishandler uitvoeren die is geschreven in C# om de gebeurtenis te verwerken.
In deze les krijgt u een gedetailleerd overzicht van de derde optie; hoe u een Blazor-gebeurtenis-handler maakt in C# om een gebeurtenis te verwerken.
Een gebeurtenis afhandelen met Blazor en C#
Elk element in de HTML-opmaak van een Blazor-app ondersteunt veel gebeurtenissen. De meeste van deze gebeurtenissen komen overeen met de DOM-gebeurtenissen die beschikbaar zijn in reguliere webtoepassingen, maar u kunt ook door de gebruiker gedefinieerde gebeurtenissen maken die worden geactiveerd door code te schrijven. Als u een gebeurtenis met Blazor wilt vastleggen, schrijft u een C#-methode die de gebeurtenis verwerkt en verbindt u de gebeurtenis vervolgens met een Blazor-instructie aan de methode. Voor een DOM-gebeurtenis deelt de Blazor-instructie dezelfde naam als de equivalente HTML-gebeurtenis, zoals @onkeydown of @onfocus. De voorbeeld-app die is gegenereerd met behulp van de Blazor Server-app bevat bijvoorbeeld de volgende code op de Counter.razor-pagina . Op deze pagina wordt een knop weergegeven. Wanneer de gebruiker de knop selecteert, activeert de @onclick gebeurtenis de IncrementCount methode waarmee een teller wordt verhoogd, waarmee wordt aangegeven hoe vaak op de knop is geklikt. Het element <p> op de pagina geeft de waarde van de tellervariabele weer:
@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++;
}
}
Veel methoden voor gebeurtenis-handler gebruiken een parameter die extra contextuele informatie biedt. Deze parameter wordt een EventArgs parameter genoemd. Bijvoorbeeld, de @onclick gebeurtenis geeft informatie door over welke knop de gebruiker heeft geklikt, of of de gebruiker tegelijkertijd een toets zoals Ctrl of Alt heeft ingedrukt bij het klikken op de knop, in een MouseEventArgs parameter. U hoeft deze parameter niet op te geven wanneer u de methode aanroept; De Blazor-runtime voegt deze automatisch toe. U kunt een query uitvoeren op deze parameter in de gebeurtenis-handler. Met de volgende code wordt de teller uit het vorige voorbeeld met vijf verhoogd als de gebruiker tegelijkertijd op de Ctrl-toets drukt en op de knop klikt.
@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 gebeurtenissen bieden verschillende EventArgs parameters. De gebeurtenis geeft bijvoorbeeld @onkeypress een KeyboardEventArgs parameter door die aangeeft welke sleutel de gebruiker heeft ingedrukt. Als u deze informatie niet nodig hebt, kunt u voor een van de DOM-gebeurtenissen de EventArgs parameter weglaten uit de methode voor gebeurtenisafhandeling.
Informatie over gebeurtenisafhandeling in JavaScript versus gebeurtenisafhandeling met Blazor
Een traditionele webtoepassing maakt gebruik van JavaScript om gebeurtenissen vast te leggen en te verwerken. U maakt een functie als onderdeel van een HTML-scriptelement <> en rangschikt vervolgens om die functie aan te roepen wanneer de gebeurtenis plaatsvindt. Ter vergelijking met het voorgaande Blazor-voorbeeld toont de volgende code een fragment van een HTML-pagina waarmee een waarde wordt verhoogd en het resultaat wordt weergegeven wanneer de gebruiker de knop Klik op mij selecteert. De code maakt gebruik van de jQuery-bibliotheek voor toegang tot de DOM.
<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>
Naast de syntactische verschillen in de twee versies van de gebeurtenis-handler moet u rekening houden met de volgende functionele verschillen:
- JavaScript bevat geen voorvoegsel voor de naam van de gebeurtenis met een
@-teken; Het is geen Blazor-richtlijn. - In de Blazor-code geeft u de naam op van de methode voor gebeurtenisafhandeling wanneer u deze aan een gebeurtenis koppelt. In JavaScript schrijft u een instructie die de methode voor gebeurtenisafhandeling aanroept; u ronde haakjes en eventuele vereiste parameters opgeeft.
- Het belangrijkste is dat de JavaScript-gebeurtenishandler wordt uitgevoerd in de browser, op de client. Als u een Blazor Server-app bouwt, wordt de Blazor-gebeurtenis-handler uitgevoerd op de server en wordt alleen de browser bijgewerkt met wijzigingen die zijn aangebracht in de gebruikersinterface wanneer de gebeurtenis-handler is voltooid. Daarnaast biedt het Blazor-mechanisme een gebeurtenishandler toegang tot statische gegevens die worden gedeeld tussen sessies; het JavaScript-model niet. Het verwerken van enkele vaak voorkomende gebeurtenissen, zoals
@onmousemovekan ertoe leiden dat de gebruikersinterface traag wordt omdat ze een retour van het netwerk naar de server vereisen. Mogelijk wilt u liever gebeurtenissen zoals deze in de browser afhandelen met behulp van JavaScript.
Important
U kunt de DOM manipuleren met behulp van JavaScript-code van een gebeurtenishandler en met behulp van C# Blazor-code. Blazor onderhoudt echter een eigen kopie van de DOM, die wordt gebruikt om de gebruikersinterface te vernieuwen wanneer dat nodig is. Als u JavaScript- en Blazor-code gebruikt om dezelfde elementen in de DOM te wijzigen, loopt u het risico dat de DOM beschadigd raakt. U kunt ook mogelijk inbreuk maken op de privacy en beveiliging van de gegevens in uw web-app.
Gebeurtenissen asynchroon verwerken
Blazor-gebeurtenis-handlers zijn standaard synchroon. Als een gebeurtenis-handler een potentieel langlopende bewerking uitvoert, zoals het aanroepen van een webservice, wordt de thread waarop de gebeurtenis-handler wordt uitgevoerd, geblokkeerd totdat de bewerking is voltooid. Deze situatie kan leiden tot slechte reacties in de gebruikersinterface. Als u dit probleem wilt bestrijden, kunt u een gebeurtenishandlermethode als asynchroon aanwijzen. Gebruik het C# async -trefwoord. De methode moet een Task object retourneren. Vervolgens kunt u de await operator in de gebeurtenis-handlermethode gebruiken om langlopende taken op een afzonderlijke thread te initiëren en de huidige thread voor ander werk vrij te maken. Wanneer een langlopende taak is voltooid, wordt de gebeurtenis-handler hervat. In het volgende voorbeeld ziet u een gebeurtenis-handler die asynchroon een tijdrovende methode uitvoert:
<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
}
}
Note
Lees Asynchrone programmeerscenario's voor gedetailleerde informatie over het maken van asynchrone methoden in C#.
Een gebeurtenis gebruiken om de focus in te stellen op een DOM-element
Op een HTML-pagina kan de gebruiker tabs maken tussen elementen en de focus wordt natuurlijk verplaatst in de volgorde waarin de HTML-elementen op de pagina worden weergegeven. In sommige gevallen moet u deze volgorde mogelijk overschrijven en de gebruiker dwingen een specifiek element te bezoeken.
De eenvoudigste manier om deze taak uit te voeren, is door de FocusAsync methode te gebruiken. Deze methode is een exemplaarmethode van een ElementReference object. De ElementReference naam moet verwijzen naar het item waarnaar u de focus wilt instellen. U wijst een elementverwijzing aan met het @ref kenmerk en maakt een C#-object met dezelfde naam in uw code.
In het volgende voorbeeld stelt de @onclick gebeurtenis-handler voor het <knopelement> de focus op het <invoerelement> . De @onfocus gebeurtenis-handler van het <invoerelement> geeft het bericht 'Ontvangen focus' weer wanneer het element de focus krijgt. Er <wordt naar het invoerelement> verwezen via de InputField variabele in de 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";
}
In de volgende afbeelding ziet u het resultaat wanneer de gebruiker de knop selecteert:
Note
Een app mag de focus alleen om een specifieke reden naar een specifiek besturingselement leiden, bijvoorbeeld om de gebruiker te vragen om invoer te wijzigen na een fout. Gebruik de focus niet om de gebruiker te dwingen de elementen op een pagina in een vaste volgorde te doorlopen. Dit ontwerp kan frustrerend zijn voor de gebruiker die mogelijk opnieuw naar elementen wil gaan om hun invoer te wijzigen.
Inline gebeurtenis-handlers schrijven
C# ondersteunt lambda-expressies. Met een lambda-expressie kunt u een anonieme functie maken. Een lambda-expressie is handig als u een eenvoudige gebeurtenis-handler hebt die u elders in een pagina of onderdeel niet opnieuw hoeft te gebruiken. In het eerste voorbeeld van het aantal klikken dat aan het begin van deze eenheid wordt weergegeven, kunt u de IncrementCount methode verwijderen en in plaats daarvan de methode-aanroep vervangen door een lambda-expressie waarmee dezelfde taak wordt uitgevoerd:
@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;
}
Note
Lees Lambda-expressies en anonieme functies voor meer informatie over de werking van lambda-expressies.
Deze methode is ook handig als u andere argumenten wilt opgeven voor een methode voor gebeurtenisafhandeling. In het volgende voorbeeld gebruikt de methode HandleClick een MouseEventArgs parameter op dezelfde manier als een gewone click-gebeurtenishandler, maar accepteert deze ook een tekenreeksparameter. De methode verwerkt de klik-gebeurtenis zoals voorheen, maar geeft ook het bericht weer als de gebruiker op de Ctrl-toets drukt. De lambda-expressie roept de HandleCLick methode aan, waarbij de MouseEventArgs parameter (mouseEvent) en een tekenreeks worden doorgegeven.
@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++;
}
}
}
Note
In dit voorbeeld wordt de JavaScript-functie alert gebruikt om het bericht weer te geven omdat er geen equivalente functie in Blazor is. U gebruikt JavaScript-interop om JavaScript aan te roepen vanuit Blazor-code. De details van deze techniek zijn het onderwerp van een afzonderlijke module.
Standaard DOM-acties voor gebeurtenissen overschrijven
Verschillende DOM-gebeurtenissen hebben standaardacties die worden uitgevoerd wanneer de gebeurtenis plaatsvindt, ongeacht of er een gebeurtenis-handler beschikbaar is voor die gebeurtenis. Bijvoorbeeld, de @onkeypress gebeurtenis voor een <invoerelement> geeft altijd het teken weer dat overeenkomt met de toets die door de gebruiker is ingedrukt en verwerkt vervolgens de toetsaanslag. In het volgende voorbeeld wordt de @onkeypress gebeurtenis gebruikt om de invoer van de gebruiker te converteren naar hoofdletters. Als de gebruiker een @ teken typt, geeft de gebeurtenis-handler bovendien een waarschuwing weer:
<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();
}
}
}
Als u deze code uitvoert en op de @ toets drukt, wordt de waarschuwing weergegeven, maar wordt het @ teken ook toegevoegd aan de invoer. De toevoeging van het @ teken is de standaardactie van de gebeurtenis.
Als u dit teken wilt onderdrukken in het invoervak, kunt u de standaardactie overschrijven met het preventDefault kenmerk van de gebeurtenis, zoals hieronder:
<input value=@data @onkeypress="ProcessKeyPress" @onkeypress:preventDefault />
De gebeurtenis wordt nog steeds geactiveerd, maar alleen de acties die door de event handler zijn gedefinieerd, worden uitgevoerd.
Sommige gebeurtenissen in een onderliggend element in de DOM kunnen gebeurtenissen in de bovenliggende elementen activeren. In het volgende voorbeeld bevat het <div-element> een @onclick gebeurtenis-handler. De <knop> in de <div> heeft een eigen @onclick gebeurtenis-handler. Daarnaast bevat het <div-element> een <invoerelement> :
<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
}
}
Wanneer de app wordt uitgevoerd, als de gebruiker op een element (of lege ruimte) klikt in het gebied dat wordt bezet door het <div-element> , wordt de methode HandleDivClick uitgevoerd en wordt een bericht weergegeven. Als de gebruiker de Click me knop selecteert, wordt de IncrementCount methode uitgevoerd, vervolgens HandleDivClick; de @onclick gebeurtenis verspreidt zich omhoog door de DOM-structuur. Als het <div-element> deel uitmaakte van een ander element dat ook de @onclick gebeurtenis heeft verwerkt, zou die gebeurtenis-handler ook worden uitgevoerd, enzovoort, naar de hoofdmap van de DOM-structuur. U kunt deze toename van gebeurtenissen beperken met het stopPropagation kenmerk van een gebeurtenis, zoals hier wordt weergegeven:
<div @onclick="HandleDivClick">
<button class="btn btn-primary" @onclick="IncrementCount" @onclick:stopPropagation>Click me</button>
<!-- Omitted for brevity -->
</div>
EventCallback gebruiken om gebeurtenissen tussen onderdelen af te handelen
Een Blazor-pagina kan een of meer Blazor-onderdelen bevatten en onderdelen kunnen worden genest in een bovenliggende en onderliggende relatie. Een gebeurtenis in een onderliggend onderdeel kan een methode voor gebeurtenis-handler in een bovenliggend onderdeel activeren met behulp van een EventCallback. Een callback verwijst naar een methode in het bovenliggende onderdeel. Het onderliggende onderdeel kan de methode uitvoeren door de callback aan te roepen. Dit mechanisme is vergelijkbaar met het gebruik van een delegate methode die verwijst naar een methode in een C#-toepassing.
Een callback kan één parameter aannemen.
EventCallback is een algemeen type. De typeparameter geeft het type van het argument aan dat wordt doorgegeven aan de callback.
Denk bijvoorbeeld aan het volgende scenario. U wilt een onderdeel maken met de naam TextDisplay waarmee de gebruiker een invoertekenreeks kan invoeren en die tekenreeks op een of andere manier kan transformeren. U kunt deze converteren naar hoofdletters, kleine letters, gemengde hoofd- en kleine letters, tekens eruit filteren, of een ander type transformatie uitvoeren. Wanneer u echter de code voor het TextDisplay onderdeel schrijft, weet u niet wat het transformatieproces wordt. In plaats daarvan wilt u deze bewerking uitstellen naar een ander onderdeel. De volgende code toont het TextDisplay onderdeel. Het biedt de invoertekenreeks in de vorm van een <invoerelement> waarmee de gebruiker een tekstwaarde kan invoeren.
@* 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;
}
}
Het TextDisplay onderdeel maakt gebruik van een object met de EventCallback naam OnKeyPressCallback. De code in de HandleKeypress methode roept de callback aan. De @onkeypress gebeurtenis-handler wordt uitgevoerd telkens wanneer een sleutel wordt ingedrukt en roept de methode aan HandleKeypress . De HandleKeypress methode maakt een KeyTransformation object met behulp van de sleutel die de gebruiker heeft ingedrukt en geeft dit object als parameter door aan de callback. Het KeyTransformation type is een eenvoudige klasse met twee velden:
namespace WebApplication.Data
{
public class KeyTransformation
{
public string Key { get; set; }
public string TransformedKey { get; set; }
}
}
Het key veld bevat de waarde die door de gebruiker is ingevoerd en het TransformedKey veld bevat de getransformeerde waarde van de sleutel na verwerking.
In dit voorbeeld is het EventCallback object een onderdeelparameter en wordt de waarde ervan opgegeven wanneer het onderdeel wordt gemaakt. Het onderdeel met de naam TextTransformer voert deze actie uit:
@page "/texttransformer"
@using WebApplication.Data;
<h1>Text Transformer - Parent</h1>
<TextDisplay OnKeypressCallback="@TransformText" />
@code {
private void TransformText(KeyTransformation k)
{
k.TransformedKey = k.Key.ToUpper();
}
}
Het TextTransformer onderdeel is een Blazor-pagina waarmee een exemplaar van het TextDisplay onderdeel wordt gemaakt. De parameter wordt gevuld OnKeypressCallback met een verwijzing naar de TransformText methode in de codesectie van de pagina. De TransformText methode gebruikt het KeyTransformation object dat is opgegeven als argument en vult de TransformedKey eigenschap in met de waarde in de Key eigenschap die is geconverteerd naar hoofdletters. In het volgende diagram ziet u de controlestroom wanneer een gebruiker een waarde invoert in het <invoerveld> in het TextDisplay onderdeel dat door de TextTransformer pagina wordt weergegeven:
De schoonheid van deze benadering is dat u het TextDisplay onderdeel kunt gebruiken met elke pagina die een callback voor de OnKeypressCallback parameter biedt. Er is een volledige scheiding tussen de weergave en de verwerking. U kunt de TransformText methode wijzigen voor elke andere callback die overeenkomt met de handtekening van de EventCallback parameter in het TextDisplay onderdeel.
U kunt een callback rechtstreeks aan een gebeurtenishandler koppelen zonder een tussenliggende methode te gebruiken als de callback is getypt met de juiste EventArgs parameter. Een onderliggend onderdeel kan bijvoorbeeld verwijzen naar een callback die muisevenementen @onclick als volgt kan verwerken:
<button @onclick="OnClickCallback">
Click me!
</button>
@code {
[Parameter]
public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
In dit geval wordt een EventCallbackMouseEventArgs typeparameter gebruikt, zodat deze kan worden opgegeven als de handler voor de @onclick gebeurtenis.