Events treten immer dann auf, wenn entweder eine Klasse (das kann eine Komponente sein, oder eine normale Klasse) mitteilen möchte, dass etwas passiert ist, oder sich etwas geändert hat.
Steuerelemente beispielsweise bieten unterschiedlichste Events an, auf die reagiert werden kann. Sei es, wenn ein Klick passiert ist, sich die Maus über diesem Event bewegt, oder Daten (beispielsweise TextBox) geändert wurden. Durch das Abonnieren eines Events (Erstellung eines Eventhandlers) kann entsprechen darauf reagiert werden.
Als Beispiel positionieren wir einen Button auf einer Form. Damit auf den Klick auf den Button reagiert werden kann, muss das Click-Ereignis abonniert werden:
button1.Click += new EventHandler(button1_Click);
Im Zuge dessen muss auch eine entsprechende Methode erstellt werden, ein so genannter Eventhandler:
void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Button clicked");
}Grundsätzlich besteht ein Eventhandler immer aus einem Methodennamen. Übergebene Parameter sind das Objekt, bei dem das Event aufgetreten ist sowie Event-Argumente, die Informationen mitgliefern können.
Innerhalb der Methode kann nun darauf reagiert werden. In unserem Beispiel würde nun eine
MessageBox angezeigt werden, sollte der Button geklickt werden.
In vielen Fällen ist es auch notwendig, eigene Events zu definieren, da Änderungen am eigenen Objekt anderen Objekten mitgeteilt werden sollen. In diesen Fällen ist es oft notwendig, wenn eigene Informationen mitgeliefert werden können, auf die im Eventhandler reagiert werden kann. Daher müssen eigene Event-Argumente implementiert werden. Diese leiten von der Klasse
EventArgs ab.
In einem kleinen Beispiel entwickeln wir eine Klasse Calculator. Diese stellt die Methode Add zur Verfügung. Mit hilfe dieser Methode können zwei Werte miteinander addiert werden. Was wir nun möchten ist, dass vor dem Addieren und nach dem Addieren ein Event geworfen wird, worauf wir reagieren können. Dieses Event teilt uns lediglich den Status der Berechnung mit und kann von uns ausgegeben werden.
Zu diesem Zweck benötigen wir eine eigene EventArgs-Implementierung, der wir unsere Nachricht (welche schlussendlich ausgegeben werden soll) mitgeben können:
public class CalculatorMessagesEventArgs : EventArgs
{
public String CalculatorMessage { get; set; }
public CalculatorMessagesEventArgs(String calculatorMessage)
{
CalculatorMessage = calculatorMessage;
}
}Wie zu sehen ist, leiten wir von der Klasse EventArgs ab. Der Rest ist lediglich eine Eigenschaft mit deren Hilfe die Message gesetzt bzw. gelesen werden kann. Als Hilfe besitzt der Konstruktor ebenfalls einen Parameter, über den die auszugebene Message mitgeliefert werden können.
Im nächsten Schritt benötigen wir einen Delegate, welcher den Methodenaufruf auf unseren eigentlichen Eventhandler weiterleitet. D.h. wird die Berechnung gestartet bzw. beendet, wird jeweils über diesen Delegate eine Zielmethode aufgerufen. Diese wird durch den noch zu erstellenden Eventhandler repräsentiert. Bevor wir daran gehen, muss jedoch noch der Delegate und die Events selbst erstellt werden:
public delegate void CalculatorHandler(object sender, CalculatorMessagesEventArgs e);
Durch diesen Delegate wird nun beschrieben, wie unsere Zielmethode (also der Eventhandler) auszusehen hat. Es wird definiert, dass ein Sender mitgeliefert werden muss, als auch entsprechende EventArgs. In diesem Fall vom Typ
CalculatorMessagesEventArgs.
Nun müssen die notwendigen Events definiert werden:
public event CalculatorHandler BeforeCalculation;
public event CalculatorHandler AfterCalculation;
Mit dieser Deklaration werden nun unsere Events, die vom
Calculator angeboten werden, defininiert. Zu beachten ist, dass zusätzlich zum Keyword event auch der zuvor erstellte Handler angegeben wird. Dieser repräsentiert den Delegate, der definiert, wie unser Eventhandler auszusehen hat.
Nun müssen die Events auch geworfen werden, dies passiert in unserer Methode Add, welche für das Addieren von Zahlen zuständig ist. Das Event BeforeCalculation wird vor der Berechnung geworfen, das Event AfterCalculation danach und zeigt an, dass die Berechnung beendet wurde:
public Int32 Add(Int32 a, Int32 b)
{
if (BeforeCalculation != null)
BeforeCalculation(this, new CalculatorMessagesEventArgs("Vor der Berechnung"));
Int32 result = a + b;
if (AfterCalculation != null)
AfterCalculation(this, new CalculatorMessagesEventArgs("Berechnung abgeschlossen"));
return result;
}Zu beachten ist die Abfrage auf
null. Diese muss durchgeführt werden, da damit überprüft wird, ob tatsächlich ein Eventhandler für dieses Event vorhanden ist. Wäre dies nicht der Fall, würde eine
NullReferenceException geworfen werden.
Insgesamt sieht der entworfene Code nun folgendermaßen aus:
using System;
using System.Collections.Generic;
using System.Text;
namespace WindowsFormsEventDemo
{
public delegate void CalculatorHandler(object sender, CalculatorMessagesEventArgs e);
public class Calculator
{
public event CalculatorHandler BeforeCalculation;
public event CalculatorHandler AfterCalculation;
public Int32 Add(Int32 a, Int32 b)
{
if (BeforeCalculation != null)
BeforeCalculation(this, new CalculatorMessagesEventArgs("Vor der Berechnung"));
Int32 result = a + b;
if (AfterCalculation != null)
AfterCalculation(this, new CalculatorMessagesEventArgs("Berechnung abgeschlossen"));
return result;
}
}
public class CalculatorMessagesEventArgs : EventArgs
{
public String CalculatorMessage { get; set; }
public CalculatorMessagesEventArgs(String calculatorMessage)
{
CalculatorMessage = calculatorMessage;
}
}
}Nun kann dies auch getestet werden. Dazu müssen die beiden Events abooniert und eine Berechnung durchgeführt werden, hier der gesamte Code. Die Berechnung wird durchgeführt, sobald der zuvor definierte Button geklickt wird:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
button1.Click += new EventHandler(button1_Click);
}
private void button1_Click(object sender, EventArgs e)
{
Calculator calc = new Calculator();
calc.AfterCalculation += new CalculatorHandler(calc_AfterCalculation);
calc.BeforeCalculation += new CalculatorHandler(calc_BeforeCalculation);
Console.WriteLine(calc.Add(4, 5));
}
private void calc_BeforeCalculation(object sender, CalculatorMessagesEventArgs e)
{
Console.WriteLine(e.CalculatorMessage);
}
private void calc_AfterCalculation(object sender, CalculatorMessagesEventArgs e)
{
Console.WriteLine(e.CalculatorMessage);
}
}Im Eventhandler des Buttons wird die Klasse Calculator instanziiert, danach werden die Events abonniert, die Eventhandler erstellt und schlussendlich die Berechnung gestartet. In den Eventhandlern der beiden Events wird die Nachricht des Calculators auf die Konsole ausgegeben. Diese sieht wie folgt aus:
Vor der Berechnung
Berechnung abgeschlossen
9
Noch ein Hinweis zum Abonnoeren von Events: Events werden mit += abonniert, können jedoch mit -= wieder abbestellt werden. Der Aufruf sieht in diesem Fall gleich aus:
calc.BeforeCalculation -= new CalculatorHandler(calc_BeforeCalculation);
Dies hat den Grund, dass nicht immer alle Events benötigt werden. Aus Gründen der Performance, oder weil besimmte Aktionen unterdrückt werden sollen, kann der Eventhandler wieder vom Event getrennt werden.