586.406 aktive Mitglieder*
2.880 Besucher online*
Kostenfrei registrieren
Einloggen Registrieren

Windows Programmierung in C# /Winforms, Einstieg in C# als praktische Anleitung zur Programmierung

Beitrag 12.04.2014, 21:05 Uhr
sharky2014
sharky2014
Level 7 = Community-Professor
*******
Gruppe: Mitglied
Mitglied seit: 25.09.2008
Beiträge: 1.692

Von den Höhen der Programmstrukturierung zu den Niederungen der Programmierung zu Fuß.

Der Adler, sagte Bismarck einmal, übersieht in seinem Fluge die Hindernisse am Boden.

Wir wollen Adler sein, müssen aber zu Fuß gehen.

Es gibt immer auch technische Probleme zu lösen bei dem Dialog mit dem Anwender.

Grundsätzlich sollte man es vermeiden, daß der Anwender völlig freihändig irgendwelchen Unsinn eingeben darf. Weil diese EIngabe dann umständlich auf Fehlerhaftigkeit geprüft werden muß.

Ich persönlich liebe das auch gar nicht, von der Maus auf die Tastatur zu wechseln. Wenn man mit der Maus einmal dran ist, will man auch alle Eingaben mit der Maus machen.

Manchmal geht es nicht. Man braucht dann mit der Tastatur frei einzugebende Felder.

Die erwarten irgendwas, z. B. eine numerische Eingabe, und es muß eine Fehlerbehandlung erfolgen.

In Bezug auf die Anzahl der zu bestellenden Artikel hab ich die freie Eingabe rausgenommen (textBox) zugunsten einer comboBox, die bestimmte Stückzahlen zur Verfügung stellt, z. B. 1,2,3,4,5,10,15,20,50,100,200,500.

Der Anwender kann dann eine von den Optionen wählen, und wir wissen, wir haben kein Formatierungsproblem mit der Umwandlung des Stringwertes in ein Integer, weil die Auswahl ja vorgegeben wurde.

Leider ist es bei der comboBox so, daß der Anwender nicht nur klicken kann, sondern auch mit der Tastatur reinschreiben darf. Ziemlich unsinnig, ist aber so, insbesondere ist das unsinnig, weil dann ein Laufzeitfehler die sichere Folge ist. Nämlich eine außerhalb des Indexes oder null Exception. Das ist ja die alte Frage bei den Eingabefunktionen in Ansi-C, wie man einen buffer-Overflow erzeugt, die Scanf() Funktion ist da so ein Kandidat.

Um die comboBox an einem Laufzeitfehler zu hindern, wäre die Eigenschaft readonly=true wünschenswert, die comboBox hat die aber nicht.

Um readonly zu simulieren, gibt es allerdings eine Möglichkeit. Nämlich, bei der comboBox müssen wir die Eigenschaft dropdownstyle auf dropdownlist setzen, und haben das gewünschte Ergebnis.

Eine andere technische Frage ist, wie man eine frei einzugebende Textbox abfragt.

private void textBox1_TextChanged(object sender, EventArgs e)
{

}

ist dafür ungeeignet, weil der Ereignishandler nicht abwartet, bis die Eingabe beendet ist, sondern schon beim ersten Zeichen in Aktion tritt.

Wir könnten uns entscheiden, das Ereignis "ENTER" zu wählen, daß der Anwender also die Eingabe mit ENTER abschließen muß.

Sobald ENTER gedrückt wurde, meldet die textBox, daß ENTER gedrückt wurde, und das Ereignis kann verarbeitet werden.

Leider ist es so, daß bei ENTER ein häßlicher Piepton ausgeworfen wird.

Wie kriegt man den weg?

Das hier ist aus dem Internet unter dem witzigen Begriff "How to Stop the 'Ding" when pressing Enter".

Antwort auf das DING, was ja im Deutschen andere Bedeutung hat, ist z. B. die hier:

private void keypressed(Object o, KeyPressEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
e.Handled = true; //this line will do the trick
}
}



That will do the trick, aber nicht so, sondern etwas anders, sonst wäre es ja auch zu einfach:

private void tbPreisnachlass_KeyPress(object sender, KeyPressEventArgs e)
{

if (e.KeyChar == CBas.ENTER)
{
e.Handled = true;
pruefeEingabe(this.tbPreisnachlass);
}
}


Wobei in der statischen Klasse CBas der Wert für ENTER mit 13 hinterlegt wurde.

Sobald der Anwender also sein freies Textfeld, was eine Zahl erwartet, mit ENTER abschließt, kann die zentrale Methode diese Eingabe überprüfen.

Wir erwarten z. B. einen double (für int müßte man das ähnlich behandeln).

Dafür brauchen wir eine Funktion, die den String, ohne einen Laufzeitfehler auszulösen, überprüft und im Fehlerfall eine default-Wert zurückliefert, z. B. wenn der Anwender asdf eingegeben hat anstelle 19.23 oder 19,23, den Wert 0.0;

Wobei wir dann noch das Komma/Punkt Problem in einem Aufwasch behandeln.

Eine solche Methode findet sich in CFormat (in diesem Demo-Programm)

public static double string_to_double(string str)
{
double result=0.0;
string strkopie="";
if (str == null) return 0.0;
if (str.Length == 0) return 0.0;
try
{
foreach (char element in str)
if (element == CBas.PUNKT) strkopie += CBas.KOMMA;
else strkopie += element;
result = Convert.ToDouble(strkopie);
}
catch
{
result = 0.0;
}
return result;
}


Strings sind da sehr zickig.

Wir prüfen erstmal auf null.

Würden wir erstmal auf str.Length() prüfen, und str wäre null, hätten wir schon einen Laufzeitfehler. Reihenfolge also nicht zufällig.

Wenn str.Length==0 so wäre, würde allerdings foreach() die Sache abfangen, nämlich gar nicht in Betrieb gehen. Das hier ist mit Gürtel und Hosenträger.

Die Methode leistet also, daß der String ohne Laufzeitfehler in einen double-Wert überführt wird, und gestattet Punkt und Komma.

Das ist wichtig, solche Methoden zu haben, weil man sonst an jeder Stelle des Codes diese Codierung einfügen müßte, Stichwort Redundanz und Fehlercode.

Der Beitrag wurde von sharky2014 bearbeitet: 12.04.2014, 21:17 Uhr


--------------------
A programmer is just a tool which converts caffeine into code
TOP    
Beitrag 12.04.2014, 21:46 Uhr
sharky2014
sharky2014
Level 7 = Community-Professor
*******
Gruppe: Mitglied
Mitglied seit: 25.09.2008
Beiträge: 1.692

Das kann ich mir jetzt nicht verkneifen, wird ja in der obigen Funktion genutzt, daß die Probleme bei der Behandlung des Datentyps string in C# exzellent gelöst sind.

Man hat viel Gedanken verwendet auf die Strings. Z. B. der Operator + ist einfach genial. Die Strings schweben so oberhalb der genauen Typisierung, z. B. muß man sie ja auch nicht mit new() anfordern. Strings in C# sind chimärische Wesen. Es kommt dazu, daß die AnsiC Strings char[zahl] in C# eine zulässige Untermenge sind. Das muß man sich das aber nicht geben, von der direkten Indizierung wegzukommen löst keine Wehmutsgefühle aus, sondern Erleichterung.

Schon aufgefallen, daß foreach() auch einen String als Auflistung betrachtet und somit verwendet werden kann?

Während aber in Ansi-C die Zuweisung str[5]='x' zulässig ist (direkte Indexierung, siehe ganz weit oben, problematisch, bin selbst kein Freund davon)

wirft der C# Compiler die Fehlermeldung aus:

Fehler 1 Einer Eigenschaft oder einem Indexer "string.this[int]" kann nichts zugewiesen werden -- sie sind schreibgeschützt.


Ja, in C# sind Strings schreibgeschützt. Das macht Sinn, nichts dagegen zu sagen, aber wie wollen wir z.B. einen String "reparieren", in dem ein Punkt vorkommt, und diesen Punkt durch ein Komma ersetzen?

Die Lösung ist die Methode string.Add. Die Klasse String hat diese Methode verfügbar.

Daher erzeugen wir eine Kopie strkopie="" und setzen die Zeichen zeichenweise dazu, zeichen=char, machen also eine Kopie char für char und können dann die Bedingung einfügen, z. B. daß wir bestimmte Zeichen nicht kopieren oder durch andere ersetzen wollen.

Warum die Zeichen nicht ersetzbar sind, innerhalb eines Strings, liegt daran, daß C# bei Änderungen an den Strings den gesamten String an eine andere Adresse kopiert.

Geschuldet dem Anspruch, daß ein String in der Länge beliebig sein kann.

Alles in allem sehr gute Lösung des ewigen Problems.

Der Beitrag wurde von sharky2014 bearbeitet: 12.04.2014, 21:56 Uhr


--------------------
A programmer is just a tool which converts caffeine into code
TOP    
Beitrag 13.04.2014, 18:06 Uhr
sharky2014
sharky2014
Level 7 = Community-Professor
*******
Gruppe: Mitglied
Mitglied seit: 25.09.2008
Beiträge: 1.692

Wie versprochen:

Wer hier ca. 20 Beiträge "aushält", kann unter Windows.Forms in C# programmieren.

Es wurde alles vorgestellt, was man für eine gängige Anwenderprogrammierung dieser Art erforderlich ist. Der Umgang mit den Datentypen, den Dateien und einigen benötigten Elementen der Windows Forms.

Was das Programm kann, sieht man in den Abbildungen.

F001Start ...

Die Rechnungsmaske wird aufgerufen.

WYSIWIG -> What you see is what you get. Wenn man den Radiobutten MWST 19 drückt, schaltet die Anzeige sofort auf 19.0 MWSt um.

Nimmt man aus der Kombobox Stückzahl den Wert 30, ist es auch so. Dasselbe gilt für die Konditionen (Rabatt).

F002Kunde ...

Den Kunden wählen wir durch Doppelklick auf ein Element der Kundenliste. Um zu zeigen, daß die Auswahl gültig ist, erscheint der Kunde in der Anzeige der Textfelder.

F003ErsterArtikel ...

Der erste Artikel ist ausgewählt durch Doppelklick auf das gewünschte Element der Artikelliste. Drückt man den Button hinzufügen, erscheint er in der dritten Listbox unten rechts, der Einkaufsliste.

F004MehrereArtikel

Hier sind mehrere Artikel ausgewählt, die Einkaufsliste füllt sich allmählich. Und es wurde eine Kondition ausgewählt.

Ergebnis:

Die Rechnung kann minutenschnell mit wenigen Mausklicks erzeugt werden.

Über Details der Programmierung anschließend, ich kriege gerade Besuch.
Angehängte Datei(en)
Angehängte Datei  F001Start.jpg ( 179.95KB ) Anzahl der Downloads: 8
Angehängte Datei  F002Kundenauswahl.jpg ( 185.96KB ) Anzahl der Downloads: 6
Angehängte Datei  F003ErsterArtikel.jpg ( 196.15KB ) Anzahl der Downloads: 4
Angehängte Datei  F004MehrereArtikelundKonditionen.jpg ( 199.83KB ) Anzahl der Downloads: 6
 


--------------------
A programmer is just a tool which converts caffeine into code
TOP    
Beitrag 13.04.2014, 20:29 Uhr
sharky2014
sharky2014
Level 7 = Community-Professor
*******
Gruppe: Mitglied
Mitglied seit: 25.09.2008
Beiträge: 1.692

Die Programmierung des Formulars Form2 (das Rechnungsformular):

public partial class Form2 : Form
{

CKunden hkunden = new CKunden(); // neue Instanz Kunden
CDateifunktionen hdatei = new CDateifunktionen(); // es folgen die Instanzhandler h.... für die benötigten Klassen
CSort hsort = new CSort();
CStart hstart = new CStart();
CTeile hteile = new CTeile();
List<CKunden.Kunde> Kundenliste; //Deklaration, aber keine Initialisierung der folgenden Bezeichner
List<CTeile.Teil> Teileliste;
CRechnungen.Rechnung Rechnung;
CRechnungen.Posten ArtBuffer;
public bool SPERRE = true;


Dann folgt der Konstruktor:

public Form2()
{
InitializeComponent();
neueRechnung();
Init();

}


neueRechnung() sorgt dafür, daß die benötigten Klassen instanziiert sind, bevor es losgeht:

public void neueRechnung()
{
Rechnung = new CRechnungen.Rechnung(); // Rechnungsformular
Rechnung.Einkaufsliste = new List<CRechnungen.Posten>(); // Einkaufliste
ArtBuffer = new CRechnungen.Posten(); // 1 Artikel für die Maske
Rechnung.datum = DateTime.Now;
this.comboBoxStueckzahl.SelectedIndex = 0;
this.listBoxEinkaufsliste.Items.Clear();
aktualisiereMaske();
}


Würde das nicht geschehen, hätten wir Laufzeitfehler wg. NullException.

Es folgt dann die einzige Methode, welche das gesamte Formular verwaltet.

// Die Generalmethode:

private void pruefeEingabe(object sender)
{

//Generalisierte Anweisungen unabhängig von der Datenquelle:
double dpruef = 0.0;
if (this.rbMWSt19.Checked)Rechnung.mwstproz=19.0;
else if (this.rbMWSt7.Checked)Rechnung.mwstproz=7.0;
else Rechnung.mwstproz=0.0;
ArtBuffer.anzahl = Convert.ToInt32(comboBoxStueckzahl.SelectedItem.ToString());
aktualisiereMaske();
// Dann spezifiziert nach sender
if (sender == this.dateTimePicker1)
{
Rechnung.datum = dateTimePicker1.Value;
this.tbDatum.Text = Rechnung.datum.ToShortDateString();
aktualisiereMaske();

}

if (sender == this.btNeueRechnung)
{
neueRechnung();
aktualisiereMaske();

}
....


SIe erhält also von allen Ereignishandlern Meldungen, wenn die geklickt wurden, und entscheidet daraufhin, was zu geschehen hat.

Hier sind alle Ereignishandler des Formulars aufgeführt:

// Die Ereignishandler:

private void dateTimePicker1_ValueChanged(object sender, EventArgs e) { pruefeEingabe(this.dateTimePicker1); }
private void btNeueRechnung_Click(object sender, EventArgs e) { pruefeEingabe(this.btNeueRechnung); }
private void btArtikelHinzufuegen_Click(object sender, EventArgs e) { pruefeEingabe(this.btArtikelHinzufuegen); }
private void btArtikelLoeschen_Click(object sender, EventArgs e) { pruefeEingabe(this.btArtikelLoeschen); }
private void btRechnungPruefen_Click(object sender, EventArgs e) { pruefeEingabe(this.btRechnungPruefen); }
private void btRechnungSpeichern_Click(object sender, EventArgs e) { pruefeEingabe(this.btEnde); }
private void btEnde_Click(object sender, EventArgs e) { pruefeEingabe(this.btEnde); }
private void cbKonditionen_SelectedIndexChanged(object sender, EventArgs e){pruefeEingabe(this.cbKonditionen);}
private void listBoxTeile_DoubleClick(object sender, EventArgs e) { pruefeEingabe(this.listBoxTeile); }
private void listBoxKunden_DoubleClick(object sender, EventArgs e) { pruefeEingabe(this.listBoxKunden); }
private void rbTeilNumSort_CheckedChanged(object sender, EventArgs e) { pruefeEingabe(this.rbTeilNumSort); }
private void rbTeilNameSort_CheckedChanged(object sender, EventArgs e) { pruefeEingabe(this.rbTeilNameSort); }
private void rbMWSt19_CheckedChanged(object sender, EventArgs e){pruefeEingabe(this.rbMWSt19); }
private void rbMWST7_CheckedChanged(object sender, EventArgs e){pruefeEingabe(this.rbMWSt7); }
private void rbDiffSteuer_CheckedChanged(object sender, EventArgs e){pruefeEingabe(this.rbDiffSteuer); }
private void rbMWSt0_CheckedChanged(object sender, EventArgs e){pruefeEingabe(this.rbMWSt0); }
private void comboBoxStueckzahl_SelectedIndexChanged(object sender, EventArgs e){ pruefeEingabe(this.comboBoxStueckzahl); }
[size="1"][/size]

Innerhalb der Ereignishandler befindet sich keine Programmlogik. ABSOLUT KEINE.

Die machen nur eines, nämlich melden, daß sie geklickt wurden. Da sie alle nur die Generalmethode aufrufen, kann man das wie zu sehen sehr kurz formatieren, es gibt da nichts zu sehen.

Wir müssen, um Daten in WinFOrms auszugeben, ein Stringformat wählen. Warum eigentlich? Wieso kann eine Textbox nicht auch einen Double ausgeben?

Die Frage ist gut, oder?

So ist es aber nicht, die lästige Typumwandlung bleibt beim Programmierer.

Tja, und das war es eigentlich auch schon.

Das Programm ist noch nicht fertig bis in den letzten Winkel, aber die Kernfunktionalität ist gegeben.

Sozusagen: Der Dachstuhl steht, die Zimmerleute sind abgerückt.

Jeder, der bis hierher durchgehalten hat, sollte nun in der Lage sein, in C# solche oder ähnliche Anwendungen zu programmieren.

Der Beitrag wurde von sharky2014 bearbeitet: 13.04.2014, 20:39 Uhr


--------------------
A programmer is just a tool which converts caffeine into code
TOP    
Beitrag 13.04.2014, 21:54 Uhr
sharky2014
sharky2014
Level 7 = Community-Professor
*******
Gruppe: Mitglied
Mitglied seit: 25.09.2008
Beiträge: 1.692

Ich muß zugeben, ich hab bis jetzt Probleme mit der "Denke" beim Umstieg von der prozeduralen Programmierung auf die Click-Event-Welt der Windows-Programmierung.

Bei der prozeduralen Programmierung läuft das Programm in einer Schleife:

while (!Ende)
{
berechne_Daten();
zeige_Daten();
if (irgendwas ) ... mache irgendwas()
if(sonstwas) ... mache sonstwas()
}

Der Erfolg ist, daß der Bildschirm immer auf dem neuesten Stand aller Eingaben ist, weil er in einer Schleife läuft. Am Anfang der Schleife werden alle Konditionen neu berechnet und ausgegeben. Darum muß man sich nicht kümmern.

Der Unterschied zur Windows-Umgebung: dort gibt es keine Schleife. Wenn wir sagen:

void pruefeEIngabe()
{
berechneDaten();
zeigeDaten()
if (sender==irgendein Button){tuwas}
if(sender== irgendeinanderer Button) {tuwasanderes}
}

Dann geschieht etwas anderes. Der Rückgabewert durchläuft den Kopf der Funktion, ohne daß eine Änderung der Ausgabe die Folge wäre, weil die neuen Daten ja erst innerhalb der Funktion (Methode) ausgewertet werden, und nachdem das der Fall ist, NICHT ZUM SCHLEIFENANFANG ZURÜCKSPRINGEN, denn wir haben keine Schleife. Ist die If-Bedingung erreicht, steht die Welt der Windows-Clicks. Nichts tut sich mehr.

Dadurch sehen wir die geänderten Daten auf dem Bildschirm nicht. Was wir sehen, ist der Zustand vor dem letzten Click-Ereignis. Mit jedem anderen Klick auf irgendein Element würden wir das sehen, aber dann würden wir wiederum die Änderungen nicht sehen, die von dem anderen Ereignis ausgehen.

SO SIND WIR IMMER EINEN CLICK HINTER DER REALITÄT ZURÜCK.

Um das zu kriegen, müssen wir nach jeder Behandlung irgendeiner Bedingung IMMER die Akutalisierungsfunktion expliizit aufrufen:

void pruefeEIngabe()
{
berechneDaten(); // Am Funktionskopf bringt das nichts, weil die neuen Daten noch nicht ausgewertet sind
zeigeDaten()

if (sender==irgendein Button)
{
tuwas();
berechneDaten(); // jetzt bringt es was
zeigeDaten();
}
if (sender==irgendeinanderer Button)
{
tuwasanderes();
berechneDaten(); // bei jeder einzelnen Bedingung
zeigeDaten();
}
}

Dann geht das.

Wie gesagt, man muß die Denke schon etwas umstellen.


--------------------
A programmer is just a tool which converts caffeine into code
TOP    
Beitrag 16.04.2014, 18:47 Uhr
sharky2014
sharky2014
Level 7 = Community-Professor
*******
Gruppe: Mitglied
Mitglied seit: 25.09.2008
Beiträge: 1.692

Der Betrieb mit einem Timer

Man ist immer versucht, auszuklammern. Redundanz, die endlose Wiederholung desselben Codes, ist fehleranfällig, wenn was geändert werden muß. Man übersieht eine Kopie dieses Codes, und das Programm wird instabil.

In dem Demo-Programm leitet jeder Event-Handler zu der Generalfunktion pruefeAnzeige(), aber wie schon beschrieben, weder am Anfang noch am Ende der Funktion wird die Anzeige des Bildschirms aktualisiert.

Also muß in jeder if-Bedingung die Funktion aktualisiereMaske() aufgerufen werden.

Wie kann man das ausklammern? Ausklammern heißt, die Methode wird nur einmal aufgerufen, am Anfang der Ausklammerung.

Mit einem Timer in einem Office-Programm gar nicht so übel.

Wir löschen alle Anweisungen in den if-Bedingungen, die Maske zu aktualisieren. Damit wird die gar nicht mehr aktualisiert. Der Methodenaufruf ist gelöscht.

Und übergeben die Funktion einem Timer. Ein Timer ist ein im Hintergrund der Programmierung laufender Prozeß, welcher bei Erreichen des Intervalls eine oder mehrere Methoden ausführt. Z.B., alle 5 Sekunden die Meldung bringt, "Hallo, es sind 5 Sekunden vorbei"). Das geht natürlich nur mit einer Multitasking Umgebung wie Windows, prozedural würde das nicht gehen.

Um den Timer zu implementieren, benötigt man eine neue Instanz der Klasse Timer, zu sehen in der letzten Zeile:

namespace DemoLagerverwaltung
{
public partial class Form2 : Form
{

CKunden hkunden = new CKunden();
CDateifunktionen hdatei = new CDateifunktionen();
CSort hsort = new CSort();
CStart hstart = new CStart();
CTeile hteile = new CTeile();
List<CKunden.Kunde> Kundenliste;
List<CTeile.Teil> Teileliste;
CRechnungen.Rechnung Rechnung;
CRechnungen.Posten ArtBuffer;
public bool SPERRE = true;
Timer tanzeige = new Timer();


Damit ist der Timer tanzeige deklariert, aber nicht instanziiert, und wann der wie und wo was machen soll, ist auch noch nicht klar. Dazu braucht es eine Funktion (Methode):

public void startTimer()
{
tanzeige.Interval=500;
tanzeige.Tick+=new EventHandler(aktualisiereMaske);
tanzeige.Start();
}


mit den folgenden Eigenschaften:

Interval=500 heißt, 500 ms = 1 halbe Sekunde.
tanzeige.Tick+= ist die Auflistung der Methoden, die alle 500ms aufgerufen werden sollen. Es ist eine unendliche Liste von += möglich.
In der letzten Zeile wird der Timer gestartet, er ist jetzt aktiv.
Allerdings erst dann, wenn die Methode starteTimer() aufgerufen wird.
Der Aufruf könnte z. B. im Konstruktur der Form2 erfolgen:

public Form2()
{
InitializeComponent();
neueRechnung();
Init();
startTimer();
}


Ab diesem Augenblick ist der Timer "scharf", bzw. aktiv.

Wir sind aus der Windows-Welt der Klick-Ereignisse ausgebrochen, wir haben im Hintergrund eine Zeitschleife laufen, die alle 500ms irgendetwas tut, ohne daß der Anwender irgendetwas machen muß. Das Gesetz des Programmstillstandes ohne CLick ist damit durchbrochen.

Aber was genau macht nun der Timer?

Er macht, was in dieser Zeile steht:

tanzeige.Tick+=new EventHandler(aktualisiereMaske);

Das heißt, er ruft immer wieder diese Methode auf, die so aussieht:

private void aktualisiereMaske(object sender, EventArgs e)
{
ErmittleRechnungsbetrag();
loescheMaske();
zeigeMaske();
}


Damit haben wir den Aufruf der Methode aktualisiereMaske(), die bisher an dutzenden Stellen aufgerufen werden mußte, erfolgreich ausgeklammert.

Zur Verdeutlichung der Timer-Funktion:

Mit diesem kleinen Progrämmchen kann man z. B. die Produktion von Panzern und Flugzeugen unabhängig voneinander ein- und ausschalten. Panzer und Flugzeuge haben unterschiedliche Produktionszeiten und Kosten. Das Ein- und Ausschalten der Produktion erfolgt mit den Radio-Buttons Produktion starten/Stop.

Beide Prozesse sind völlig unabhängig voneinander, nur das Guthaben, der Geldbetrag, der am Anfang zur Verfügung steht, leidet natürlich. Die Panzerproduktion findet sich in der Form2, um die zu sehen muß noch eine Form1 instanziiert werden, welche die Form2 aufruft:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace TestWinForms
{
public partial class Form2 : Form
{
public int Panzer = 0;
public int Flugzeuge = 0;
public double Guthaben = 100000;
public Timer tp =new Timer();
public Timer tf = new Timer();

public Form2()
{
InitializeComponent();
anzeige();
}
public void anzeige()
{
this.tbGuthaben.Text = Guthaben.ToString();
this.tbPanzer.Text = Panzer.ToString();
this.tbFlugzeuge.Text = Flugzeuge.ToString();

}
public void startTimerPanzer()
{
tp.Interval = 2000;
tp.Tick += new EventHandler(tp_Tick);
tp.Start();
}

public void startTimerFlugzeuge()
{
tf.Interval = 4000;
tf.Tick += new EventHandler(tf_Tick);
tf.Start();
}

void tp_Tick(object sender, EventArgs e)
{
double preis =5000;
if (Guthaben>preis )
{
Panzer ++;
Guthaben-=preis;
}
anzeige();
}
void tf_Tick(object sender, EventArgs e)
{
double preis =2800;
if (Guthaben>preis )
{
Flugzeuge ++;
Guthaben-=preis;
}
anzeige();
}

private void btEnde_Click(object sender, EventArgs e)
{
this.Hide();
return;
}

private void rbPanzerProduktionStop_CheckedChanged(object sender, EventArgs e)
{
this.tp.Stop();
}

private void rbFlugzeugProduktionStop_CheckedChanged(object sender, EventArgs e)
{
this.tf.Stop();
}

private void rbPanzerproduktionStart_CheckedChanged(object sender, EventArgs e)
{
startTimerPanzer();
}

private void rbFlugzeugProdutionStart_CheckedChanged(object sender, EventArgs e)
{
startTimerFlugzeuge();
}



}//form2
}// ns





Um zu sehen, welche Bedienelemente erforderlich sind, um das Programm auszuführen, ein Blick in den Designer der Form2 (der Panzer und Flugzeuge). Hier sind alle Elemente Radio Buttons, Textfelder usf. aufgeführt, incl. der Events und statischen Eigenschaften. Den Designer

Form2.Designer.cs nimmt man bei der Programmierung eigentlich nicht wahr, aber er entsteht parallel zum Design des Formulars und ist oft viel länger als die Funktionalitäten:

namespace TestWinForms
{
partial class Form2
{
/// <summary>
/// Erforderliche Designervariable.
/// </summary>
private System.ComponentModel.IContainer components = null;

/// <summary>
/// Verwendete Ressourcen bereinigen.
/// </summary>
/// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

#region Vom Windows Form-Designer generierter Code

/// <summary>
/// Erforderliche Methode für die Designerunterstützung.
/// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
/// </summary>
private void InitializeComponent()
{
this.btEnde = new System.Windows.Forms.Button();
this.rbPanzerproduktionStart = new System.Windows.Forms.RadioButton();
this.rbPanzerProduktionStop = new System.Windows.Forms.RadioButton();
this.rbFlugzeugProdutionStart = new System.Windows.Forms.RadioButton();
this.rbFlugzeugProduktionStop = new System.Windows.Forms.RadioButton();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.tbPanzer = new System.Windows.Forms.TextBox();
this.tbFlugzeuge = new System.Windows.Forms.TextBox();
this.tbGuthaben = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.groupBox1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.SuspendLayout();
//
// btEnde
//
this.btEnde.Location = new System.Drawing.Point(586, 354);
this.btEnde.Name = "btEnde";
this.btEnde.Size = new System.Drawing.Size(171, 65);
this.btEnde.TabIndex = 3;
this.btEnde.TabStop = false;
this.btEnde.Text = "Beenden";
this.btEnde.UseVisualStyleBackColor = true;
this.btEnde.Click += new System.EventHandler(this.btEnde_Click);
//
// rbPanzerproduktionStart
//
this.rbPanzerproduktionStart.AutoSize = true;
this.rbPanzerproduktionStart.Location = new System.Drawing.Point(23, 25);
this.rbPanzerproduktionStart.Name = "rbPanzerproduktionStart";
this.rbPanzerproduktionStart.Size = new System.Drawing.Size(198, 24);
this.rbPanzerproduktionStart.TabIndex = 6;
this.rbPanzerproduktionStart.Text = "Panzerproduktion Start";
this.rbPanzerproduktionStart.UseVisualStyleBackColor = true;
this.rbPanzerproduktionStart.CheckedChanged += new System.EventHandler(this.rbPanzerproduktionStart_CheckedChanged);
//
// rbPanzerProduktionStop
//
this.rbPanzerProduktionStop.AutoSize = true;
this.rbPanzerProduktionStop.Checked = true;
this.rbPanzerProduktionStop.Location = new System.Drawing.Point(24, 55);
this.rbPanzerProduktionStop.Name = "rbPanzerProduktionStop";
this.rbPanzerProduktionStop.Size = new System.Drawing.Size(197, 24);
this.rbPanzerProduktionStop.TabIndex = 7;
this.rbPanzerProduktionStop.TabStop = true;
this.rbPanzerProduktionStop.Text = "Panzerproduktion Stop";
this.rbPanzerProduktionStop.UseVisualStyleBackColor = true;
this.rbPanzerProduktionStop.CheckedChanged += new System.EventHandler(this.rbPanzerProduktionStop_CheckedChanged);
//
// rbFlugzeugProdutionStart
//
this.rbFlugzeugProdutionStart.AutoSize = true;
this.rbFlugzeugProdutionStart.Location = new System.Drawing.Point(25, 25);
this.rbFlugzeugProdutionStart.Name = "rbFlugzeugProdutionStart";
this.rbFlugzeugProdutionStart.Size = new System.Drawing.Size(214, 24);
this.rbFlugzeugProdutionStart.TabIndex = 8;
this.rbFlugzeugProdutionStart.Text = "Flugzeugproduktion Start";
this.rbFlugzeugProdutionStart.UseVisualStyleBackColor = true;
this.rbFlugzeugProdutionStart.CheckedChanged += new System.EventHandler(this.rbFlugzeugProdutionStart_CheckedChanged);
//
// rbFlugzeugProduktionStop
//
this.rbFlugzeugProduktionStop.AutoSize = true;
this.rbFlugzeugProduktionStop.Checked = true;
this.rbFlugzeugProduktionStop.Location = new System.Drawing.Point(26, 55);
this.rbFlugzeugProduktionStop.Name = "rbFlugzeugProduktionStop";
this.rbFlugzeugProduktionStop.Size = new System.Drawing.Size(213, 24);
this.rbFlugzeugProduktionStop.TabIndex = 9;
this.rbFlugzeugProduktionStop.TabStop = true;
this.rbFlugzeugProduktionStop.Text = "Flugzeugproduktion Stop";
this.rbFlugzeugProduktionStop.UseVisualStyleBackColor = true;
this.rbFlugzeugProduktionStop.CheckedChanged += new System.EventHandler(this.rbFlugzeugProduktionStop_CheckedChanged);
//
// groupBox1
//
this.groupBox1.Controls.Add(this.rbPanzerproduktionStart);
this.groupBox1.Controls.Add(this.rbPanzerProduktionStop);
this.groupBox1.Location = new System.Drawing.Point(35, 92);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(291, 91);
this.groupBox1.TabIndex = 10;
this.groupBox1.TabStop = false;
//
// groupBox2
//
this.groupBox2.Controls.Add(this.rbFlugzeugProdutionStart);
this.groupBox2.Controls.Add(this.rbFlugzeugProduktionStop);
this.groupBox2.Location = new System.Drawing.Point(35, 213);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(291, 100);
this.groupBox2.TabIndex = 11;
this.groupBox2.TabStop = false;
//
// tbPanzer
//
this.tbPanzer.Location = new System.Drawing.Point(534, 134);
this.tbPanzer.Name = "tbPanzer";
this.tbPanzer.ReadOnly = true;
this.tbPanzer.Size = new System.Drawing.Size(100, 26);
this.tbPanzer.TabIndex = 12;
//
// tbFlugzeuge
//
this.tbFlugzeuge.Location = new System.Drawing.Point(534, 236);
this.tbFlugzeuge.Name = "tbFlugzeuge";
this.tbFlugzeuge.ReadOnly = true;
this.tbFlugzeuge.Size = new System.Drawing.Size(100, 26);
this.tbFlugzeuge.TabIndex = 13;
//
// tbGuthaben
//
this.tbGuthaben.Location = new System.Drawing.Point(534, 45);
this.tbGuthaben.Name = "tbGuthaben";
this.tbGuthaben.ReadOnly = true;
this.tbGuthaben.Size = new System.Drawing.Size(100, 26);
this.tbGuthaben.TabIndex = 14;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(391, 45);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(81, 20);
this.label1.TabIndex = 15;
this.label1.Text = "Guthaben";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(391, 134);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(117, 20);
this.label2.TabIndex = 16;
this.label2.Text = "Panzerbestand";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(395, 236);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(133, 20);
this.label3.TabIndex = 17;
this.label3.Text = "Flugzeugbestand";
//
// Form2
//
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(792, 445);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.tbGuthaben);
this.Controls.Add(this.tbFlugzeuge);
this.Controls.Add(this.tbPanzer);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.btEnde);
this.Name = "Form2";
this.Text = "Form2";
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();

}

#endregion

private System.Windows.Forms.Button btEnde;
private System.Windows.Forms.RadioButton rbPanzerproduktionStart;
private System.Windows.Forms.RadioButton rbPanzerProduktionStop;
private System.Windows.Forms.RadioButton rbFlugzeugProdutionStart;
private System.Windows.Forms.RadioButton rbFlugzeugProduktionStop;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.GroupBox groupBox2;
private System.Windows.Forms.TextBox tbPanzer;
private System.Windows.Forms.TextBox tbFlugzeuge;
private System.Windows.Forms.TextBox tbGuthaben;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
}
}

Der Beitrag wurde von sharky2014 bearbeitet: 16.04.2014, 18:54 Uhr


--------------------
A programmer is just a tool which converts caffeine into code
TOP    
Beitrag 16.04.2014, 18:59 Uhr
sharky2014
sharky2014
Level 7 = Community-Professor
*******
Gruppe: Mitglied
Mitglied seit: 25.09.2008
Beiträge: 1.692

Was der Designer mit sehr viel Code sagen will, sieht dann in der praktischen Anwendung so aus wie in der Abb.

Wie der Designer zeigt, sind die RadioButtons Panzerproduktion Start/Stop und FlugzeugProduktion Start/Stop in groupBoxen untergebracht, damit sie voneinander unabhängig sind, nicht Start/Stop aber Flugzeuge von den Panzern.

Der Beitrag wurde von sharky2014 bearbeitet: 16.04.2014, 19:06 Uhr
Angehängte Datei(en)
Angehängte Datei  PanzerundFlugzeuge.jpg ( 51.96KB ) Anzahl der Downloads: 11
 


--------------------
A programmer is just a tool which converts caffeine into code
TOP    



1 Besucher lesen dieses Thema (Gäste: 1)
0 Mitglieder: