5.3 Änderungen im Projekt »GeometricObjects«
5.3.1 Überarbeitung des Events »InvalidMeasure«

In Abschnitt 5.2 wurde das Ereignis InvalidMeasure in der Klasse Circle eingeführt. Eine inakzeptable Wertzuweisung kann natürlich auch die beiden Eigenschaften Length und Width der Klasse Rectangle betreffen. Wir sollten daher das Ereignis in der Basisklasse GeometricObject bereitstellen, einschließlich der entsprechenden OnXxx-Methode.
Nun könnte es natürlich sein, dass den Benutzer einer der von GeometricObject abgeleiteten Klassen interessiert, welche Eigenschaft von der Ablehnung der Zuweisung betroffen ist. Um auch diese Information zu liefern, wollen wir die Klasse InvalidMeasureEventArgs um eine Eigenschaft vom Typ string ergänzen, in der der Bezeichner der betroffenen Eigenschaft angegeben wird. Falls der Name der fehlerverursachenden Eigenschaft nicht ausdrücklich namentlich angegeben oder null übergeben wird, soll bei einer Auswertung von PropertyName die Ausgabe [unknown] erfolgen.
Nach diesen Vorgaben sieht der Code in der Klasse InvalidMeasureEventArgs wie folgt aus:
public class InvalidMeasureEventArgs : EventArgs {
// Felder
private int _InvalidMeasure;
private string _PropertyName;
// Eigenschaften
public int InvalidMeasure {
get { return _InvalidMeasure; }
}
public string PropertyName {
get { return _PropertyName; }
}
// Konstruktor
public InvalidMeasureEventArgs(int invalidMeasure, string propertyName) {
_InvalidMeasure = invalidMeasure;
if (propertyName == "" || propertyName == null)
_PropertyName = "[unknown]";
else
_PropertyName = propertyName;
}
}
Listing 5.27 Die Definition der Klasse »InvalidMeasureEventArgs«
Natürlich müssen Sie diese Erweiterung auch in den Eigenschaften Radius, Width und Length berücksichtigen.
5.3.2 Weitere Ereignisse
Das Ereignis InvalidMeasure ist eher untypisch, weil es im Fehlerfall ausgelöst wird (später werden wir diese Stelle im Programm auch noch viel besser codieren). Nun wollen wir unser Projekt auch um zwei sehr typische Ereignisse erweitern, die in der Methode Move ausgelöst werden sollen. Es sind die Ereignisse Moving (wird ausgelöst vor der eigentlichen Verschiebung) und Moved (wird ausgelöst nach erfolgter Verschiebung). Solche Ereignispärchen treten häufig im .NET Framework auf und entsprechen in ihrem Verhalten immer demselben Muster. Werden die Xxxing-Ereignisse abonniert, besteht die Möglichkeit, die eingeleitete Operation im buchstäblich letzten Moment doch noch abzubrechen. Üblicherweise stellt das EventArgs-Objekt dieser Ereignisse dazu eine Eigenschaft namens Cancel bereit, die in der Ereignisquelle nach der Eventauslösung ausgewertet werden muss. Zum Abbrechen der Operation muss der Ereignisempfänger Cancel nur auf true setzen.
public class MovingEventArgs : EventArgs
{
public bool Cancel { get; set; }
}
Listing 5.28 Die Klasse »MovingEventArgs«
MovingEventArgs ist sehr einfach implementiert. Da die Eigenschaft Cancel im Ereignishandler unter Umständen einen neuen Wert erhält, der ausgewertet werden muss, genügt uns die einfache Deklaration einer booleschen Variablen.
Das Moved-Ereignis, das nach der Verschiebeoperation ausgelöst wird, dient nur dazu, dem Benutzer die Möglichkeit zu eröffnen, nach der Verschiebung nach eigenem Ermessen zusätzliche Operationen zu codieren. Das EventArgs-Objekt soll selbst keine weiteren, zusätzlichen Daten bereitstellen. Daher können wir direkt auf die Klasse EventArgs zurückgreifen.
Mit diesen Überlegungen lassen sich die beiden notwendigen Delegates beschreiben.
public delegate void MovingEventHandler(Object sender, MovingEventArgs e);
public delegate void MovedEventHandler(Object sender, EventArgs e);
Listing 5.29 Zusätzliche Delegaten im Projekt »GeometricObjects«
In der Klasse GeometricObject ist die Move-Methode definiert, in der die beiden Ereignisse Moving und Moved ausgelöst werden sollen. Folglich gilt es, in dieser Klasse die beiden Ereignisse zu definieren. Dazu gehören auch die entsprechenden geschützten Methoden, die die Ereignisauslösung kapseln.
Move wird um den Code ergänzt, der prüft, ob der Anwender die eingeleitete Verschiebung des Bezugspunktes abbrechen möchte. Dazu wird die Eigenschaft Cancel des MovingEventArgs-Objekts untersucht. Hat der Benutzer mit true kundgetan, doch nicht zu verschieben, wird Move mit return beendet.
public abstract class GeometricObject {
// Ereignisse
public event MovingEventHandler Moving;
public event MovedEventHandler Moved;
// Geschützte Methoden
protected virtual void OnMoving(MovingEventArgs e){
if (Moving != null)
Moving(this, e);
}
protected virtual void OnMoved(EventArgs e){
if (Moved != null)
Moved(this, e);
}
public virtual void Move(double dx, double dy){
// Moving-Ereignis
MovingEventArgs e = new MovingEventArgs();
OnMoving(e);
if (e.Cancel == true) return;
XCoordinate += dx;
YCoordinate += dy;
// Moved-Ereignis
OnMoved(new EventArgs());
}
[...]
}
Listing 5.30 Ergänzung der Klasse »GeometricObject«
Damit sind wir aber noch nicht fertig. Wir müssen uns noch einmal die Überladung der Methode Move in Circle genau ansehen, die momentan immer noch wie folgt implementiert ist:
public virtual void Move(double dx, double dy, int dRadius)
{
Move(dx, dy);
Radius += dRadius;
}
Beim Aufruf von Move in der ersten Anweisung könnte die eingeleitete Verschiebung noch abgebrochen werden. Das wird auch gemacht, aber nur halbherzig. Denn die Bezugskoordinaten werden zwar in X- und Y-Richtung nicht verschoben, aber der Radius wird trotzdem geändert, weil die zweite Anweisung in der dreifach parametrisierten Move-Methode keine Kenntnis vom Abbruch der Operation bekommt. Besser wäre es stattdessen, den Aufruf der zweifach parametrisierten Methode durch die vollständige Implementierung zu ersetzen:
public virtual void Move(double dx, double dy, int dRadius) {
MovingEventArgs e = new MovingEventArgs();
// Moving-Ereignis auslösen
OnMoving(e);
if (e.Cancel == true) return;
XCoordinate += dx;
YCoordinate += dy;
Radius += dRadius;
// Moved-Ereignis auslösen
OnMoved(new EventArgs());
}
Listing 5.31 Änderung der überladenen »Move«-Methode
In gleicher Weise muss auch die vierfach parametrisierte Move-Methode in Rectangle angepasst werden.
Sie finden den kompletten Code des überarbeiteten Beispiels auf der Buch-DVD unter
\Beispiele\Kapitel 5\GeometricObjectsSolution_6.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.