Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Az esemény olyan művelet, amelyre a kódban válaszolhat vagy "kezelhet". Az eseményeket általában egy felhasználói művelet, például az egérre kattintás vagy a kulcs lenyomása generálja, de programkód vagy a rendszer is létrehozhatja őket.
Az eseményvezérelt alkalmazások egy eseményre válaszul futtatnak kódot. Minden űrlap és vezérlőelem előre definiált eseménykészletet tesz elérhetővé, amelyekre válaszolhat. Ha az események egyike előjön, és van egy társított eseménykezelő, a rendszer meghívja a kezelőt, és futtatja a kódot.
Az objektum által kiváltott eseménytípusok eltérőek, de a legtöbb vezérlőben sok típus gyakori. A legtöbb objektum például olyan eseményt Click hoz létre, amely akkor merül fel, amikor egy felhasználó rá kattint.
Megjegyzés:
Sok esemény más eseményekkel is előfordul. Például a DoubleClick esemény során a MouseDown, MouseUpés Click események következnek be.
Az események létrehozásának és felhasználásának általános információi: Események kezelése és növelése a .NET-ben.
Meghatalmazottak és szerepkörük
A delegáltak a .NET-en belül gyakran használt osztályok, amelyek eseménykezelési mechanizmusokat építenek ki. A delegátok nagyjából egyenértékűek a Visual C++ és más objektumorientált nyelvekben gyakran használt függvénymutatókkal. A függvénymutatókkal ellentétben azonban a meghatalmazottak objektumorientáltak, típusbiztosak és biztonságosak. Ha a függvénymutató csak egy adott függvényre mutató hivatkozást tartalmaz, a meghatalmazott egy objektumra mutató hivatkozásból és az objektumon belüli egy vagy több metódusra mutató hivatkozásból áll.
Ez az eseménymodell meghatalmazottakat használja az eseményeknek a kezelésüket biztosító metódusokhoz való kötésére. A meghatalmazott egy kezelő metódus megadásával lehetővé teszi, hogy más osztályok regisztráljanak az eseményértesítésre. Az esemény bekövetkezésekor a meghatalmazott meghívja a kötött metódust. A delegáltak definiálásáról további információt lásd az Események kezelése és kiváltása című témakört.
A delegáltak egyetlen metódushoz vagy több metódushoz, más néven multicasthoz kapcsolhatók. Amikor egy eseményhez delegáltat hoz létre, általában csoportos delegációra szolgáló eseményt hoz létre. Ritka kivétel lehet egy olyan esemény, amely egy adott eljárást eredményez (például egy párbeszédpanel megjelenítését), amely nem ismétlődik logikailag eseményenként többször. A többszörös meghatalmazottak létrehozásáról további információt a Delegáltak egyesítése (Többszörös meghatalmazottak)című témakörben talál.
A csoportos küldési meghatalmazott fenntartja a hozzá tartozó metódusok meghívási listáját. A csoportos küldési delegált egy Combine metódust támogat, amely hozzáad egy metódust a meghívási listához, és egy Remove metódust az eltávolításához.
Amikor egy alkalmazás rögzít egy eseményt, a vezérlő az eseményt az esemény meghatalmazottjának meghívásával emeli ki. A meghatalmazott viszont meghívja a kötött metódust. A leggyakoribb esetben (csoportos küldési meghatalmazott) a meghatalmazott meghívja a híváslista minden kötött metódusát, amely egy-a-többhöz értesítést biztosít. Ez a stratégia azt jelenti, hogy a vezérlőnek nem kell fenntartania az eseményértesítés célobjektumainak listáját – a meghatalmazott kezeli az összes regisztrációt és értesítést.
A delegáltak emellett lehetővé teszik, hogy több esemény is ugyanahhoz a metódushoz legyen kötve, így lehetővé válik a több-az-egyhez értesítés is. Egy gombkattintásos esemény és egy menüparancsos kattintásos esemény például meghívhatja ugyanazt a delegáltat, amely ezután egyetlen metódust hív meg a különálló események ugyanúgy való kezeléséhez.
A meghatalmazottakhoz használt kötési mechanizmus dinamikus: a meghatalmazottak futásidőben bármilyen olyan metódushoz köthetők, amelynek aláírása megegyezik az eseménykezelőével. Ezzel a funkcióval konfigurálhatja vagy módosíthatja a kötött metódust egy feltételtől függően, és dinamikusan csatolhat egy eseménykezelőt egy vezérlőhöz.
Események a Windows Formsban
A Windows Forms eseményei a EventHandler<TEventArgs> kezelő metódusok meghatalmazottjával vannak deklarálva. Minden eseménykezelő két paramétert biztosít, amelyek lehetővé teszik az esemény megfelelő kezelését. Az alábbi példa egy Button vezérlőelem Click eseményének eseménykezelőjét mutatja be.
Private Sub button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles button1.Click
End Sub
private void button1_Click(object sender, System.EventArgs e)
{
}
Az első paraméter(sender) az eseményt okozó objektumra mutató hivatkozást tartalmaz. A második paraméter egy e, a kezelt eseményre jellemző objektumot ad át. Az objektum tulajdonságaira (és néha módszereire) hivatkozva olyan információkat szerezhet be, mint például az egér helye az egéreseményekhez vagy az áthúzásos eseményekben átvitt adatokhoz.
Általában minden esemény egy eseménykezelőt hoz létre, amely a második paraméterhez eltérő eseményobjektum-típussal rendelkezik. Egyes eseménykezelők, például a MouseDown és MouseUp események objektumtípusa megegyezik a második paraméter objektumtípusával. Az ilyen típusú események esetében ugyanazt az eseménykezelőt használhatja mindkét esemény kezelésére.
Ugyanazt az eseménykezelőt is használhatja, hogy ugyanazt az eseményt kezelje a különböző vezérlők esetében. Ha például egy űrlap vezérlőinek RadioButton egy csoportja van, létrehozhat egyetlen eseménykezelőt az Click egyes RadioButtoneseményekhez. További információ: Vezérlőesemények kezelése.
Aszinkron eseménykezelők
A modern alkalmazásoknak gyakran aszinkron műveleteket kell végrehajtaniuk a felhasználói műveletekre válaszul, például adatokat kell letölteni egy webszolgáltatásból vagy fájlokat elérni. A Windows Forms eseménykezelői deklarálhatók olyan módszerként async , amely támogatja ezeket a forgatókönyveket, de a gyakori buktatók elkerülése érdekében fontos szempontokat is figyelembe kell venni.
Alapszintű aszinkron eseménykezelő minta
Az eseménykezelők deklarálhatók a async (Visual Basic) módosítóval, és (a Visual Basicben) aszinkron műveletekhez használhatókawaitAwait.Async Mivel az eseménykezelőknek vissza kell térniük void (vagy deklarálniuk Sub kell a Visual Basicben), a (vagy Async Sub a Visual Basicben) az egyik ritka elfogadható felhasználási módjuk async void :
private async void downloadButton_Click(object sender, EventArgs e)
{
downloadButton.Enabled = false;
statusLabel.Text = "Downloading...";
try
{
using var httpClient = new HttpClient();
string content = await httpClient.GetStringAsync("https://github.com/dotnet/docs/raw/refs/heads/main/README.md");
// Update UI with the result
loggingTextBox.Text = content;
statusLabel.Text = "Download complete";
}
catch (Exception ex)
{
statusLabel.Text = $"Error: {ex.Message}";
}
finally
{
downloadButton.Enabled = true;
}
}
Private Async Sub downloadButton_Click(sender As Object, e As EventArgs) Handles downloadButton.Click
downloadButton.Enabled = False
statusLabel.Text = "Downloading..."
Try
Using httpClient As New HttpClient()
Dim content As String = Await httpClient.GetStringAsync("https://github.com/dotnet/docs/raw/refs/heads/main/README.md")
' Update UI with the result
loggingTextBox.Text = content
statusLabel.Text = "Download complete"
End Using
Catch ex As Exception
statusLabel.Text = $"Error: {ex.Message}"
Finally
downloadButton.Enabled = True
End Try
End Sub
Fontos
Bár async void ez nem ajánlott, az eseménykezelőknek (és az eseménykezelőhöz hasonló kódnak, például Control.OnClick), mivel nem tudnak visszaadni Task. A kivételek megfelelő kezelése érdekében mindig csomagolja be a várt műveleteket blokkokban try-catch , ahogy az előző példában is látható.
Gyakori buktatók és holtpontok
Figyelmeztetés
Soha ne használjon blokkoló hívásokat, például .Wait(), .Resultvagy .GetAwaiter().GetResult() eseménykezelőkben vagy felhasználói felületi kódban. Ezek a minták holtpontot okozhatnak.
Az alábbi kód egy gyakori blokkolásgátló mintát mutat be, amely holtpontokat okoz:
// DON'T DO THIS - causes deadlocks
private void badButton_Click(object sender, EventArgs e)
{
try
{
// This blocks the UI thread and causes a deadlock
string content = DownloadPageContentAsync().GetAwaiter().GetResult();
loggingTextBox.Text = content;
}
catch (Exception ex)
{
MessageBox.Show($"Error: {ex.Message}");
}
}
private async Task<string> DownloadPageContentAsync()
{
using var httpClient = new HttpClient();
await Task.Delay(2000); // Simulate delay
return await httpClient.GetStringAsync("https://github.com/dotnet/docs/raw/refs/heads/main/README.md");
}
' DON'T DO THIS - causes deadlocks
Private Sub badButton_Click(sender As Object, e As EventArgs) Handles badButton.Click
Try
' This blocks the UI thread and causes a deadlock
Dim content As String = DownloadPageContentAsync().GetAwaiter().GetResult()
loggingTextBox.Text = content
Catch ex As Exception
MessageBox.Show($"Error: {ex.Message}")
End Try
End Sub
Private Async Function DownloadPageContentAsync() As Task(Of String)
Using httpClient As New HttpClient()
Return Await httpClient.GetStringAsync("https://github.com/dotnet/docs/raw/refs/heads/main/README.md")
End Using
End Function
Ez a következő okok miatt okoz holtpontot:
- A felhasználói felületi szál meghívja az aszinkron metódust, és blokkolja az eredményre váró blokkokat.
- Az aszinkron metódus rögzíti a felhasználói felület szálát
SynchronizationContext. - Az aszinkron művelet befejeződésekor megpróbálja folytatni a rögzített felhasználói felületi szálat.
- A felhasználói felületi szál le van tiltva a művelet befejezésére várva.
- A holtpont azért fordul elő, mert egyik művelet sem folytatható.
Szálközi műveletek
Ha az aszinkron műveletek háttérszálaiból kell frissítenie a felhasználói felület vezérlőit, használja a megfelelő marshaling technikákat. A blokkoló és a nem blokkoló megközelítés közötti különbség megértése elengedhetetlen a rugalmas alkalmazásokhoz.
Bevezettük Control.InvokeAsynca .NET 9-et, amely aszinkron felhasználóbarát marshalingot biztosít a felhasználói felületi szálhoz. A hívószálat Control.InvokeAsync letiltó üzenetsortól eltérően Control.Invoke a felhasználói felületi szál üzenetsorába való bejegyzéseket (nem blokkoló) küldi el. További információ: Control.InvokeAsyncMenetbiztos hívások kezdeményezése vezérlőkhöz.
Az InvokeAsync fő előnyei:
- Nem blokkoló: Azonnal visszaadja a hívó szál folytatását.
-
Aszinkronbarát: Olyan értéket
Taskad vissza, amely várható. - Kivételpropagálás: A kivételek megfelelően propagálása a hívókódra.
-
Lemondási támogatás: Támogatja
CancellationTokena művelet törlését.
private async void processButton_Click(object sender, EventArgs e)
{
processButton.Enabled = false;
// Start background work
await Task.Run(async () =>
{
for (int i = 0; i <= 100; i += 10)
{
// Simulate work
await Task.Delay(200);
// Create local variable to avoid closure issues
int currentProgress = i;
// Update UI safely from background thread
await progressBar.InvokeAsync(() =>
{
progressBar.Value = currentProgress;
statusLabel.Text = $"Progress: {currentProgress}%";
});
}
});
processButton.Enabled = true;
}
Private Async Sub processButton_Click(sender As Object, e As EventArgs) Handles processButton.Click
processButton.Enabled = False
' Start background work
Await Task.Run(Async Function()
For i As Integer = 0 To 100 Step 10
' Simulate work
Await Task.Delay(200)
' Create local variable to avoid closure issues
Dim currentProgress As Integer = i
' Update UI safely from background thread
Await progressBar.InvokeAsync(Sub()
progressBar.Value = currentProgress
statusLabel.Text = $"Progress: {currentProgress}%"
End Sub)
Next
End Function)
processButton.Enabled = True
End Sub
A felhasználói felületen futtatandó valóban aszinkron műveletek esetén:
private async void complexButton_Click(object sender, EventArgs e)
{
// This runs on UI thread but doesn't block it
statusLabel.Text = "Starting complex operation...";
// Dispatch and run on a new thread
await Task.WhenAll(Task.Run(SomeApiCallAsync),
Task.Run(SomeApiCallAsync),
Task.Run(SomeApiCallAsync));
// Update UI directly since we're already on UI thread
statusLabel.Text = "Operation completed";
}
private async Task SomeApiCallAsync()
{
using var client = new HttpClient();
// Simulate random network delay
await Task.Delay(Random.Shared.Next(500, 2500));
// Do I/O asynchronously
string result = await client.GetStringAsync("https://github.com/dotnet/docs/raw/refs/heads/main/README.md");
// Marshal back to UI thread
await this.InvokeAsync(async (cancelToken) =>
{
loggingTextBox.Text += $"{Environment.NewLine}Operation finished at: {DateTime.Now:HH:mm:ss.fff}";
});
// Do more async I/O ...
}
Private Async Sub complexButton_Click(sender As Object, e As EventArgs) Handles complexButton.Click
'Convert the method to enable the extension method on the type
Dim method = DirectCast(AddressOf ComplexButtonClickLogic,
Func(Of CancellationToken, Task))
'Invoke the method asynchronously on the UI thread
Await Me.InvokeAsync(method.AsValueTask())
End Sub
Private Async Function ComplexButtonClickLogic(token As CancellationToken) As Task
' This runs on UI thread but doesn't block it
statusLabel.Text = "Starting complex operation..."
' Dispatch and run on a new thread
Await Task.WhenAll(Task.Run(AddressOf SomeApiCallAsync),
Task.Run(AddressOf SomeApiCallAsync),
Task.Run(AddressOf SomeApiCallAsync))
' Update UI directly since we're already on UI thread
statusLabel.Text = "Operation completed"
End Function
Private Async Function SomeApiCallAsync() As Task
Using client As New HttpClient()
' Simulate random network delay
Await Task.Delay(Random.Shared.Next(500, 2500))
' Do I/O asynchronously
Dim result As String = Await client.GetStringAsync("https://github.com/dotnet/docs/raw/refs/heads/main/README.md")
' Marshal back to UI thread
' Extra work here in VB to handle ValueTask conversion
Await Me.InvokeAsync(DirectCast(
Async Function(cancelToken As CancellationToken) As Task
loggingTextBox.Text &= $"{Environment.NewLine}Operation finished at: {DateTime.Now:HH:mm:ss.fff}"
End Function,
Func(Of CancellationToken, Task)).AsValueTask() 'Extension method to convert Task
)
' Do more Async I/O ...
End Using
End Function
Jótanács
A .NET 9 elemző figyelmeztetéseket (WFO2001) tartalmaz, amelyek segítenek észlelni, ha az aszinkron metódusok helytelenül vannak átadva a szinkron túlterhelésnek InvokeAsync. Ez segít megelőzni a "fire-and-forget" viselkedést.
Ajánlott eljárások
- Használja az aszinkron/várakozási elemet következetesen: Ne keverje az aszinkron mintákat blokkoló hívásokkal.
-
Kivételek kezelése: Az eseménykezelőkben mindig sorba kell csomagolni az aszinkron műveleteket a try-catch blokkokban
async void. - Felhasználói visszajelzés küldése: Frissítse a felhasználói felületet a művelet előrehaladásának vagy állapotának megjelenítéséhez.
- Vezérlők letiltása a műveletek során: Megakadályozza, hogy a felhasználók több műveletet indítsanak.
- A CancellationToken használata: A művelet megszakításának támogatása hosszú ideig futó feladatokhoz.
- Fontolja meg a ConfigureAwait(false): A kódtárkódban használva elkerülheti a felhasználói felületi környezet rögzítését, ha nincs szükség rá.
Kapcsolódó tartalom
.NET Desktop feedback