Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Olay, kodda yanıt verebileceğiniz veya "işleyebileceğiniz" bir eylemdir. Olaylar genellikle fareye tıklama veya bir tuşa basma gibi bir kullanıcı eylemi tarafından oluşturulur, ancak program kodu veya sistem tarafından da oluşturulabilir.
Olay temelli uygulamalar bir olaya yanıt olarak kod çalıştırır. Her form ve denetim, yanıt verebileceğiniz önceden tanımlanmış bir olay kümesini kullanıma sunar. Bu olaylardan biri oluşturulursa ve ilişkili bir olay işleyicisi varsa, işleyici çağrılır ve kod çalıştırılır.
Bir nesne tarafından tetiklenen olay türleri farklılık gösterir, ancak çoğu denetimde birçok tür yaygındır. Örneğin, çoğu nesnenin Click bir kullanıcı buna tıkladığında tetiklenen bir olayı vardır.
Uyarı
Birçok olay diğer olaylarla gerçekleşir. Örneğin, gerçekleşen DoubleClick olayı sırasında, MouseDown, MouseUpve Click olayları gerçekleşir.
Bir olayı oluşturma ve kullanma hakkında genel bilgi için bkz. .NET'te olayları işleme ve oluşturma.
Temsilciler ve rolleri
Temsilciler, olay işleme mekanizmaları oluşturmak için .NET içinde yaygın olarak kullanılan sınıflardır. Delegeler, Visual C++ ve diğer nesne yönelimli dillerde yaygın olarak kullanılan işlev işaretçilerine kabaca eşittir. Ancak işlev işaretçilerinin aksine, temsilciler nesne odaklı, tür açısından güvenli ve güvenlidir. Ayrıca, işlev işaretçisi yalnızca belirli bir işleve başvuru içerdiğinde, temsilci bir nesneye başvurudan ve nesne içindeki bir veya daha fazla yönteme başvurudan oluşur.
Bu olay modeli, olayları işlemek için kullanılan yöntemlere bağlamak amacıyla temsilcilerini kullanır. Temsilci, bir işleyici yöntemi belirterek diğer sınıfların olay bildirimine kaydolmasını sağlar. Olay gerçekleştiğinde, delege bağlı yöntemi çağırır. Temsilcileri tanımlama hakkında daha fazla bilgi için bkz.olayları işleme ve oluşturma
Delege, tek bir yönteme veya çok yönlü yayım olarak adlandırılan birden çok yönteme bağlanabilir. Bir olay için temsilci oluşturduğunuzda, genellikle çok yayınlı bir olay oluşturursunuz. Nadir bir özel durum, olay başına mantıksal olarak birden çok kez yinelenmeyecek belirli bir yordamla (iletişim kutusu görüntüleme gibi) sonuçlanan bir olay olabilir. Çoklu yayın temsilcisi oluşturma hakkında bilgi için bkz. Temsilcileri birleştirme (Çoklu Yayın Temsilcileri).
Çok noktaya yayın temsilcisi, ona bağlı yöntemlerin çağrı listesini tutar. Çok noktaya yayın temsilcisi, çağırma listesine bir yöntem eklemek için Combine yöntemini ve kaldırmak için Remove yöntemini destekler.
Bir uygulama bir olayı kaydettiğinde, denetim bu olay için temsilciyi çağırarak olayı tetikler. Temsilci sırasıyla bağlı yöntemi çağırır. En yaygın durumda (çok noktaya yayın temsilcisi), temsilci çağrı listesindeki her ilişkili yöntemi sırayla çağırır ve bu da bire çok bildirim yapılmasını sağlar. Bu strateji, denetimin olay bildirimi için hedef nesnelerin listesini tutmasına gerek olmadığı anlamına gelir; temsilci tüm kayıt ve bildirimi işler.
Temsilciler, birden fazla olayın aynı metoda bağlanmasına imkân tanıyarak çoktan bire bir bilgilendirme sağlar. Örneğin, bir düğme tıklama olayı ve menü-komut tıklama olayı aynı temsilciyi çağırabilir ve ardından bu ayrı olayları aynı şekilde işlemek için tek bir yöntem çağırır.
Temsilcilerle kullanılan bağlama mekanizması dinamiktir: Bir temsilci, çalışma zamanında imzası olay işleyicisiyle eşleşen herhangi bir yönteme bağlanabilir. Bu özellik sayesinde, bir koşula bağlı olarak bağlı yöntemi ayarlayabilir veya değiştirebilir ve denetime dinamik olarak bir olay işleyicisi ekleyebilirsiniz.
Windows Forms'daki olaylar
Windows Forms'daki olaylar, işleyici yöntemleri için EventHandler<TEventArgs> temsilci kullanılarak bildirilir. Her olay işleyicisi, olayı düzgün bir şekilde işlemenizi sağlayan iki parametre sağlar. Aşağıdaki örnekte, bir Button denetiminin Click olayı için bir olay işleyicisi gösterilmektedir.
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)
{
}
senderilk parametresi, olayı tetikleyen nesneye bir başvuru sağlar. İkinci parametre olan e, işlenen olaya özgü bir nesne geçirir. Nesnenin özelliklerine (ve bazen yöntemlerine) başvurarak fare olayları için farenin konumu veya sürükle ve bırak olaylarında aktarılan veriler gibi bilgileri alabilirsiniz.
Genellikle her olay, ikinci parametre için farklı bir olay nesnesi türüne sahip bir olay işleyicisi oluşturur. MouseDown ve MouseUp olayları için olanlar gibi bazı olay işleyicileri, ikinci parametreleri için aynı nesne türüne sahiptir. Bu tür olaylar için, her iki olayı işlemek için aynı olay işleyicisini kullanabilirsiniz.
Farklı denetimler için aynı olayı işlemek için aynı olay işleyicisini de kullanabilirsiniz. Örneğin, formda bir denetim grubunuz RadioButton varsa, her Clickolayı için RadioButton tek bir olay işleyicisi oluşturabilirsiniz. Daha fazla bilgi için bkz. Denetim olayını işleme.
Zaman uyumsuz olay işleyicileri
Modern uygulamaların genellikle web hizmetinden veri indirme veya dosyalara erişme gibi kullanıcı eylemlerine yanıt olarak zaman uyumsuz işlemler gerçekleştirmesi gerekir. Windows Forms olay işleyicileri bu senaryoları desteklemek için yöntemler olarak async bildirilebilir, ancak yaygın tuzaklardan kaçınmak için dikkat edilmesi gereken önemli noktalar vardır.
Temel zaman uyumsuz olay işleyici düzeni
Olay işleyicileri (Async Visual Basic'te) değiştiricisi ile async bildirilebilir ve zaman uyumsuz işlemler için (Await Visual Basic'te) kullanılabilir await . Olay işleyicilerinin döndürülmesi void (veya Visual Basic'te olarak Sub bildirilmesi) gerektiğinden, nadir kabul edilebilir kullanımlarından async void biridir (veya Async Sub Visual Basic'te):
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
Önemli
async void Önerilmez, ancak olay işleyicileri (ve gibi Control.OnClickolay işleyicisi benzeri kod) için gereklidir çünkü döndüremezlerTask. Önceki örnekte gösterildiği gibi özel durumları düzgün bir şekilde işlemek için her zaman beklenen işlemleri bloklar halinde try-catch sarmalayın.
Yaygın tuzaklar ve kilitlenmeler
Uyarı
, .Result.GetAwaiter().GetResult() veya gibi .Wait()engelleme çağrılarını hiçbir zaman olay işleyicilerinde veya kullanıcı arabirimi kodlarında kullanmayın. Bu desenler kilitlenmelere neden olabilir.
Aşağıdaki kod, kilitlenmelere neden olan yaygın bir anti-deseni gösterir:
// 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
Bu, aşağıdaki nedenlerden dolayı kilitlenmeye neden olur:
- UI iş parçacığı zaman uyumsuz yöntemini çağırır ve sonucun beklemesini engeller.
- Zaman uyumsuz yöntem ui iş parçacığının
SynchronizationContextyakalar. - Zaman uyumsuz işlem tamamlandığında, yakalanan ui iş parçacığında devam etmeye çalışır.
- kullanıcı arabirimi iş parçacığı işlemin tamamlanmasını beklerken engellendi.
- Hiçbir işlem devam etemediği için kilitlenme oluşur.
İş parçacıkları arası işlemler
Zaman uyumsuz işlemler içindeki arka plan iş parçacıklarından kullanıcı arabirimi denetimlerini güncelleştirmeniz gerektiğinde, uygun sıralama tekniklerini kullanın. Engelleyici ve engelleyici olmayan yaklaşımlar arasındaki farkı anlamak, duyarlı uygulamalar için çok önemlidir.
.NET 9, kullanıcı arabirimi iş parçacığına zaman uyumsuz hazırlama sağlayan ile kullanıma sunulmuştur Control.InvokeAsync. Bu işlemden farklı olarakControl.Invoke, Control.InvokeAsync kullanıcı arabirimi iş parçacığının ileti kuyruğuna gönderiler (engelleyici olmayan) gönderir (çağıran iş parçacığını engeller). hakkında Control.InvokeAsyncdaha fazla bilgi için bkz. Denetimlere iş parçacığı açısından güvenli çağrılar yapma.
InvokeAsync'in temel avantajları:
- Engelleyici olmayan: Çağıran iş parçacığının devam etmesi için hemen döndürür.
-
Zaman uyumsuz: Beklenebilen bir
Taskdöndürür. - Özel durum yayma: Özel durumları çağıran koda doğru şekilde yayılım.
-
İptal desteği: İşlem iptali için destekler
CancellationToken.
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
Kullanıcı arabirimi iş parçacığında çalıştırılması gereken gerçekten zaman uyumsuz işlemler için:
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
Tavsiye
.NET 9, zaman uyumsuz yöntemlerin zaman uyumlu aşırı yüklemelerine InvokeAsyncyanlış geçirildiğini algılamaya yardımcı olmak için çözümleyici uyarıları (WFO2001) içerir. Bu, "yangın ve unutma" davranışını önlemeye yardımcı olur.
En iyi yöntemler
- Sürekli olarak async/await kullanın: Zaman uyumsuz desenleri engelleme çağrılarıyla karıştırmayın.
-
Özel durumları işleme: Olay işleyicilerindeki try-catch bloklarında
async voidher zaman zaman uyumsuz işlemleri sarmalayın. - Kullanıcı geri bildirimi sağlayın: İşlem ilerleme durumunu veya durumunu göstermek için kullanıcı arabirimini güncelleştirin.
- İşlemler sırasında denetimleri devre dışı bırakma: Kullanıcıların birden çok işlem başlatmasını engelleyin.
- CancellationToken kullanma: Uzun süre çalışan görevler için işlem iptali desteği.
- ConfigureAwait(false): Gerekli olmadığında kullanıcı arabirimi bağlamını yakalamaktan kaçınmak için kitaplık kodunda kullanın.
İlgili içerik
.NET Desktop feedback