Programmieren lernen Kapitel 7 Events

Tutorials zu verschiedenen Softwarenutzungen

Moderator: Moderatoren

Benutzeravatar
Jaegerfeld
Tribunus Angusticlavius
Tribunus Angusticlavius
Beiträge: 3571
Registriert: 10. November 2010 21:15
Wohnort: AudiCity
:
Pfeiler der Community Gewinner Userwahl

Programmieren lernen Kapitel 7 Events

Beitragvon Jaegerfeld » 19. Dezember 2010 15:33

Kapitel 7

Events


Nach dem letzten Kapitel haben wir zwar jetzt eine tolle GUI, leider macht sie aber rein gar nichts.

Wir wollen aber, dass bei einem Klick auf den 1 Button auch eine 1 im Anzeigefeld geschrieben wird.Irgendiwe müssen wir die beiden miteinander verknüpfen. Irgendjemand muss dem Anzeigefeld sagen: Schreib mal 'ne Eins.

Hier gibt es prinzipiell zwei Ansätze, Polling und Events.

Beim Polling fragen alle Komponenten zyklisch alle anderen Komponenten ob es etwas zu tun gibt.

Man kann sich das ungefähr so vorstellen: In einem recht großen Saal stehen hunderte von Menschen und ständig schreien alle: "Habt ihr was für mich?"

Anderes Beispiel: Der Postbote kommt nicht zu euch, sondern ihr lauft jeden Tag zur Post und fragt nach ob ein Brief da ist.

HOI2 macht das z.B. so ähnlich, das Eventsystem (PI hatte schon immer den Hang dazu Begrifflichkeiten zu verdrehen) basiert auf einer Art Polling.

Die Nachteile sind denke ich offensichtlich. Hunderte von Komponenten fragen völlig umsonst, da für sie keine Nachricht dabei war. Das ist eine unglaubliche Verschwendung von Ressourcen. Damit dürfte jetzt auch geklärt sein, woher bei HOI das laggen um 00:00 kommt.

Ein völlig andere Ansatz ist ein Programm das nach dem Event oder Observer Pattern designed wurde.Hier kennt eine Komponente alle, die sich für eine Nachricht von ihr interessieren könnten und benachrichtigt genau diese.

Um auf den vollen Saal zurückzukommen:

Hier sind alle still und wenn jemand einen Nachricht hat wirft er den Leuten aus seiner Abonenntenliste eine Mitteilung zu.

C# ist voll und ganz auf Events ausgelegt. Wenn wir also auf einen Button klicken wird ein Event ausgelöst und alle, die auf so ein Ereignis warten tun dann etwas. Sinnigerweise nennt sich das Event, das ausgelöst wird sobald wir auf einen Button klicken ButtonNameClick.

In anderen Programmiersprachen muss man jetzt umständlich Events und die dazugehörigen Handler (die Empfänger) schreiben und ne Menge Sachen beachten. In unserer Entwicklungsumgebung geht das aber sehr einfach. Doppelklickt einfach mal im Designer auf den Button 1.

Bild

Es öffnet sich automatisch die Form1.cs und wie durch Magie ist dort auf einmal einen neue Methode aufgetaucht.
Bild

Dort sagen wir dem Programm was geschehen soll, wenn jemand auf den Button1 klickt.

In unserem Fall soll der TextboxAnzeige eine 1 hinzugefügt werden,danach fügen wir einer Warteschlange etwas hinzu.

Wer sich jetzt fragt was das komische += bedeutet und vor allem was anstehen mit unserem Programm zu tun hat, dem werde geholfen.

Das mit dem += ist schnell erklärt, es ist einfach ein Abkürzung und bedeutet nichts anderes als : füge dem was links vom += steht das was rechts steht hinzu. Eine kurze Schreibweise von

Code: Alles auswählen

textBoxAnzeige.Text = textBoxAnzeige.Text + "1";


Das funktioniert auch mit -= . *= und /= gibt es auch noch, allerdings macht das hier keinen Sinn. Wie will man ein Wort durch die Ziffer 1 teilen?.

Die Warteschlange ist eine besondere Form der Collections, also so etwas wie ein Datenspeicher oder auch Container.

In sie kann man Daten stecken um sie später leichter wiederzufiinden. Ganz wie im richtigen Leben. C# kennt Listen; Stapel Reihungen, Dictionaries (die man sich als echte Lexika vorstellen kann, Stichwort und Inhalt) und eben Warteschlangen.

Die funktionieren nach genau diesem Prinzip, Daten kommen an und stellen sich in eine Reihe, wer zuerst da war wird auch als erster bedient. Das nennt sich dann FIFO, kurz für First In First Out. Alternativ gibt es den Stack oder Stapel, der genau anders herum funktioniert (First In Last Out).

Wir benötigen eine Queue (Warteschlange), da wir in unserem Anzeigefeld immer dann, wenn ein Zahlenbutton oder ein Operator gedrückt wurde auch hinten den jeweiligen Wert hinzufügen wollen. Für das alleinige Anzeigen ist das nicht notwendig, aber wir wollen später ja damit rechnen. Und es ist schwer, dem Rechner einfach eine Zeichenkette "2424 +556" hinzuwerfen und zu sagen: Rechne mal.

Viel einfacher ist es, wenn wir uns die Zahlen und Operatoren gesondert merken und die später logisch verknüpfen.

Um dies zu bewerkstellingen legen wir uns zwei Queues an, zahlen und operatoren genannt.

Da unsere gesamte Logik in der Form1.cs stehen soll, müssen wir dort auch die beiden Queues einführen. Dies machen wir gleich nach der öffnenden Klammer der Klasse, die Variablen gelten also in der gesamten Klasse.

Code: Alles auswählen

namespace Taschenrechner_GuiVersion
{
 public partial class Form1 : Form
 {
   Queue<string> operatoren;
   Queue<double> zahlen, zwischenergebnis;

im Konstruktor der Klasse initialisieren wir die beiden dann auch gleich.

Code: Alles auswählen

 public Form1()
        {
            InitializeComponent();
            zahlen = new Queue<double>();
            operatoren = new Queue<string>();
            zwischenergebnis = new Queue<double>();
        }

Warum sind das aber jetzt drei? Ganz einfach, ich habe mich dazu entschlossen mehr als eine Operation zu zulassen.

Dafür müssen wir uns das Zwischenergebnis merken. Ich verwende auch hierfür eine Queue. Theoretisch sollte es zwar immer nur ein Zwischenergebnis geben, aber es sind auch andere Szenarien denkbar. Außerdem ist unser Taschenrechner so klein, dass der zusätzliche Speicherbrauch nicht auffällt.

Anschließend klicken wir auf jeden Button und ergänzen den Code des Ereignisses.

Für die Zahlen sieht das so aus:

Code: Alles auswählen

private void button1_Click(object sender, EventArgs e)
 {
   textBoxAnzeige.Text += "1";
   zahlen.Enqueue(1);
 }

usw.

Die Operatoren analog:

Code: Alles auswählen

private void buttonPlus_Click(object sender, EventArgs e)
 {
   textBoxAnzeige.Text += " + ";
   operatoren.Enqueue("+");
 }


Um etwas zu berechnen können wir unsere Methoden aus dem Konsolenrechner bemühen. Ihr könnt die einfach so übernehmen.

Code: Alles auswählen

double addition(double a, double b)
 {
   return a + b;
 }
 double subtraktion(double a, double b)
 {
   return a - b;
 }
 double multiplikation(double a, double b)
 {
   return a * b;
 }
 double division(double a, double b)
 {
   return a / b;
 }


Einzige Änderung ist, dass wir das static nicht mehr benötigen da wir ja innerhalb eines erzeugten (von Programm.cs aus) Objektes arbeiten. Wenn ihr das static aber dort stehen lasst macht es auch nichts.

Bleiben noch die "Funktionsbutton" = , CLEAR und BACK.

Bei betätigen der = Taste soll das Ergebnis berechnet und anschließend im ANzeigefeld ausgegeben werden.

Wir gehen wie folgt vor:

Der User gibt über die Zahlen- und Operatorentasten eine Rechnung ein , wir merken uns die Ziffern und Operatoren in eine Queue und wenn auf = geklickt wird, holen wir unsdie ersten beiden Zahlen und rufen die Operation auf, die in der Schlange ganz vorne steht.

Anschließend speichern wir das ergebnis in das Zwischenergebnis und reihen dies in den Zahlen wieder VORNE ein.

Wer aufmerksam mitgedacht hat stellt,vielleicht ein Problem fest, das dürft ihr als Hausaufgabe lösen. Tipp: Einfach noch eine Queue oder etwas ähnliches nutzen. Wem jetzt nichts auffällt merkt es spätestens beim ausprobieren des Rechners.

Die Methode ist etwas komplizierter, wir unterscheiden wieder mit switch mehrere Fälle:

Code: Alles auswählen

private void buttonErgebnis_Click(object sender, EventArgs e)
 {
   foreach (string s in operatoren)
   {
     switch (s)
     {
       case"+":
       if (zwischenergebnis.Count != 0)
       {
         zwischenergebnis.Enqueue(addition(zwischenergebnis.Dequeue(), zahlen.Dequeue()));
       }
       else
       {
         zwischenergebnis.Enqueue(addition(zahlen.Dequeue(), zahlen.Dequeue()));
       }

       break;

       case "-":
       if (zwischenergebnis.Count != 0)
       {
         zwischenergebnis.Enqueue(subtraktion(zwischenergebnis.Dequeue(), zahlen.Dequeue()));
       }
       else
       {
         zwischenergebnis.Enqueue(subtraktion(zahlen.Dequeue(), zahlen.Dequeue()));
       }

       break;

       case "*":
       if (zwischenergebnis.Count != 0)
       {
         zwischenergebnis.Enqueue(multiplikation(zwischenergebnis.Dequeue(), zahlen.Dequeue()));
       }
       else
       {
         zwischenergebnis.Enqueue(multiplikation(zahlen.Dequeue(), zahlen.Dequeue()));
       }

       break;

       case "/":
       if (zwischenergebnis.Count != 0)
       {
       zwischenergebnis.Enqueue(division(zwischenergebnis.Dequeue(), zahlen.Dequeue()));
       }
       else
       {
       zwischenergebnis.Enqueue(division(zahlen.Dequeue(), zahlen.Dequeue()));
       }

       break;

       default:
       break;
     }
   }
 operatoren.Clear();
 textBoxAnzeige.Text = zwischenergebnis.First().ToString();
 }


Die Taste CLEAR soll den Speicher der Rechners löschen, das geht sehr einfach:

Code: Alles auswählen

private void buttonClear_Click(object sender, EventArgs e)
 {
   zahlen.Clear();
   operatoren.Clear();
   zwischenergebnis.Clear();
   textBoxAnzeige.Text = "";
 }


Die BACK Taste soll wie Backspace auf der Tastatur funktionieren, das ist aber schwerer als es scheint.
Wir müssen ja die letzte Stelle der Queue löschen. Leider geht das aber nicht so einfach. C# bietet uns keine passende Methode.
Wir machen das also selbst und schreiben einfach alle Einträge bis auf den letzten in eine neue Queue und nutzen anschließend diese.

Code: Alles auswählen

private void buttonBack_Click(object sender, EventArgs e)
 {
   textBoxAnzeige.Text = textBoxAnzeige.Text.Remove(textBoxAnzeige.Text.Count() - 1);
   Queue<double> merkerQueue = new Queue<double>();
   int count = 0;
   foreach(double d in zahlen)
   {
     if (count != zahlen.Count()-1)
     {
       merkerQueue.Enqueue(d);
     }
     count++;
   }
   zahlen = merkerQueue;
 }


Das war es auch schon, ab sofort ist unser kleiner Rechner einsatzbereit.

Wer Lust hat, kann sich noch um folgende Probleme kümmern:
  • Mehr Funktionen wären toll, Wurzeln z.B. oder Potenzen. (leicht)
  • Mehrstelligen Zahlen werden falsch addiert /subtrahiert etc. (mittelschwer)
  • Es gibt kein Punkt vor Strich (schwer)

Wir wollten uns aber nur ein wenig mit C# vertraut machen, im nächsten Kapitel stürzen wir uns auf XNA und schreiben unsere eigene Spieleengine.
Zuletzt geändert von Jaegerfeld am 19. Dezember 2010 17:36, insgesamt 5-mal geändert.
„Ich schätze mal, das kann jeder Online-Community passieren. Irgendwann stellen die höflichen und vernünftigen Leute fest, dass sie sich in dieser Gruppe nicht mehr aufhalten wollen. Also verschwinden die. Und diejenigen die übrig bleiben, erfahren nur noch die Leute die genau so wie sie drauf sind.“

=== David Gaider, Bioware ===

Benutzeravatar
Jaegerfeld
Tribunus Angusticlavius
Tribunus Angusticlavius
Beiträge: 3571
Registriert: 10. November 2010 21:15
Wohnort: AudiCity
:
Pfeiler der Community Gewinner Userwahl

Re: Programmieren lernen Kapitel 7 Events

Beitragvon Jaegerfeld » 19. Dezember 2010 17:17

Der erste zitierte Code war falsch, jetzt steht da richtig
Bild

Danke an pogotorte, der Fluch des Copy&Paste hatte mich mal wieder erreicht.
„Ich schätze mal, das kann jeder Online-Community passieren. Irgendwann stellen die höflichen und vernünftigen Leute fest, dass sie sich in dieser Gruppe nicht mehr aufhalten wollen. Also verschwinden die. Und diejenigen die übrig bleiben, erfahren nur noch die Leute die genau so wie sie drauf sind.“

=== David Gaider, Bioware ===