Daily-Hour 21: Ereignisse und Interaktionen Teil 5: MouseEventArgs/MouseBottonEventArgs

8. Februar 2010

Der MouseEvent-Handler wird mit zwei Parameter aufgerufen. Der erste ist eine Referenz auf das Objket, welches die Behandlungsroutine aufruft. Der andere Parameter ist entweder vom Typ MouseEventArgs oder MouseButtonEventArgs, abhängig davon welches Event das Ereigniss auslöst.

Der MouseEventArgs-Parameter wird verwendet, wenn die auslösenden Ereignisse MouseEnter, MouseLeave oder MouseMove ist. MouseButtonEventArgs ist Ableitung vom MouseEventArgs, die um die Eigenschaft Handled erweitert wurde, damit der Bubbling-Mechanismus auch gestoppt werden kann.

Sowohl MouseEventArgs als auch MouseButtonEventArgs liefert noch weitere Informationen, welche sehr nützlich sein können.

Die wichtigste dürfte GetPosition sein. Bei Aufruf dieser Methode erfährt man die Koordinaten des Mauszeigers und kann damit arbeiten. Aber ganz so einfach ist es nicht, denn welche Koordinaten werden jetzt genau zurückgeliefert?

Nachdem Silverlight, in einem Browser-PlugIn ausgeführt wird, kann es nun mal nicht auf die Bildschirmkoordinaten zurückgreifen. Deshalb sind es auch die Koordinaten innerhalb des Browser-PlugIn, wenn der GetPosition-Methode als Parameter null übergeben wird.

Man kann aber auch die Position innerhalb eines Objektes über diese Methode ermitteln, in dem man eine Referenz auf das Objekt als Parameter übergibt. Zusätzlich ist es auch möglich jedes x-beliebige Objekt als Referenz heranzuziehen. Der ausschlaggebende Punkt dabei ist immer die linke obere Ecke der BoundingBox eines Objektes. Die Koordinaten werden deshalb negativ, wenn sich die Maus links bzw. oberhalb des Objektes befindet, welches als Referenz für die Koordinatenangabe dient.

Erstellen wir einige Objekt erstellen um das ganze zu betrachten.

xmlns=”http://shemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://shemas.microsoft.com/winfx/2006/xaml”
Width=”640″ Height=”480″ MouseMove=”MouseMoveUserControl”>

Fill=”Red”> Canvas.Left=”50″ Canvas.Top=”75″/>

Fill=”LightBlue” Canvas.Left=”150″ Canvas.Top=”175″/>

FontFamily=”Verdana” FontSize=”15″/>


Innerhalb des UserControls wurde jetzt ein Canvas als Child deklariert. Innerhalb dieses Canvas gibt es 3 weitere Kindobjekte (Ellipse, Rectangle und TextBlock)

Als nächstes benötigt man noch die Methode, die bei Eintritt des MouseMove-Events aufgerufen wird. Da MouseMove ja weitergeleitet wird, können wir diese Event im obersten Element (UserControl) ab arbeiten. In der Methode, welche aus Event-Handler mit den Parameter MouseEventArg aufgerufen wird, verwendet man nun die GetPosition-Methode und gibt dieser verschiedene Objekte als Parameter mit, um die Mauskoordinaten in Relation zu den einzelnen Objekten zu ermitteln.

private void MouseOverUserControl(object sender, MouseEventArgs e)
{
TextDisplay.Text = “MouseMove Koordinaten (UserControl): x= ” +
e.GetPosition(null).X.ToString() + “, y= ” +
e.GetPosition(null).Y.ToString();
TextDisplay.Text += “\nMouseMove Koordinaten (Canvas): x= ” +
e.GetPosition(RootCanvas).X.ToString() + “, y= ” +
e.GetPosition(RootCanvas).Y.ToString();

TextDisplay.Text += “\nMouseMove Koordinaten (Ellipse): x= ” +
e.GetPosition(Circle).X.ToString() + “, y= ” +
e.GetPosition(Circle).Y.ToString();

TextDisplay.Text += “\nMouseMove Koordinaten (Rectangle): x= ” +
e.GetPosition(Square).X.ToString() + “, y= ” +
e.GetPosition(Square).Y.ToString();

}

Die Methode selbst ist relativ einfach, auch wenn diese zunächst verwirrend erscheint. Man erzeugt hier einen Text im ersten Schritt und fügt in den nachfolgenden Schritten weiteren Text hinzu. Wobei es auch möglich gewesen wäre, nicht immer TextDisplay.Text +=… aufzurufen, sondern man hätte wie bei schon z.T. gezeigt, einfach den TextDisplay mit einem einfach “+” anhängen können.

GetPostion()
erwartet immer als Parameter das Objekt, welches man für die Koordinatenmessung heranziehen will. Als Rückgabewerte erhält man von dieser Methdoe den Typ Point, also einen X- und Y-Wert.

Wenn man sich den Aufruf von GetPosition() isoliert betrachten will und die Koordinaten zum UserControl ermitteln will:

Point pt = e.GetPosition(null);
double xValue = pt.X;
double yValue = pt.Y;

Hier deklariert man in der ersten Zeile eine Point-Struktur, und übergibt dieser mittels der GetPosition()-Methode die entsprechenden Werte. Da diese eine Pointstruktur haben, kann man davon die X- und Y-Meber abfragen, die wir schließlich in den Double-Werten xValue und yValue speichern.

Wenn jetzt die MouseOverUserControl-Methode betrachtet wird, kann man erkennen, dass in einer Textzeile die X- und Y-Werte ausgelesen werden und mit ein paar andern Textbestandteilen zu einem String zusammengefasst werden. Die beiden Double-Werte werden mittels ToString() noch in einen String umgewandelt.

Wenn man da Projekt jetzt startet, sieht man die verschiedenen Koordinatenwerte, in dem man die Maus über die Objekte bewegt.

juergen79 Informatik, Internet, Microsoft, Programmiersprachen, RIA, Silverlight , , ,

Spiele-KI: Miss dich mit anderen

7. Februar 2010

Mitte der 90iger Jahre, gab es bei uns in der Schule (HTBLuVA Wr. Neustadt) wo ich den Zweig EDVO besucht einen kleinen Wettbewerb, damals besuchte ich die 1. Klasse und war eigentlich der einzige aus der ersten Klasse der daran teilnahm und schnitt überraschenderweise recht gut ab, obwohl meine Programmierfähigkeit zu diesem Zeitpunkt noch minimal war, da ich gerade erst ein halbes Jahr programmiert habe.

Der Wettbewerb damals wurde CoreWar genannt, und wie das aktuelle CoreWars war es in Assembler zu schreiben, mit dem ich mich bis zu dem Zeitpunkt überhaupt nicht beschäftigt hatte und seit dem auch kaum wieder. Aber es war eine interessante Erfahrung es gibt  zu CoreWars Tutorials und ein Framework zum downloaden.

Aber solche Wettbewerbe gibt es nicht nur für Assembler, auch für .NET-Sprachen gibt es solche Wettbewerbe.

AntMe
In AntMe schickst du ein Ameisenvolk in einem virtuellen Wald auf Nahrungssuche. Wie im echten Leben ist die Futtermenge begrenzt und es gibt überall Feinde. Die eigenen Ameisen werden über ein kleines Programm gesteuert, welches der Spieler selbst schreibt und so auf verschiedene Ereignisse reagieren kann. Hierbei gilt es durch clevere Programmierung und Strategien seinem eigenen Volk einen Vorteil zu schaffen.

AntMe v1.6 ist erhältlich als

Natürlich gibt es für das “Spiel! auch ein Tutorial und und Bücher AntMe! – Programmieren und Spielen mit den Ameisen und Visual C# von W. Saumweber, T. Wendel, W. Gallo, S. Loers bzw. AntMe! – Spielend programmieren lernen mit Visual Basic und den Ameisen von Hans-Georg Schumann.

C# Pirates
Ein anderes ähnliches Projekt ist C# Pirates bei dem es keine Rolle spielt, ob man Anfänger in C# ist oder schon ein C#-Profi. C# Pirates ist ein Battleship und lädt jeden ein einen Algorithmus zu schreiben und hin hochzuladen und in die Hall of Fame einzutragen. Seinen programmierten Spieler kann man auch auf einem netten Silverlight Spielfeld betrachten.

C# Pirates kann man nicht manuell spielen sondern man schreibt seinen eigenen Spieler in C# welcher dann gegen andere geschrieben Spieler von anderen spielt. Man kann das Spiel im Text-basiert Feld oder in einem Silverlight Spielfeld betrachten.

juergen79 Informatik, Internet, Microsoft, Programmiersprachen , ,

Daily-Hour 20: Ereignisse und Interaktionen Teil 5 – MouseMove/MouseLeftButtonDown & MouseLeftButtonUp

7. Februar 2010

MouseMove
Eine weiteres wichtiges Mouse-Event ist das MouseMove-Event, dass immer ausgelöst wird, wenn sich die Maus innerhalb eines Objekts bewegt, dh die Koordinaten des Mauspointer ändern sich innerhalb der Objektgrenzen. Sobald der User die Maus stillhält, werden keine MouseMove-Events gesendet. Natürlich gilt es auch, wenn der Mauszeiger, das Objekt verlässt (zumindest für dieses Objekt).

Im Gegensatz zu MouseEnter und MouseLeave unterliegt dieses Event wieder dem Bubbling-Mechanismus und wird innerhalb des visuellen Baums an Parent-Objekt weiterg.eleitet.

Wir nehmen wieder das Beispiel von vorhin und erweitern es wie bisher schon. Der Kreis soll sich solange drehen bis ein MouseMove-Event eintritt. Um diese Drehung auch sehen zu können, benötigen wir eine Umrandung, welche aber nicht durchgehend sein darf. Innerhalb des Ellipse-Objekt definieren wir die RotateTransform-Transformation, welche auch die x:Name-Eigenschaft erhält, damit wir später per Code auch darauf zurückgreifen können:

<Ellipse Canvas.Left=”50″ Canvas.Left=”50″ x:Name=”Kreis” Width=”150″ Height=”150″
       Fill=”DarkGreen” MouseEnter=”MausEnterKreis” MouseMove=”MausMoveKreis”
       StrokeDashCap=”Round” StrokeDashArray=”3 3″ StrokeThickness=”4″ Stroke=”Blue”>
       <Ellipse.RenderTransfrom>
           <RotateTransform x:Name=”RoatateKreis” Angle=”360″ CenterX=”100″
                CenterY=”100″/>
       </Ellipse.RenderTransform>
</Ellipse>

Jetzt muss im Code nur noch sorgen, dass der Winkel bei einem MouseMove-Event von RotateTransform verändert wird. Sollte der Winkel dabei 0 erreichen, muss der Winkle wieder auf 360 zurück.

private void MausMoveKreis(object sender, MouseEventArgs e)
{
     –RotateKreis.Angle;
     if (RotateKreis.Angle == 0)
          RotateKreis.Angle = 360;
}

In der ersten Zeile wird der Winkel neu berechnet, indem man den aktuellen Wert einfach um eins verringert in in der if-Abfrage wird geprüft, ob der Winkel 0 erreicht und dann wird der Wert auf 360 zurückgesetzt. Damit man keine Fehlermeldung produziert, da es ja keine negativen Winkel gibt.

MouseLeftButtonDown & MouseLeftButtonUp
MouseLeftButtonDown wurde schon in den vorangegangenen Beispielen eingesetzt. Es tritt auf, wenn ein User die Maustaste (linke) innerhalb eines Objektes niederdrückt. Es unterliegt auch dem Bubbling-Mechanismus, wie schon festgestellt wurde.

Das Gegenstück dazu ist MouseLeftButtonUp und tritt ein, wenn ein User die Maustaste wieder loslässt. Auch wenn die beiden MouseEvents häufig gemeinsam auftreten, muss nicht immer nach einem MouseLeftButtonDown nicht zwingendermaßen ein MouseLeftButtonUp folgen. Denn der Anwender kann nach dem drücken der linken Maustaste (dem generieren des MouseLeftButtonDown-Events) das Objekt verlassen, wodurch das MouseLeftButtonUp-Event nicht mehr an das selbe Objekt gesendet wird. Deshalb kann es sinnvoll sein, zusätzlich noch das MouseLeave-Event abzufragen.

Das ClickEvent ist bei UIElementen nicht vorgesehen, kann aber durch das Reagieren auf das MouseLeftButtonUp-Event simuliert werden.

Einige Controls (zB das Button-Control) bieten zusätzlich ein Click-Event an, weil der Entwickler dies im Zusammenhang mit Buttons gewohnt sind.

juergen79 Informatik, Internet, Microsoft, Programmiersprachen, RIA, Silverlight , , ,

Daily-Hour 19: Ereignisse und Interaktionen Teil 4 – MouseEnter und MouseLeave

6. Februar 2010

MouseEnter
Das Ereignis MouseEnter tritt ein, sobald der Mauszeiger erstmals ein bestimmtes Objekt überstreicht. Wenn der Mauszeiger das Objekt berührt, wird gleich das MouseEnter-Event ausgelöst. Für dieses Objekt kann MouseEnter erst wieder ausgelöst werden, wenn der Mauszeiger, das Objekt wieder verlässt und erneut in das Objekt hineinfährt.

Das MouseEnter-Event unterliegt nicht dem Bubbling Mechanismus und wird deshalb nicht im visuellen Baum weitergeleitet. es kann nur in Verbindung eines bestimmten Objekt auftreten.

Mit dem Event ist es möglich, mit dem entsprechenden Event-Handler die Farbe oder die Größe eines Objektes zu ändern. Dafür muss man nur eine Objekt, wie z.B. eine Ellipse


       Fill=”DarkGreen” MouseEnter=”MausEnterKreis”/>

Somit ist der MouseEnter-Event-Handler aktiviert, sobald der Mauszeiger über der Ellipse steht. Jetzt muss noch die Methode aktiviert werden.

private void MouseEnterKreis(object sender, MouseEventArgs e)
{
    Kreis.Fill = new SolidColorBrush(Colors.DarkBlue);
}

So wird die Farbe des Kreises, wenn man das Objekt mit dem Mauszeiger berührt, blau gefärbt.

MouseLeave
MouseLeave ist das Gegenstück zu MouseEnter und tritt ein, sobald ein Mauszeiger ein bestimmtes Objekt verlässt. Wie auch MouseEnter unterliegt MouseLeave nicht dem Bubbling-Mechanismus und tritt nur mit dem bestimmten Objekt auf.

Gemeinsam mit dem MouseEnter-Event bildet dieses Paar die vollständige Logik, um Sensitivität anzuzeigen. Deshalb wird einen weiteren Event-Handler hinzu, der die Farbe wieder zur Ursprungsfarbe zurücksetzt, sobald die Maus den Kreis wieder verlässt.

MouseLeave=”MouseLeaveKreis”

Schließlich muss noch die zusätzliche MouseLeaveKreis-Methode wird die Farbe wieder auf Dunkelgrün setzten. Das Problem besteht, dass unsere Ursprungsfarbe Dunkelgrün (DarkGreen), in der Methode können wir nur auf die ColorsCollection zugreifen können, dort sind nur einige der Farben definiert, DarkGreen gehört nicht dazu. Es gibt noch die Color-Struktur von .NET. Sie definiert die Methode FromArgb, mit dem die Farbe definiert werden kann:

Color.FromArgb(a, r, g, b)

Wobei a, r, g und b als Bytewerte übergeben werden und können Werte zwischen 0 und 255 haben. Man kann die Farbtabelle nun unter www.farb-tabelle.de nach schauen. Für a nehmen wir 255 an. Die restlichen Werte übernehmen wir aus der Farbtabelle. Um beim MouseLeave-Event auf Dunkelgrün zurückwechseln zu können, benötigen wir als folgende Zeile im Code:

Kreis.Fill = new SolidColorBrush(Color.FromArgb(255,0,100,0));

Der Fill-Eigenschaft des Kreises wird hier wieder ein SolidColorBrush zugewisen und als Parameter wird die gewünschte Farbe übergeben.

juergen79 Informatik, Internet, Microsoft, Programmiersprachen, RIA, Silverlight , , ,

Daily-Hour 18: Ereignisse und Interaktionen Teil 3 – Mouse Events (MouseHits)

5. Februar 2010

Nachdem die Maus wohl eines der wichtigsten Eingabegeräte des User ist, sind auch MouseEvents vermutlich wichtige Events für Interaktionen. Die Events werden ausgelöst, wenn der Anwender mit seiner Maus (inkludiert natürlich auch TouchPad) eine Aktion ausführt.

Neben der Maus und dem TouchPad (einfachhalber in Zukunft nur Maus genannt) gibt es noch komplexere Zeigegeräte, wie z.B. Stift auf einem Tablet-PC, bei dem auch die Stärke des Drucks registriert wird, welche auch in SL 4 z.T. schon verfügbar sind.

In den vorangegegangenen Teil (Bubbling und Maustaste) haben wir schon ein MousEvent angeschaut, aber es gibt im allgemeinen fünf MouseEvents für alle UIElement:

  • MouseMove
  • MouseEnter
  • MouseLeave
  • MouseLeftButtonDown
  • MouseLeftButtonUp

Als erstes wollen wir uns aber anschauen, was wird benötigt, um ein MouseEvent überhaupt auslösen zu können.

Am wichtigsten ist neben der Maus natürlich, das der User mit dem Pointer innerhalb der sichtbaren Grenzen eines Objektes eine Aktion auslöst (zB drücken der linken Maustaste,…) und das Objekt muss auch das Event empfangen können. Das klingt zwar einfach, ist es aber nicht wirklich, denn auf dem Bildschirm können mehrere Objekte übereinander gelagert sein, was auch meist der Fall ist, den Klick empfängt dann nur das Objekt, das direkt unter der Zeigerspitze liegt. Welches Objekt das wirklich ist ist vom ZIndex abhängig.

Obwohl sichtbar auch nicht ganz der richtige Ausdruck ist, das Objekt muss an der Stelle vorhanden sein, hat das Objekt an der obersten Stelle die Opacity = “0″, so wird es nicht sichtbar sein. Enthält das Objekt aber die Eigenschaft Visibility=”False” so ist es nicht nur nicht sichtbar sondern, kann auch keine Events empfangen.

Aber ab und zu ist es auch notwendig, das ein Objekt zwar sichtbar sein soll, aber kein MouseEvent empfangen soll, deshalb gibt es auch die Möglichkeit die Empfangsbereitschaft eines Objektes auf Hinblick auf Mausereignisse zu unterbinden. Dafür dient der Parameter IsHitTestVisible.

  • ist die Eigenschaft auf true gesetzt, empfängt das Objekt sämtliche MouseEvents, wenn die Maus innerhalb der sichtbaren Grenzen des Objektes ist. RoutedEvents werden ebenso empfangen. Das ist auch der standardmäßig gesetzte Wert eines Objektes.
  • ist der Wert auf false, so empfängt das Objekt keine Events und empfängt auch keine routed MouseEvents.

Setzen wir nun, die Theorie wieder anhand unseres Beispiels in die Tat um. Als erstes löschen wir das StackPanel aus dem bisherigen Code und machen aus der Ellipse einen Kreis, indem wir Width und Height auf gleiche Größe setzen (z.B. 150).

Als nächstes werden wir noch einen zweiten Größeren Kreis in das Beispiel hinzufügen (Width=Height=250) und wir erhalten 2 unterschiedliche Kreise, falls wir den größeren Kreis als erstes definiert haben oder der ZIndex des kleineren Kreis höher ist als der vom größeren. Es wäre optisch ansprechender wenn der kleinere Kreis in der Mitte des großen Kreis ist, deshalb setzen wir bei Canvas.Top=”50″ und Canvas.Left=”50″ bei dem kleineren Kreis. Und somit liegen die beiden direkt übereinander, wobei der größere einen Kreis um den Kleinen bildet.

In den jeweiligen Event-Handler wird der Text von den letzten Beispiel übernommen und so erhält man wie im Bild angezeigt, welches Objekt gerade den Event-Handler ausgelöst hat bzw. an welchen es weitergeleitet wurde. Wenn man den kleineren Kreis anklickt, wird der Event-Handler des Kleinen ausgelöst, weil ja der Kleinere das größere Objekt überlagert. Klickt man den außeren Ring an, so erhält man die drei Eventhandler, des größeren Kreises.

Würde nun der Wert von IsHitVisibility des kleinen Kreis auf False gesetzt, so ändert sich das gesamte Verhalten, da der kleinere Kreis keine Events mehr empfängt sondern nur mehr der darunterliegende Größere Kreis. Das Event wird in Silverlight natürlich nur innerhalb der Fläche des Kreises ausgelöst, nicht in der beim Design sichtbaren BoundingBox der Ellipse.

Würden wir anstelle der Ellipseklein nun eine TextBlock definieren und für diesen einen Eventhandler implementieren, welche die gleiche Ausgabe liefert, wird aber die BoundingBox anstelle des sichtbaren Text verwendet, um Events zu empfangen, da es doch schwer sein könnte für den Anwender, einzelne Buchstaben zu treffen.

juergen79 Informatik, Internet, Microsoft, Programmiersprachen, RIA, Silverlight , , ,

Daily-Hour 17: Ereignisse und Interaktionen Teil 2 – Bubbling und Maustaste

4. Februar 2010

Wie nun das Bubbling funktioniert schaut man sich am Besten an einem Beispiel an. Um dies zu machen verwenden wir gleich unser Beispiel aus Teil 1 und ändern es ein wenig.

Im UserControl-Tag wird

MouseLeftButtonDown =”MouseDownUserControl”

hinzugefügt. Ebenso wird der MouseLeftButtonDown-Eventhandler beim Canvas, StackPanel und der Ellipse hinzugefügt.

Somit haben wir in jedem Objekt des Visuellen Baums einen MouseLeftButtonDown-Eventhandler deklariert. Im Web gibt es normalerweise nur die Unterstützung deshalb wurde der o.a. Eventhandler einfachhalber MouseDownUserControl genannt.

Im Code-Beside defineren wir als nächstes die Event-Handler, sodass jeder Handler einen eigenen TextBlock hinzufügt, deshalb muss davor noch eine Änderung im TextBlock durchführen und die bestehenden TextBlock durch folgenden ersetzen:

und in den erzeugt Event-Handler-Methoden fügt man zwischen die Klammern einfach

TxtDisplay.Text += “MouseDown in UserControl\n”;

hinzu und den Text passt man für die restlichen Event-Handler an. Wenn man das Projekt startet und auf die Ellipse klickt, werden die Eventhandler der Reihe nach aufgerufen und jede einzelne Behandlungsroutine trägt sich im TextBlock ein und hinterlässt Spuren. Hier sieht man dann in welcher Reihenfolge die Events ausgelöst wurden.

Jeder dieser Handler hängt seinen Text an das Ende des aktuellen Textinhaltes dazu. Das erste ausgelöste Event ist, wenn man auf die Ellipse klickt, die Ellipse selbst. Danach bubbelt der Event weiter zum StackPanel und löst auch dort das MouseLeftDown-Event aus und schließlich zum Canvas und zum UserControl.

Was passiert, wenn mehrere Objekte übereinander liegen? Mit einem Canvas ist es leicht zu bewerkstelligen, indem die Objekte alle an der gleichen Position dargestellt werden können und sich gegenseitig überdecken. Der Mausklick folgt an einer beliebigen Stelle im Canvas und trifft ein Objekt, welches unter dem Mauszeiger sichtbar ist. Dieses Objekt ist sichtbar, weil es den höchsten ZIndex hat oder aufgrund seiner Dimension oder Form nicht von anderen Objekten überlappt wird.

Dieses Objekt leitet das Event dann an seinen Parent weiter während die anderen Objekte des Canvas kein Event erhalten. Den Grund kann man im visuellen Baum gut erkennen. Die anderen Objekte des Canvas haben keine Verbindung zum Objekt selbst und sind somit keine Eltern des Objektes, dass das Event auslöst.

Löst ein Objekt nun einen Event-Handler aus, ist aufgrund des Bubbling-Mechanismus zuerst nicht klar, wodurch das Event ausgelöst wird. In unserem Beispiel empfängt das Canvas ein MouseLeftDown-Event, ohne dass das Canvas angeklickt wurde. Um prüfen zu können, welches Objekt der eigentliche Aulöser für das Event ist, werden beim Aufruf der entsprechenden Behandlungsmethode weitere Informationen mitgegeben.

Hier noch mal wie die Event-Handler-Methode im Normalfall aussieht:

private void MouseDownUserControl(object sender, MouseButtonEventArgs e)

Die Methode übergibt object sender und MouseButtonEventArgs e, ein Objekt, das Methoden zum Abfragen oder Setzen weiterer Informationen enthält. Der Parameter Sender teilt uns keine Informationen zum gewünschten Event mit, es handelt sich dabei um den Sender selbst, das ist das Objekt, welches die Ereignisbehandlungsroutine ausgelöst hat. D.h. Wird das Event durch die Ellipse ausgelöst, dann ist das Senderobjekt die Ellipse selbst, im Falle eines Canvas das Canvas selbst.

Aufschlussreich ist deshalb nur eine Eigenschaft, welche vom MouseButtonEventArgs-Objekt angeboten wird. Diese nennt sich OrginalSource und verweist auf da Objekt, welches ein weitergeleitetes Event ursprünglich ausgelöst hat, in dem Beispiel ist es die Ellipse.

Um nun den Namen des jeweiligen auslösenden Objektes zu erhalten und nicht eine Referenz auf die Objektinstanz müssen wir casten. Im folgenden Beispiel wird es zwar nur für das UserControl gezeigt, aber es ist für alle anderen Event-Handler identisch.

private void MouseDownUserControl(object sender, MouseButtonEventArgs e)
{
            FrameworkElement FE = (FrameworkElement)e.OriginalSource;
            TxtDisplay.Text += “MouseDown (von ” + FE.Name + “) in UserControl\n”;
}

Mit e.OriginalSource kann man auf diese Eigenschaft zurückgreifen. Um nun auf spezifische Eigenschaften des Objektes z.B. Name-Eigenschaft muss man es als FrameworkElement casten. Dieses Frameworkelement stellt die Namen-Eigenschaft zur Verfügung, welche man in die Textausgabe einbauen kann.

Auf diese Weise kann jeder Event-Handler auslesen, welches Objekt das Ereignis ausgelöst hat und kann entsprechend darauf reagieren. So ist es möglich verschiedene Behandlungen an zentraler Stelle im Code abzulegen und dort via switch-Anweisung oder if-Abfragen zu prüfen, wie reagiert werden soll.

Man kann aber auch das Bubbling in einer Behandlungsroutine unterbinden, da das Event schon behandelt wurde. Für das Stoppen des Event-Bubblings kann ebenfalls der MouseButtonEventArgs-Konstrukt verwendet werden. Die Methode e.Handled ist vom Typ Boolean und das Event-Bubbling geht weiter, solange dieser Wert auf false ist. Um die weitere Event-Behandlung zu unterbinden, setzt man diesen Wert einfach auf true und das Event wird nicht mehr weiter in der Objektkette nach oben gereicht.

e.Handled = true;

juergen79 Informatik, Internet, Microsoft, Programmiersprachen, RIA, Silverlight , ,

Daily-Hour 16: Ereignisse und Interaktionen Teil 1 – Routed Events

2. Februar 2010

Im Web und bei Benutzeroberflächen geht es immer um den Benutzer, deshalb müssen Oberflächen so gestaltet werden, des eine inutiteve Bdienung erfolgen kann.

Im Kern geht es um die Erzeugung von Objekten und deren Beziehungen untereinander. Diese Objekte können sich gegenseitig beeinflussen bzw. durch den Anwender beeinflusst werden. Eine Web- oder Desktopanwendung ist darauf aufgebaut, dass die Anwendung auf ein Ereignis (Event) wartet und auf dieses reagiert und weitere Ereignisse auslöst.

Entscheidend sind Ereignisse, welche  das variable Verhalten einer Applikation ausmachen. Einige Ereignisse werden von Objekten selbst ausgelöst und sind Reaktionen auf zuvor ausgelöste Ereignissen. Wird das Ereignis durch den User ausgelöst so spricht man von Interaktionen, z.B. dem Drücken einer Taste oder dem Bewegen der Maus.

Bei Silverlight und der Code-beside-Programmierung spielen Events deshalb eine entscheidende Rolle. in den nächsten Posts werden wir uns deshalb mit Events und Interaktionen beschäftigen.

Ein Event wird durch ein bestimmtes Objekt (Instanz) ausgelöst, dieses Objket ist der Sender, weil es ein Event sendet. Der Event-Handler, die Ereignisbehandlungsroutine, empfängt das Ereignis und bekommt 2 Argumente übermittelt. Der erste Parameter enthält das Objekt, das für dieses Event registiert wurde und der zweite Parameter weitere Informationen , die von der Art des Ereignis abhängt. Dazu aber später mehr.

Es gibt 2 Möglichkeiten, wie ein Objekt ein Event registrieren kann. Es wird entweder ein externes Event direkt durch das Objekt registriert (z.B. Mausklick, Mausbewegung, …). Es könnte sich hierbei auch um ein weitergeleitetes Event (routed Event) handeln, das durch ein anderes Objekt weitergeleitet wurde. Die andere Möglichkeit die es gibt, ist die Zustandsänderung eines Objektes, zB DownloadProgressChanged.

Routed Events
In Silverlight wird ein Objektbaum (Object Tree) verwendet, welcher angibt, welches Objekt an obersten Stelle steht und welches davon ein Kindobjekt ist. Es gibt eine klardefinierte Hierachie innerhalb der Objekte, welche durch den XAML-Code direkt entspricht bzw. durch den hierarchischen XAML-Code erst aufgebaut wird. Der XAML-Code spiegelt diese Baumstruktur deshalb wieder, da es eine XML-basierte Beschreibungssprache ist.

Die Analogie zwischen Objektbaum und XAMl ist nur näherungsweise zu sehen, denn in XAML gibt es auch Kindelemente, die eigenlich nur Attribute von Objekten definieren oder beschreiben, aber selbst keine eigenen Objekte im Sinne der Events sind, zB Füllungen und Umrandungen.

Der typische XAML-Code:
<UserControl x:Class=”MouseEvents.MainPage”
    xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
    xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
    xmlns:d=”http://schemas.microsoft.com/expression/blend/2008″ xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006″
    mc:Ignorable=”d” d:DesignWidth=”640″ d:DesignHeight=”480″>
    <Grid x:Name=”LayoutRoot”>
        <Canvas Background=”AntiqueWhite”>
            <StackPanel>
                <Ellipse Width=”150″ Height=”110″ x:Name=”Ellipse”>
                    <Ellipse.Fill>
                        <LinearGradientBrush StartPoint=”-0,5″ EndPoint=”1,5″>
                            <GradientStop Color=”CadetBlue” Offset=”0.3″></GradientStop>
                            <GradientStop Color=”DarkOliveGreen” Offset=”0.9″/>
                        </LinearGradientBrush>
                    </Ellipse.Fill>
                    <Ellipse.Stroke>
                        <SolidColorBrush Color=”BlanchedAlmond”>
                        </SolidColorBrush>
                    </Ellipse.Stroke>
                </Ellipse>
                <TextBlock Text=”Test”/>
                <TextBlock Foreground=”AliceBlue”>
                    <Run>Hier sieht man einen Rune</Run>
                    <LineBreak/>
                    <Run>und einen zweiten nach einem LineBreak</Run>
                </TextBlock>
            </StackPanel>
        </Canvas>
    </Grid>
</UserControl>

In Visual Studio wird die Baumstruktur durch Einrückungen dargestellt. Das es einen obersten Knoten -  Wurzel (Root) – gibt ist klar zu erkennen. Es ist im Falle von Silverlight das UserControl und ist eigentlich, dass was schließlich das PlugIn im Browser dargestellt wird.

Das UserControl besitzt neben diversen Parametern auch einen Content, deshalb wird es auch ContentControl genannt. Der Inhalt besteht aus einem bestimmten Objekten bestehen oder einem Bereichselement, welches der grafischen Anzeige dient. In einem Bereichselement können, wiederum ein oder mehrere Unterobjekte besitzen usw..

Inhalt des UserControls ist das Grid, welches in der Grafik einfachhalber weggelassen wurde, da es nur noch das Canvas-Element enthält.  Das Canvas beinhaltet zwei Objekte, eine Ellipse und einen TextBlock. Sowohl die Ellipse als auch der TextBlock haben, weitere Inhaltsobjekte, zB. Füllungen oder Runs, welche Eigenschaften der Objekte beschreiben, aber nicht mehr im Sinne der Ereignisweiterleitung als Objekt zählen.

Routed Events sind jene Ereignisse, die entlang dieser Baumstruktur von unten nach oben weitergeleitet werden, dabei leitet das Child-Objekt ein Ereignis an sein Elternobjekt weiter und dieses wieder an seinen Parent usw. bis das Ereignis schließlich wieder be im Wurzelelement landet, also dem UserControl. Die Weiterleitung erfolgt von unten nach oben, wie eine Blase die im Wasser aufsteigt, deshalb wird diese Art der Eventweiterleitung auch Bubbling genannt.

Der wirkliche Objektbaum ist umfangreicher als der der vorigen Abbildung, an der die Events weitergeleitet werden. Deshalb spricht man vom Visuellen Baum und dieser wird in der Regel von Objekten aufgebaut, die einem Renderporgramm unterliegen. Sammlungen (Collection) wie zB GradientStop zählen nicht dazu.

Eine Weiterleitung entlang eines visuellen Baums kann jedoch auf Wunsch unterbunden werden, sodass ein Ereignis an einer bestimmten Stelle eines Baumes abgefangen, verarbeitet und nicht weitergeleitet wird. In Silverlight löst ein Event entweder direkt einen Event-Handler aus oder wird nach oben weitergeleitet.

juergen79 Informatik, Internet, Microsoft, Programmiersprachen, RIA, Silverlight , ,

Daily-Hour 15: Entwicklung eines Spieles Teil9 – Collision & State Change

31. Januar 2010

Entschuldigt noch mal die verspätete Fortsetzung, aber durch technische Umstände wurde ich verhindert das Projekt fortzusetzen, und das kann ich jetzt habe ich mir eine andere Möglichkeit geschaffen, das Projekt fortzusetzen.

Kurz vor dem Schluss muss noch die Ball Kollision abgeprüft werden. Die Abfrage muss jedes mal durchgeführt werden, wenn der Ball aufgrund der Y-Position in Höhe der Spielsteine bewegt, ob vielleicht eine Kollision vorkommt.

Im ersten Schritt wird geprüft, aus welcher Richtung sich der Ball auf den Stein zubewegt.
// Ball moves down
if (_ball.Direction.Y < 0)
   YCollisionspointBall = Canvas.GetTop(_ball);
else
// Ball moves up
   YCollisionpointBall = Canvas.GetTop(_ball) + _ball.Height;

Entsprechendes gibt es auch für die Prüfung, ob der Ball für links und rechts kommt, sollte der Ball von rechts kommen, dann muss zusätzlich die Ball-breite hinzugezählt werden (_ball.Width). Sollte eine Kollision für einen Sichbaren Stein stattfindens, so stzt man seine Visbility-Eigenschaften, auf Collapsed gesetzt und die Punkte des Steines den Punkten hinzugezählt. Zusätzlich wird hier das Abprallen des Balles realisiert.

int xStonePosition = (int)Math.Round((XCollisionspointBall / 60) -.5);
if (xStonePosition < 0) xStonePosition = 0;
if (xStonePosition > 9) xStonePosition = 9;

int yStonePosition = (int)Math.Round((YColliosionpointBall -60)/20);
string stoneID = “Stone” + ((yStonePosition -1) * 10 + xStonePosition +1).ToString();

if (this.FindName(stoneID != Null)
   stone hitStone = (stone)(this.FindName(stoneID))
   if(hitStone.Visibility == Visibility.Visible)
   {
      hitStone.Visibility = Visibility.Collapsed;
      _Points += hitStone.Value;
      _ball.StoneCollision();
      _StoneNumber –;
      if (_StoneNumber == 0)
      {
         _Points += 650;
         StateChange(“GameRoundEndDisplay”);
      }
   }

Als nächstes benötigen wir noch die Methode InfoDisplay welche mit einem instanziierten Info-UserControl als Parameter aufgerufen.

private void InfoDisplay(UserControl uc)
{
   GameInfo.Children.Clear();
   GameInfo.Children.Add(uc);
   StoryboardInfoDisplay.Begin();
}

In der Methode wird das übergebene Child-Element bereinigt und schließlich das Canvas GameInfo angehängt. Durch das Storyboard  wird das Canvas schließlich eingeblendet.

Der MouseLeftButtonDown-Handler ist zuständig, dass bei drücken der linken Maustaste das Spiel gestartet bzw. fortgesetzt wird.

private void MouseLeftDown(object sender, MouseButtonEventArgs e)
{
   if (_GameState == “Intro” || _GameState == “GameEndWaitKeyPress”)
      StoryboardInfoDisappear.Begin();
}

Als nächstes wird das noch das Completed-Event des Storyboard behandelt.

private void InfoDisplayCompleted(object sender, EventArgs e)
{
   if (_GameState == “LooseBallDisplay”)
   {
      StateChange(“LooseBallWaitKeyPress”);
   }
    if (_GameState == “GameOverDisplay”)
   {
      StateChange(“GameEndWaitKeyPress”);
   }
  
if (_GameState == “GameRoundEndDisplay”)
   {
      StateChange(“GameRoundEndWaitKeyPress”);
   }

}

Dies wird gemacht, weil der User zum Zeitpunkt der Infoeinblendung mit Hoher Wahrscheinlichkeit noch eine Taste gedrückt hat. Das Storyboard würde sonst gestoppt werden und es gäbe dadurch keine Zustandsunterscheidungen. Deshalb wird nach dem ausgeführten Einblendevorgang (durch das Completed-Event ausgelöst) eine weitere Zustandsänderung durchgeführt. Der Vorteil darin liegt, das der Anwender genügend Zeit hat, die entsprechende Taste loszulassen.

Ähnlich funktioniert auch das Completed-Ereignis des Storyboard InfoDisappear.

Als letzter Schritt um das Spiel zu vervollständigen, benötigt man noch die Methode Zustandsänderung, dem ein String-Parameter übergeben wird und die Methode ist neben der GameLoop-Methode die wichtigste und auch umfangreichste, wir werden uns deshalb auch nur die wichtigsten Element daraus anschauen.

private void StateChange(string state)
{
   if (state == “GameLoad”)
   {
      _GameState = “Intro”;
      InfoDisplay(new Gamestart());
   }
   if (state == “GameStart”)
   {
      Layout.Root.Cursor = Coursers.None; // Mousepointer disappears
      SpielInitialisierung();
      RoundBuild();
      StartRound();
      _GameState = “GameLoaded”;
   }


    if (state == “GamEndDisplay”)
   {
       _GameState = “GameEndDisplay”;
      GameLoopTimer.Stop();
      GameEnd gameEnd = new Gameend();
      gameEnd.Points.Text = gameEnd.Points.Replace(“#”), _points.ToString());
      InfoDisplay(gameEnd);
      LayoutRoot.Cursor = Cursors.Arrow; // Display Mouspointer

   }
   …
   …
  
}

Die Reaktion welche in der Methode StateChange ausgelöst wird ist abhängig von dem übergebenen Parameter. Es wird mindestens eine Methode aufgerufen oder auch mehrere. Wichtig ist es, das einige Zustandsänderungen der GameloopTimer gestoppt bzw. gestartet werden muss.

Zu Beginn des Spiels wird der Mauszeiger ausgeblendet und am Ende der Mauszeiger wieder eingeblendet.

Im Prinzip ist Silvernoid jetzt lauffähig, aber es gibt sicher noch die eine oder andere Möglichkeit das Spiel zuverbessern bzw. zu erweitern.
}

juergen79 Games, Informatik, Internet, Programmiersprachen, RIA, Silverlight , ,

Daily-Hour: Pause

26. Januar 2010

Leider werde ich eine Pause mit der Daily-Hour-Reihe einlegen müssen, da ich bis auf weiteres auf mein MacBook verzichten muss, da dieses leider in Reparatur ist.

Aber  es werden alle ausgefallenen Stunden nachgeholt, sobald das MacBook wieder zurück ist.

juergen79 Allgemein

Silverlight Facebook Client

26. Januar 2010

Gestern ist mir schon Tim Heuers Blogeintrag “Silverlight Client for Facebook available” aufgefallen, aber irgendwie wollte bei mir der Client nicht laufen. Heute habe ich es endlich geschafft und muss sagen es sieht besser aus als das Original.

Silverlight Client

Download des Silverlight FB Client

Mehr dazu:

juergen79 Allgemein