7. Grafische Benutzeroberflächen mit C# und VS.NET
7.1. Die Grundlagen grafischer Benutzeroberflächen
7.1.1. Ein erstes Projekt
Erstellen wir ein erstes „Windows-Anwendung“-Projekt:
![]() |
- [1]: Erstellen Sie ein neues Projekt
- [2]: Typ „Windows-Anwendung“
- [3]: Der Name des Projekts ist im Moment nicht wichtig
- [4]: Das Projekt wurde erstellt
![]() |
- [5]: Aktuelle Lösung speichern
- [6]: Projektname
- [7]: Lösungsdatei
- [8]: Lösungsname
- [9]: Es wird ein Ordner für die Lösung [Chap5] erstellt. Die darin enthaltenen Projekte werden in Unterordnern abgelegt.
![]() |
- [10]: Projekt [01] in der Lösung [Chap5]:
- [Program.cs] ist die Hauptklasse des Projekts
- [Form1.cs] ist die Quelldatei, die das Verhalten des Fensters [11] steuert
- [Form1.Designer.cs] ist die Quelldatei, die Informationen über die Komponenten des Fensters enthält [11]
- [11]: Datei [Form1.cs] im Entwurfsmodus
- [12]: Die generierte Anwendung kann mit (Strg-F5) ausgeführt werden. Das Fenster [Form1] wird angezeigt. Es kann verschoben, in der Größe angepasst und geschlossen werden. Die grundlegenden Elemente eines grafischen Fensters stehen nun zur Verfügung.
Die Hauptklasse [Program.cs] sieht wie folgt aus:
using System;
using System.Windows.Forms;
namespace Chap5 {
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
- Zeile 2: Anwendungen mit Formularen verwenden den Namespace System.Windows.Forms.
- Zeile 4: Der ursprüngliche Namespace wurde in Chap5 umbenannt.
- Zeile 10: Wenn das Projekt ausgeführt wird (Strg-F5), wird die Methode [Main] ausgeführt.
- Zeilen 11–13: Die Anwendung „Classroom“ gehört zum Namespace System.Windows.Forms. Sie enthält statische Methoden zum Starten und Beenden von Windows-Grafikanwendungen.
- Zeile 11: optional – ermöglicht es Ihnen, den auf einem Formular platzierten Steuerelementen unterschiedliche visuelle Stile zuzuweisen
- Zeile 12: optional – legt die Rendering-Engine für Steuerelementtexte fest: GDI+ (true), GDI (false)
- Zeile 13: Die einzige wesentliche Zeile in der [Main]-Methode: Sie instanziiert die Klasse [Form1] – die Formular-Klasse – und weist sie an, ausgeführt zu werden.
Die Quelldatei [Form1.cs] sieht wie folgt aus:
using System;
using System.Windows.Forms;
namespace Chap5 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
}
}
- Zeile 5: Das Formular Form1 leitet sich von der Klasse [System.Windows.Forms.Form] ab, der übergeordneten Klasse aller Fenster. Das Schlüsselwort partial gibt an, dass die Klasse partiell ist und durch andere Quelldateien vervollständigt werden kann. Dies ist hier der Fall, wo die Klasse Form1 in zwei Dateien aufgeteilt ist:
- [Form1.cs]: Hier finden Sie das Verhalten des Formulars, einschließlich seiner Ereignisbehandler
- [Form1.Designer.cs]: enthält die Formularkomponenten und ihre Eigenschaften. Diese Datei wird jedes Mal neu generiert, wenn der Benutzer das Fenster im [Design]-Modus ändert.
- Zeilen 6–8: Klassenkonstruktor Form1
- Zeile 7: Ruft die Methode InitializeComponent auf. Diese Methode ist in [Form1.cs] nicht vorhanden. Sie befindet sich in [Form1.Designer.cs].
Die Quelldatei [Form1.Designer.cs] sieht wie folgt aus:
namespace Chap5 {
partial class Form1 {
// <summary>
// Required designer variable.
// </summary>
private System.ComponentModel.IContainer components = null;
//tax <summary>
//tax Clean up any resources being used.
//tax </summary>
//tax <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
//tax <summary>
//a IImpot object Required method for Designer support - do not modify
//Tax the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.SuspendLayout();
///
///
///
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(196, 98);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
}
}
- Zeile 2: Die Klasse lautet immer Form1. Beachten Sie, dass nicht mehr wiederholt werden muss, dass sie von der Klasse Form abgeleitet ist.
- Zeilen 25–37: Die Methode InitializeComponent, die vom Konstruktor der Klasse [Form1] aufgerufen wird. Diese Methode erstellt und initialisiert alle Formularkomponenten. Sie wird jedes Mal neu generiert, wenn das Formular im [Entwurfsmodus] geändert wird. In den Zeilen 19–39 wird ein Abschnitt namens „Region“ erstellt, um sie abzugrenzen. Der Entwickler darf in dieser Region keinen Code hinzufügen: Er wird bei der nächsten Neugenerierung überschrieben.
Zunächst ist es einfacher, den Code in [Form1.Designer.cs] zu ignorieren. Er wird automatisch generiert und ist die Übersetzung der vom Entwickler im [Design]-Modus getroffenen Entscheidungen in die Sprache C#. Nehmen wir ein erstes Beispiel:
![]() |
- [1]: Wählen Sie den [Design]-Modus aus, indem Sie auf die Datei [Form1.cs] doppelklicken
- [2]: Klicken Sie mit der rechten Maustaste auf das Formular und wählen Sie [Eigenschaften]
- [3]: Eigenschaftenfenster von [Form1]
- [4]: Die Eigenschaft [Text] gibt den Fenstertitel an
- [5]: Die Änderung der Eigenschaft [Text] wird sowohl im [Design]-Modus als auch im Quellcode von [Form1.Designer.cs] berücksichtigt:
private void InitializeComponent() {
this.SuspendLayout();
...
this.Text = "Mon 1er formulaire";
...
}
7.1.2. Ein zweites Projekt
7.1.2.1. Das Formular
Wir starten ein neues Projekt namens 02. Dazu befolgen wir die oben beschriebene Vorgehensweise zum Erstellen eines Projekts. Das zu erstellende Fenster sieht wie folgt aus:
![]() |
Die Formularkomponenten sind wie folgt:
Nr. | Name | Typ | Rolle |
1 | BezeichnungEingabe | Bezeichnung | ein Text |
2 | textBoxSaisie | Textfeld | ein Eingabefeld |
3 | SchaltflächeAnzeigen | Schaltfläche | zum Anzeigen des Inhalts des Eingabefelds „textBoxSaisie“ in einem Dialogfeld |
Um dieses Fenster zu erstellen, gehen Sie wie folgt vor:
![]() |
- [1]: Klicken Sie mit der rechten Maustaste auf das Formular außerhalb einer Komponente und wählen Sie die Option [Eigenschaften]
- [2]: Das Fenster mit den Eigenschaften erscheint in der unteren rechten Ecke von Visual Studio
Zu den Formulareigenschaften gehören:
zum Festlegen der Hintergrundfarbe des Fensters | |
zum Festlegen der Farbe von Zeichnungen oder Text im Fenster | |
um dem Fenster ein Menü zuzuordnen | |
um dem Fenster einen Titel zu geben | |
um den Fenstertyp festzulegen | |
zum Festlegen der Schriftart für die Anzeige im | |
um den Fensternamen festzulegen |
Hier legen wir die Eigenschaften Text und Name fest:
Eingabefelder und Schaltflächen – 1 | |
frmSaisiesBoutons |
![]() |
- [1]: Wählen Sie die Toolbox [Common Controls] aus den in Visual Studio verfügbaren Toolboxen aus
- [2, 3, 4]: Doppelklicken Sie nacheinander auf die Komponenten [Label], [Button] und [TextBox]
- [5]: Die drei Komponenten befinden sich nun auf dem Formular
Um Komponenten korrekt auszurichten und ihre Größe anzupassen, können Sie die Elemente der Symbolleiste verwenden:
![]() | |||||||||
Das Prinzip der Formatierung ist wie folgt:
- Wählen Sie die Komponenten aus, die gemeinsam formatiert werden sollen (halten Sie die Strg-Taste gedrückt, während Sie auf die Komponenten klicken)
- Wählen Sie die gewünschte Formatierungsart aus:
- (Fortsetzung)
- Optionen „Ausrichten“ ermöglichen das Ausrichten der Komponenten oben, unten, links, rechts, zentriert usw.
- Optionen „Gleiche Größe“ ermöglichen es, Komponenten auf die gleiche Höhe oder Breite zu bringen
- Die Option „Horizontaler Abstand“ ermöglicht die horizontale Ausrichtung der Komponenten mit gleich breiten Abständen dazwischen. Dasselbe gilt für die Option „Vertikaler Abstand“ zur vertikalen Ausrichtung.
- Die Option „Zentrieren“ zentriert eine Komponente horizontal (horizontal) oder vertikal (vertikal) im
Sobald die Komponenten platziert sind, legen wir ihre Eigenschaften fest. Klicken Sie dazu mit der rechten Maustaste auf die Komponente und wählen Sie die Option „Eigenschaften“:
![]() |
- [1]: Wählen Sie die Komponente aus, um das Eigenschaftenfenster zu öffnen. Ändern Sie in diesem Fenster die folgenden Eigenschaften: Name: labelSaisie, Text: Eingabe
- [2]: Gehen Sie auf die gleiche Weise vor: Name: textBoxSaisie, Text: nichts eingeben
- [3] : Name: buttonAfficher, Text: Anzeigen
- [4]: das Fenster selbst: Name: frmSaisiesBoutons, Text: Eingabefelder und Schaltflächen – 1
- [5]: Führen Sie das Projekt aus (Strg-F5), um einen ersten Eindruck vom Fenster in Aktion zu erhalten.
Was im [Design]-Modus erstellt wurde, wurde in [Form1.Designer.cs]-Code umgesetzt:
namespace Chap5 {
partial class frmSaisiesBoutons {
...
private System.ComponentModel.IContainer components = null;
...
private void InitializeComponent() {
this.labelSaisie = new System.Windows.Forms.Label();
this.buttonAfficher = new System.Windows.Forms.Button();
this.textBoxSaisie = new System.Windows.Forms.TextBox();
this.SuspendLayout();
///
///
///
this.labelSaisie.AutoSize = true;
this.labelSaisie.Location = new System.Drawing.Point(12, 19);
this.labelSaisie.Name = "labelSaisie";
this.labelSaisie.Size = new System.Drawing.Size(35, 13);
this.labelSaisie.TabIndex = 0;
this.labelSaisie.Text = "Saisie";
///
///
///
this.buttonAfficher.Location = new System.Drawing.Point(80, 49);
this.buttonAfficher.Name = "buttonAfficher";
this.buttonAfficher.Size = new System.Drawing.Size(75, 23);
this.buttonAfficher.TabIndex = 1;
this.buttonAfficher.Text = "Afficher";
this.buttonAfficher.UseVisualStyleBackColor = true;
this.buttonAfficher.Click += new System.EventHandler(this.buttonAfficher_Click);
///
// Form1
// labelSaisie
this.textBoxSaisie.Location = new System.Drawing.Point(80, 19);
this.textBoxSaisie.Name = "textBoxSaisie";
this.textBoxSaisie.Size = new System.Drawing.Size(100, 20);
this.textBoxSaisie.TabIndex = 2;
// buttonAfficher
// textBoxSaisie
// frmSaisiesBoutons
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 118);
this.Controls.Add(this.textBoxSaisie);
this.Controls.Add(this.buttonAfficher);
this.Controls.Add(this.labelSaisie);
this.Name = "frmSaisiesBoutons";
this.Text = "Saisies et boutons - 1";
this.ResumeLayout(false);
this.PerformLayout();
}
private System.Windows.Forms.Label labelSaisie;
private System.Windows.Forms.Button buttonAfficher;
private System.Windows.Forms.TextBox textBoxSaisie;
}
}
- Zeilen 53–55: Die drei Komponenten haben drei private Felder in der Klasse [Form1] erzeugt. Beachten Sie, dass die Namen dieser Felder den Namen entsprechen, die den Komponenten im [Design]-Modus gegeben wurden. Dies gilt auch für Zeile 2, bei der es sich um die Klasse selbst handelt.
- Zeilen 7–9: Die drei Objekte vom Typ [Label], [TextBox] und [Button] werden erstellt. Diese dienen zur Verwaltung visueller Komponenten.
- Zeilen 14–19: Konfiguration des Labels „labelSaisie“
- Zeilen 23–29: Konfiguration der Schaltfläche buttonAfficher
- Zeilen 33–36: Konfiguration des Eingabefelds textBoxSaisie
- Zeilen 40–47: Formular-Konfiguration frmSaisiesBoutons. Die Zeilen 43–45 zeigen, wie Komponenten zum Formular hinzugefügt werden.
Dieser Code ist leicht verständlich. Daher ist es möglich, Formulare per Code zu erstellen, ohne den [Entwurfsmodus] zu verwenden. Zahlreiche Beispiele hierfür finden sich in der MSDN-Dokumentation zu Visual Studio. Wenn Sie diesen Code beherrschen, können Sie Formulare zur Laufzeit erstellen: Sie können beispielsweise spontan ein Formular erstellen, um eine Datenbanktabelle zu aktualisieren, wobei die Tabellenstruktur erst zur Laufzeit ermittelt wird.
Nun muss nur noch die Prozedur für die Verarbeitung eines Klicks auf die Ansicht geschrieben werden. Wählen Sie die Schaltfläche aus, um das Eigenschaftenfenster aufzurufen. Dieses Fenster verfügt über mehrere Registerkarten:
![]() |
- [1]: Liste der Eigenschaften in alphabetischer Reihenfolge
- [2]: Steuerelementereignisse
Auf Steuerelementeigenschaften und -ereignisse kann nach Kategorie oder alphabetisch zugegriffen werden:
- [3]: Eigenschaften oder Ereignisse nach Kategorie
- [4]: Eigenschaften oder Ereignisse in alphabetischer Reihenfolge
Die Ereignisse in den Kategorien für die Schaltfläche „Afficher“ lauten wie folgt:
![]() |
- [1]: In der linken Spalte des Fensters sind die möglichen Ereignisse für die Schaltfläche aufgelistet. Ein Klick auf eine Schaltfläche entspricht dem Ereignis „Click“.
- [2]: Die rechte Spalte enthält den Namen der Prozedur, die aufgerufen wird, wenn das entsprechende Ereignis eintritt.
- [3]: Wenn Sie auf die Ereigniszelle „Click“ doppelklicken, wechseln wir automatisch zum Codefenster, um den Ereignis-Handler „Click“ für die Schaltfläche „buttonAfficher“ zu schreiben:
using System;
using System.Windows.Forms;
namespace Chap5 {
public partial class frmSaisiesBoutons : Form {
public frmSaisiesBoutons() {
InitializeComponent();
}
private void buttonAfficher_Click(object sender, EventArgs e) {
}
}
}
- Zeilen 10–12: Das Gerüst des Ereignisbehandlungsroutinen Klicken Sie auf die Schaltfläche „buttonAfficher“. Folgende Punkte sind zu beachten:
- Die Methode ist wie folgt benannt: eventName_ComponentName
- Die Methode ist privat. Sie erhält zwei Parameter:
- sender: ist das Objekt, das das Ereignis ausgelöst hat. Wenn die Prozedur nach einem Klick auf buttonAfficher ausgeführt wird, ist sender gleich buttonAfficher. Es ist denkbar, dass buttonAfficher_Click von einer anderen Prozedur aus ausgeführt wird. Diese Prozedur könnte dann das Objekt sender nach Belieben festlegen.
- EventArgs: ein Objekt, das Informationen über das Ereignis enthält. Bei einem Click-Ereignis enthält es nichts. Bei einem Mausbewegungsereignis enthält es die (X,Y)-Koordinaten der Maus.
- Wir werden hier keinen dieser Parameter verwenden.
Um einen Ereignisbehandler zu schreiben, muss das vorangegangene Code-Gerüst vervollständigt werden. Mit „Ici“ möchten wir ein Dialogfeld anzeigen, das den Inhalt des Feldes „textBoxSaisie“ enthält, sofern dieses nicht leer ist [1], andernfalls eine Fehlermeldung [2]:
![]() |
Der Code dafür könnte wie folgt aussehen:
private void buttonAfficher_Click(object sender, EventArgs e) {
// displays the text entered in the TextBox textboxSaisie
string texte = textBoxSaisie.Text.Trim();
if (texte.Length != 0) {
MessageBox.Show("Texte saisi= " + texte, "Vérification de la saisie", MessageBoxButtons.OK, MessageBoxIcon.Information);
} else {
MessageBox.Show("Saissez un texte...", "Vérification de la saisie", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
Die Klasse MessageBox wird verwendet, um Meldungen in einem Fenster anzuzeigen. Wir haben hier die Methode Show verwendet:
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon);
mit
die anzuzeigende Meldung | |
Fenstertitel | |
Schaltflächen im Fenster | |
das Symbol im |
Die Schaltflächen können ihre Werte aus den folgenden Konstanten beziehen (mit dem Präfix „MessageBoxButtons“, wie in Zeile 7 oben gezeigt):
Schaltflächen | |
![]() | |
![]() | |
![]() | |
![]() | |
![]() | |
![]() |
Das Symbol kann seine Werte aus den folgenden Konstanten beziehen (mit dem Präfix „MessageBoxIcon“, wie in Zeile 10 oben gezeigt):
![]() | idem Stopp | ||
idem Warnung | ![]() | ||
idem Sternchen | ![]() | ||
![]() | idem Hand | ||
![]() |
Die Methode Show ist eine statische Methode, die ein Ergebnis vom Typ [System.Windows.Forms.DialogResult] zurückgibt, bei dem es sich um eine Aufzählung handelt:

Um herauszufinden, welche Schaltfläche der Benutzer zum Schließen des MessageBox-Fensters gedrückt hat, schreiben wir:
7.1.2.2. Code zur Ereignisbehandlung
Zusätzlich zu dem von uns geschriebenen buttonAfficher_Click hat Visual Studio in der Methode InitializeComponents von [Form1.Designer.cs], die die Formularkomponenten erstellt und initialisiert, die folgende Zeile generiert:
this.buttonAfficher.Click += new System.EventHandler(this.buttonAfficher_Click);
Click ist eine Ereignisklasse des Buttons [1, 2, 3]:
![]() |
- [5]: die Deklaration des Ereignisses [Control.Click] [4]. Dies zeigt, dass das Ereignis Click nicht spezifisch für die Klasse [Button] ist. Es gehört zur Klasse [Control], der übergeordneten Klasse der Klasse [Button].
- EventHandler ist ein Prototyp (ein Modell) einer Methode, die als Delegat bezeichnet wird. Wir werden später darauf zurückkommen.
- event ist ein Schlüsselwort, das die Funktionalität des Delegaten EventHandler einschränkt: Ein Objekt-Delegat verfügt über umfangreichere Funktionen als ein event.
Der Delegat EventHandler ist wie folgt definiert:
![]() |
Der EventHandler-Delegat bezeichnet ein Methodenmodell:
- mit dem Typ als erstem Parameter Object
- dessen zweiter Parameter ein EventArgs ist
- und der keine Ergebnisse zurückgibt
Dies ist der Fall bei der von Visual Studio generierten Methode zur Verwaltung von Klicks auf die Schaltfläche „Afficher“:
private void buttonAfficher_Click(object sender, EventArgs e);
buttonAfficher_Click entspricht dem durch den EventHandler definierten Prototyp. Um einen EventHandler zu erstellen, gehen Sie wie folgt vor:
EventHandler evtHandler=new EventHandler(méthode correspondant au prototype défini par le type EventHandler);
Da buttonAfficher_Click dem vom EventHandler definierten Prototyp entspricht, können wir schreiben:
Eine Variable vom Typ Delegate ist eigentlich eine Liste von Verweisen auf Methoden wie den Delegate. Um der obigen Variablen evtHandler eine neue Methode M hinzuzufügen, verwenden wir die folgende Syntax:
Die Notation += kann auch verwendet werden, wenn evtHandler eine leere Liste ist.
Kehren wir zu der Zeile in [InitializeComponent] zurück, die dem Ereignis Click des Objekts buttonAfficher einen Ereignis-Handler hinzufügt:
this.buttonAfficher.Click += new System.EventHandler(this.buttonAfficher_Click);
Diese Anweisung fügt einen EventHandler zur Liste der Methoden in buttonAfficher.Click hinzu. Diese Methoden werden jedes Mal aufgerufen, wenn ein Klick auf die Komponente buttonAfficher erkannt wird. Oft gibt es nur eine. Sie wird als „Ereignisbehandler“ bezeichnet.
Kehren wir zur Signatur von EventHandler zurück:
private delegate void EventHandler(object sender, EventArgs e);
Der zweite Parameter des Delegaten ist ein Objekt vom Typ EventArgs oder einer abgeleiteten Klasse. Der Typ EventArgs ist sehr allgemein gehalten und liefert eigentlich keine Informationen über das aufgetretene Ereignis. Für einen Klick auf eine Schaltfläche ist dies ausreichend. Für eine Mausbewegung auf einem Formular hätten wir ein MouseMove der Klasse [Form], definiert durch:
Der Delegat MouseEventHandler ist wie folgt definiert:
![]() |
Dies ist eine delegierte Signaturfunktion void f (object, MouseEventArgs). Die Klasse MouseEventArgs ist definiert durch:
![]() |
Die Klasse MouseEventArgs bietet mehr Funktionen als die Klasse EventArgs. So können wir beispielsweise die X- und Y-Koordinaten der Maus zum Zeitpunkt des Ereignisses ermitteln.
7.1.2.3. Fazit
Aus den beiden untersuchten Projekten lässt sich schließen, dass die Aufgabe des Entwicklers, sobald die GUI mit Visual Studio erstellt wurde, hauptsächlich darin besteht, die Ereignisbehandler zu schreiben, die er für diese GUI verwalten möchte. Der Code wird automatisch von Visual Studio generiert. Dieser Code, der komplex sein kann, kann zunächst ignoriert werden. Später kann seine Untersuchung jedoch zu einem besseren Verständnis der Erstellung und Verwaltung von Formularen beitragen.
7.2. Grundlegende Komponenten
Wir stellen nun eine Reihe von Anwendungen vor, die die gängigsten Komponenten enthalten, um deren wichtigste Methoden und Eigenschaften kennenzulernen. Für jede Anwendung präsentieren wir die grafische Oberfläche und den relevanten Code, vor allem den der Ereignisbehandler.
7.2.1. Formular Formular
Wir beginnen mit der Vorstellung der wesentlichen Komponente, dem Formular, auf dem Sie Komponenten platzieren. Einige seiner grundlegenden Eigenschaften haben wir bereits vorgestellt. Wir werden nun einige der wichtigsten Ereignisse des Formulars behandeln.
Das Formular wird geladen | |
Das Formular wird geschlossen | |
Das Formular ist geschlossen |
Das Load-Ereignis tritt ein, bevor das Formular angezeigt wird. Das Closing-Ereignis tritt ein, wenn das Formular geschlossen wird. Es ist auch möglich, dieses Schließen programmgesteuert zu verhindern.
Wir erstellen ein Formular namens Form1 ohne Komponenten:
![]() |
- [1]: das Formular
- [2]: die drei behandelten Ereignisse
Der Code für [Form1.cs] lautet wie folgt:
using System;
using System.Windows.Forms;
namespace Chap5 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
// initial form loading
MessageBox.Show("Evt Load", "Load");
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
// the form is closing
MessageBox.Show("Evt FormClosing", "FormClosing");
// confirmation requested
DialogResult réponse = MessageBox.Show("Voulez-vous vraiment quitter l'application", "Closing", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (réponse == DialogResult.No)
e.Cancel = true;
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e) {
// the form will be closed
MessageBox.Show("Evt FormClosed", "FormClosed");
}
}
}
Wir verwenden die MessageBox, um über Ereignisse benachrichtigt zu werden.
Zeile 10: Das Ereignis „Load“ tritt ein, wenn die die Anwendung, noch bevor das Formular überhaupt angezeigt wird: | ![]() |
Zeile 15: Das Ereignis „FormClosing“ wird ausgelöst, wenn der Benutzer das Fenster schließt. | ![]() |
Zeile 19: Wir fragen ihn dann, ob er das Fenster wirklich schließen möchte : | ![]() |
Zeile 20: Wenn er mit „Nein“ antwortet, setzen wir die Eigenschaft „Cancel“ des dem Ereignis „CancelEventArgs e“, das die Methode im . Wenn wir diese Eigenschaft auf „False“ setzen, wird das Schließen des Fensters abgebrochen, andernfalls wird es fortgesetzt. Das Ereignis FormClosed wird dann ausgelöst: | ![]() |
7.2.2. Beschriftungen und Eingabefelder TextBox
Diese beiden Komponenten sind uns bereits begegnet. Label ist eine Textkomponente und TextBox eine Eingabefeldkomponente. Ihre Haupteigenschaft ist Text, die entweder den Inhalt des Eingabefelds oder den Text des Labels bezeichnet. Diese Eigenschaft ist les- und schreibbar.
Das für TextBox üblicherweise verwendete Ereignis ist „TextChanged“, das signalisiert, dass der Benutzer das Eingabefeld geändert hat. Hier ist ein Beispiel, bei dem „TextChanged“ verwendet wird, um Änderungen in einem Eingabefeld zu verfolgen:
![]() |
Nr. | Typ | Name | Rolle |
1 | Textfeld | textBoxSaisie | Eingabefeld |
2 | Beschriftung | labelControle | zeigt den Text aus 1 in Echtzeit an AutoSize=False, Text=(rien) |
3 | Schaltfläche | buttonEffacer | zum Löschen der Felder 1 und 2 |
4 | Schaltfläche | SchaltflächeQuitter | zum Beenden der Anwendung |
Der Code für diese Anwendung lautet:
using System.Windows.Forms;
namespace Chap5 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void textBoxSaisie_TextChanged(object sender, System.EventArgs e) {
// the content of TextBox has changed - copy it to Label labelControle
labelControle.Text = textBoxSaisie.Text;
}
private void buttonEffacer_Click(object sender, System.EventArgs e) {
// delete the contents of the input box
textBoxSaisie.Text = "";
}
private void buttonQuitter_Click(object sender, System.EventArgs e) {
// click on the Quit button - exit the application
Application.Exit();
}
private void Form1_Shown(object sender, System.EventArgs e) {
// focus on the input field
textBoxSaisie.Focus();
}
}
}
- Zeile 24: Das Ereignis [Form].Shown wird ausgelöst, wenn das Formular angezeigt wird
- Zeile 26: Der Fokus (für die Eingabe) wird auf die Komponente textBoxSaisie gesetzt.
- Zeile 9: Das Ereignis [TextBox].TextChanged tritt ein, sobald sich der Inhalt der Komponente TextBox ändert
- Zeile 11: Der Inhalt der Komponente [TextBox] wird in die Komponente [Label] kopiert
- Zeile 14: Verarbeitet den Klick auf die Schaltfläche [Delete]
- Zeile 16: Wir setzen die leere Zeichenfolge in die Komponente [TextBox]
- Zeile 19: Verarbeitet den Klick auf die Schaltfläche [Quit]
- Zeile 21: Beendet die laufende Anwendung. Beachten Sie, dass die Anwendung über die Methode [Main] in [Form1.cs] gestartet wird:
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
Das folgende Beispiel verwendet ein mehrzeiliges Textfeld:
![]() |
Die Liste der Steuerelemente lautet wie folgt:
Nr. | Typ | Name | Rolle |
1 | Textfeld | textBoxLignes | mehrzeiliges Eingabefeld Multiline=true, ScrollBars=Both, AcceptReturn=True, AcceptTab=True |
2 | TextBox | textBoxLigne | Einzeiliges Eingabefeld |
3 | Schaltfläche | buttonAjouter | Fügt Inhalte von 2 nach 1 hinzu |
Um ein Textfeld mehrzeilig zu gestalten, legen Sie die folgenden Steuerungseigenschaften fest:
, um mehrere Zeilen Text zuzulassen | |
um festzulegen, ob das Steuerelement Bildlaufleisten haben soll (Horizontal, Vertical, Both) oder nicht (None) | |
Wenn „true“, springt die Eingabetaste zur Zeile | |
Wenn „true“, erzeugt die Tabulatortaste einen Tabulator im Text |
Die Anwendung ermöglicht es, Zeilen direkt in [1] einzugeben oder über [2] und [3] hinzuzufügen.
Der Anwendungscode lautet wie folgt:
using System.Windows.Forms;
using System;
namespace Chap5 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void buttonAjouter_Click(object sender, System.EventArgs e) {
// add the content of textBoxLigne to that of textBoxLignes
textBoxLignes.Text += textBoxLigne.Text+Environment.NewLine;
textBoxLigne.Text = "";
}
private void Form1_Shown(object sender, EventArgs e) {
// focus on the input field
textBoxLigne.Focus();
}
}
}
- Zeile 18: Wenn das Formular angezeigt wird (Ereignis Shown), Fokus auf das Eingabefeld textBoxLigne
- Zeile 10: Verarbeitet den Klick auf die Schaltfläche [Hinzufügen]
- Zeile 12: Der Text aus dem Eingabefeld textBoxLigne wird an den Text im Eingabefeld textBoxLignes angehängt, gefolgt von einem Zeilenumbruch.
- Zeile 13: Das Eingabefeld textBoxLigne wird gelöscht
7.2.3. Dropdown-Listen ComboBox
Wir erstellen das folgende Formular:
![]() |
Nr. | Typ | Name | Rolle |
1 | Kombinationsfeld | comboNombres | enthält Zeichenfolgen DropDownStyle=DropDownList |
Eine ComboBox-Komponente ist eine Dropdown-Liste mit einem Eingabefeld: Der Benutzer kann entweder einen Eintrag auswählen (2) oder Text eingeben (1). Es gibt drei Arten von ComboBoxen, die durch die Eigenschaft DropDownStyle festgelegt werden:
Nicht-Dropdown-Liste mit Bearbeitungsfeld | |
Dropdown-Liste mit Bearbeitungsfeld | |
Dropdown-Liste ohne Bearbeitungsfeld |
Standardmäßig ist der Typ einer ComboBox „DropDown“.
Die Klasse „ComboBox“ eines einzelnen Herstellers:
erstellt eine leere Combobox |
Die Elemente der ComboBox sind in der Eigenschaft Items verfügbar:
Dies ist eine indizierte Eigenschaft, wobei Items[i] das i-te Element der Combo bezeichnet. Sie ist schreibgeschützt.
Oder C eine Combo und C.Items ihre Liste von Elementen. Wir haben die folgenden Eigenschaften:
Anzahl der Combo-Elemente | |
Element i der Combobox | |
fügt Objekt o als letztes Element der Kombinationsliste hinzu | |
fügt ein Array von Objekten am Ende einer Combobox hinzu | |
fügt das Objekt o an Position i der Combobox ein | |
entfernt Element i aus der Combobox | |
entfernt das Objekt o aus der Combobox | |
löscht alle Elemente der Combobox | |
gibt die Position i des Objekts o in der Combobox zurück | |
Index des ausgewählten Elements | |
ausgewähltes Element | |
Text, der für das ausgewählte Element angezeigt wird | |
Text, der für das ausgewählte Element angezeigt wird |
Es mag überraschen, dass eine Kombinationsfeld Objekte enthalten kann, während es visuell Zeichenfolgen anzeigt. Wenn ein Kombinationsfeld ein Objekt obj enthält, zeigt es die Zeichenfolge obj.ToString() an. Denken Sie daran, dass jedes Objekt eine von der Klasse Object geerbte ToString-Methode besitzt, die eine Zeichenfolge rendert, die das Objekt „repräsentiert“.
Das in der Combo-Box C ausgewählte Element ist C.SelectedItem oder C.Items[C.SelectedIndex], wobei C.SelectedIndex die Nummer des ausgewählten Elements ist, beginnend mit Null für das erste Element. Der ausgewählte Text kann auf verschiedene Arten abgerufen werden: C.SelectedItem.Text, C.Text
Wenn ein Element aus der Dropdown-Liste ausgewählt wird, wird das Ereignis SelectedIndexChanged ausgelöst, das dann verwendet werden kann, um den Benutzer über eine Auswahländerung im Kombinationsfeld zu informieren. In der folgenden Anwendung verwenden wir dieses Ereignis, um das in der Liste ausgewählte Element anzuzeigen.
![]() |
Der Anwendungscode lautet wie folgt:
using System.Windows.Forms;
namespace Chap5 {
public partial class Form1 : Form {
private int previousSelectedIndex=0;
public Form1() {
InitializeComponent();
// combo filling
comboBoxNombres.Items.AddRange(new string[] { "zéro", "un", "deux", "trois", "quatre" });
// select item no. 0
comboBoxNombres.SelectedIndex = 0;
}
private void comboBoxNombres_SelectedIndexChanged(object sender, System.EventArgs e) {
int newSelectedIndex = comboBoxNombres.SelectedIndex;
if (newSelectedIndex != previousSelectedIndex) {
// the selected item has changed - it is displayed
MessageBox.Show(string.Format("Elément sélectionné : ({0},{1})", comboBoxNombres.Text, newSelectedIndex), "Combo", MessageBoxButtons.OK, MessageBoxIcon.Information);
// note the new index
previousSelectedIndex = newSelectedIndex;
}
}
}
}
- Zeile 5: previousSelectedIndex speichert den zuletzt in der Combobox ausgewählten Index
- Zeile 10: Füllt das Kombinationsfeld mit einem Array von Zeichenfolgen
- Zeile 12: Das erste Element wird ausgewählt
- Zeile 15: Die Methode, die jedes Mal ausgeführt wird, wenn der Benutzer ein Element aus dem Kombinationsfeld auswählt. Anders als der Name vermuten lässt, tritt dieses Ereignis auch dann ein, wenn das ausgewählte Element mit dem vorherigen übereinstimmt.
- Zeile 16: Notiert den Index des ausgewählten Elements
- Zeile 17: falls anders als oben
- Zeile 19: Zeigt die Nummer und den Text des ausgewählten Elements an
- Zeile 21: Notiere den neuen Index
7.2.4. Komponente ListBox
Wir schlagen vor, die folgende Schnittstelle zu erstellen:
![]() |
Die Komponenten dieses Fensters sind wie folgt:
Nr. | Typ | Name | Funktion/Eigenschaften |
0 | Formular | Form1 | Formular FormBorderStyle=FixedSingle (Rahmen nicht skalierbar) |
1 | Textfeld | textBoxSaisie | Eingabefeld |
2 | Schaltfläche | SchaltflächeHinzufügen | Schaltfläche zum Hinzufügen des Inhalts des Eingabefelds [1] zur Liste [3] |
3 | ListBox | listBox1 | Liste 1 SelectionMode=MultiExtended : |
4 | ListBox | ListBox2 | Liste 2 Auswahlmodus=MultiSimple : |
5 | Schaltfläche | Schaltfläche1zu2 | überträgt ausgewählte Elemente aus Liste 1 in Liste 2 |
6 | Schaltfläche | Schaltfläche2zu1 | macht das Gegenteil |
7 | Schaltfläche | buttonEffacer1 | leert Liste 1 |
8 | Schaltfläche | buttonEffacer2 | leert Liste 2 |
Die Komponenten ListBox verfügen über einen Auswahlmodus für ihre Elemente, der durch ihre Eigenschaft SelectionMode definiert wird:
nur ein Element kann ausgewählt werden | |
Mehrfachauswahl möglich: Durch Gedrückthalten der UMSCHALTTASTE und Klicken auf ein Element wird die Auswahl vom zuvor ausgewählten Element auf das aktuelle Element erweitert. | |
Mehrfachauswahl möglich: Ein Element kann per Mausklick oder durch Drücken der Leertaste ausgewählt oder abgewählt werden. |
- Der Benutzer gibt Text in Feld 1 ein und fügt ihn über die Schaltfläche „Hinzufügen“ (2) zur Liste 1 hinzu. Das Eingabefeld (1) wird anschließend geleert, und der Benutzer kann ein neues Element hinzufügen.
- Er kann Elemente von einer Liste in eine andere übertragen, indem er das zu übertragende Element in einer der Listen auswählt und die entsprechende Schaltfläche 5 oder 6 wählt. Das übertragene Element wird am Ende der Zielliste hinzugefügt und aus der Quellliste entfernt.
- Er kann auf ein Element in Liste 1 doppelklicken. Dieses Element wird dann in das Bearbeitungsfeld übertragen und aus Liste 1 entfernt.
Die Schaltflächen werden nach den folgenden Regeln aktiviert oder deaktiviert:
- Die Schaltfläche „Hinzufügen“ leuchtet nur, wenn das Eingabefeld nicht leer ist
- Die Schaltfläche [5] zum Übertragen von Liste 1 in Liste 2 leuchtet nur, wenn in Liste 1 ein Element ausgewählt ist
- Die Schaltfläche [6] zum Übertragen von Liste 2 in Liste 1 leuchtet nur, wenn in Liste 2 ein Element ausgewählt ist
- Die Schaltflächen [7] und [8] zum Löschen der Listen 1 und 2 leuchten nur, wenn die zu löschende Liste Elemente enthält.
Unter den oben genannten Bedingungen müssen alle Schaltflächen beim Start der Anwendung deaktiviert sein. Dies sind die „Enabled“-Schaltflächen, die dann auf „false“ gesetzt werden müssen. Dies kann bereits in der Entwurfsphase erfolgen, wodurch der entsprechende Code in der Methode „InitializeComponent“ generiert wird, oder wir können es selbst im Builder wie unten gezeigt tun:
public Form1() {
InitializeComponent();
// --- initialisations complémentaires ---
// on inhibe un certain nombre de boutons
buttonAjouter.Enabled = false;
button1vers2.Enabled = false;
button2vers1.Enabled = false;
buttonEffacer1.Enabled = false;
buttonEffacer2.Enabled = false;
}
Der Status der Schaltfläche „Hinzufügen“ wird durch den Inhalt des Eingabefelds gesteuert. Dies ist das TextChanged-Ereignis, mit dem wir Änderungen an diesem Inhalt verfolgen können:
private void textBoxSaisie_TextChanged(object sender, System.EventArgs e) {
// the content of textBoxSaisie has changed
// the Add button is only lit if the entry is non-empty
buttonAjouter.Enabled = textBoxSaisie.Text.Trim() != "";
}
Der Status der Übertragungsschaltflächen hängt davon ab, ob in der Liste, die sie steuern, ein Element ausgewählt wurde oder nicht:
private void listBox1_SelectedIndexChanged(object sender, System.EventArgs e) {
// an item has been selected
// switch on the 1 to 2 transfer button
button1vers2.Enabled = true;
}
private void listBox2_SelectedIndexChanged(object sender, System.EventArgs e) {
// an item has been selected
// switch on the 2 to 1 transfer button
button2vers1.Enabled = true;
}
Der Code für das Klicken auf „Hinzufügen“ lautet wie folgt:
private void buttonAjouter_Click(object sender, System.EventArgs e) {
// add a new element to list 1
listBox1.Items.Add(textBoxSaisie.Text.Trim());
// raz de la saisie
textBoxSaisie.Text = "";
// List 1 is not empty
buttonEffacer1.Enabled = true;
// return focus to input box
textBoxSaisie.Focus();
}
Beachten Sie den Befehl „Focus“, um den Fokus auf ein Formularsteuerelement zu setzen. Der Code, der mit dem Klicken auf die Schaltfläche „Löschen“ verknüpft ist:
private void buttonEffacer1_Click(object sender, System.EventArgs e) {
// delete list 1
listBox1.Items.Clear();
// delete button
buttonEffacer1.Enabled = false;
}
private void buttonEffacer2_Click(object sender, System.EventArgs e) {
// delete list 2
listBox2.Items.Clear();
// delete button
buttonEffacer2.Enabled = false;
}
Der Code zum Übertragen ausgewählter Elemente von einer Liste in eine andere:
private void button1vers2_Click(object sender, System.EventArgs e) {
// transfer the item selected in List 1 to List 2
transfert(listBox1, button1vers2, buttonEffacer1, listBox2, button2vers1, buttonEffacer2);
}
private void button2vers1_Click(object sender, System.EventArgs e) {
// transfer the item selected in List 2 to List 1
transfert(listBox2, button2vers1, buttonEffacer2, listBox1, button1vers2, buttonEffacer1);
}
Die beiden oben genannten Methoden delegieren die Übertragung ausgewählter Elemente von einer Liste in eine andere an eine einzige private Methode namens transfer :
// transfer
private void transfert(ListBox l1, Button button1vers2, Button buttonEffacer1, ListBox l2, Button button2vers1, Button buttonEffacer2) {
// transfer selected items from list l1 to list l2
for (int i = l1.SelectedIndices.Count - 1; i >= 0; i--) {
// index of selected item
int index = l1.SelectedIndices[i];
// addition to l2
l2.Items.Add(l1.Items[index]);
// deletion in l1
l1.Items.RemoveAt(index);
}
// delete buttons
buttonEffacer2.Enabled = l2.Items.Count != 0;
buttonEffacer1.Enabled = l1.Items.Count != 0;
// transfer buttons
button1vers2.Enabled = false;
}
- Zeile b: Die Methode „transfer“ erhält sechs Parameter:
- eine Referenz auf die Liste mit den ausgewählten Elementen, hier l1 genannt. Beim Ausführen der Anwendung ist l1 entweder listBox1 oder listBox2. Beispiele für Aufrufe finden sich in den Zeilen 3 und 8 der Transferprozedur buttonXversY_Click.
- eine Referenz auf die Schaltfläche „transfer“, die mit der Liste l1 verknüpft ist. Wenn l1 beispielsweise listBox2 ist, ist dies button2to1 (siehe Aufruf in Zeile 8)
- eine Referenz auf die Schaltfläche zum Löschen der Liste l1. Ist l1 beispielsweise listBox1, lautet sie buttonEffacer1 (siehe Aufruf in Zeile 3)
- Die anderen drei Verweise sind ähnlich, beziehen sich jedoch auf l2.
- Zeile d: Die [ListBox]-Sammlung.SelectedIndices stellt die Indizes der in der Komponente [ListBox] ausgewählten Elemente dar. Dies ist ein:
- [ListBox].SelectedIndices.Count ist die Anzahl der Elemente in dieser Sammlung
- [ListBox].SelectedIndices[i] ist Element Nr. i in dieser Sammlung
Wir durchlaufen die Sammlung in umgekehrter Reihenfolge, beginnend am Ende und endend am Anfang. Wir erklären, warum.
- Zeile f: Index eines ausgewählten Elements in der Liste l1
- Zeile h: Dieses Element wird zur Liste l2 hinzugefügt
- Zeile j: und aus der Liste l1 gelöscht. Da es gelöscht wurde, ist es nicht mehr ausgewählt. Die Sammlung l1.SelectedIndices der Zeile d wird neu berechnet. Das soeben gelöschte Element geht dabei verloren. Bei allen nachfolgenden Elementen ändert sich die Nummer von n auf n-1.
- Wenn die Schleife in Zeile (d) aufsteigend ist und gerade das Element Nr. 0 verarbeitet hat, wird sie anschließend das Element Nr. 1 verarbeiten. Oder das Element, das vor dem Löschen des Elements Nr. 0 die Nummer 1 hatte, wird dann die Nummer 0 haben. Es wird dann von der Schleife übersehen.
- Wenn die Schleife in Zeile (d) absteigend ist und gerade Element Nr. n verarbeitet hat, wird anschließend Element Nr. n-1 verarbeitet. Nach dem Löschen von Element Nr. n ändert sich die Nummer von Element Nr. n-1 nicht. Es wird daher in der nächsten Schleife verarbeitet.
- Zeilen m–n: Der Status der [Löschen]-Schaltflächen hängt davon ab, ob Elemente in den zugehörigen Listen vorhanden sind
- Zeile p: Die Liste l2 enthält keine ausgewählten Elemente mehr: Schalte ihre Übertragungsschaltfläche aus.
7.2.5. Kontrollkästchen CheckBox, Optionsfelder ButtonRadio
Wir schlagen vor, die folgende Anwendung zu schreiben:
![]() |
Die Fensterkomponenten sind wie folgt:
Nr. | Typ | Name | Rolle |
1 | GroupBox cf [6] | groupBox1 | Ein Komponenten-Container. Andere Komponenten können hier hinein gezogen werden. Text=Radiobuttons |
2 | RadioButton | radioButton1 radioButton2 radioButton3 | 3 Radiobuttons – radioButton1 hat Checked=True und Text=1 – radioButton2 hat Text=2 – radioButton3 hat Text=3 Radiobuttons im selben Container, hier der GroupBox, schließen sich gegenseitig aus: Nur einer von ihnen ist aktiviert. |
3 | GroupBox | groupBox2 | |
4 | CheckBox | CheckBox1 CheckBox2 checkBox3 | 3 Kontrollkästchen. checkBox1 hat Checked=True und Text=A – checkBox2 hat Text=B – checkBox3 hat Text=C |
5 | ListBox | listBoxValeurs | Eine Liste, die die Werte der Optionsfelder und Kontrollkästchen anzeigt, sobald eine Änderung erfolgt. |
6 | zeigt, wo sich der Container „GroupBox“ befindet |
Das für diese sechs Steuerelemente relevante Ereignis ist CheckChanged, das anzeigt, dass sich der Status des Kontrollkästchens oder des Optionsfelds geändert hat. In beiden Fällen wird dieser Status durch die boolesche Eigenschaft Checked dargestellt, was konkret bedeutet, dass das Steuerelement aktiviert ist. Wir verwenden hier nur eine Methode, um alle sechs CheckChanged-Ereignisse zu verarbeiten, nämlich die Methode poster. Um sicherzustellen, dass die sechs CheckChanged-Ereignisse von derselben Methode poster verarbeitet werden, können Sie wie folgt vorgehen:
Wählen Sie die Komponente „radioButton1“ aus und klicken Sie mit der rechten Maustaste darauf, um ihre Eigenschaften aufzurufen:
![]() |
In den Ereignissen [1] ordnen wir das Poster [2] dem Ereignis CheckChanged zu. Das bedeutet, dass wir möchten, dass der Klick auf die Option A1 durch eine Methode namens poster verarbeitet wird. Visual Studio generiert das Poster automatisch im Codefenster:
private void affiche(object sender, EventArgs e) {
}
Die Methode „poster“ ist ein EventHandler.
Für die anderen fünf Komponenten verfahren wir genauso. Wählen wir zum Beispiel die Option CheckBox1 und ihre Ereignisse [3] aus. Beim Ereignis Click sehen wir eine Dropdown-Liste [4] mit den vorhandenen Methoden, die dieses Ereignis verarbeiten können. In diesem Fall ist das nur die Methode affiche. Wir wählen sie aus. Wiederholen Sie diesen Vorgang für alle anderen Komponenten.
Der Code für die Methode InitializeComponent wurde generiert. Das Poster wurde wie folgt als Handler für die sechs CheckedChanged-Ereignisse deklariert:
this.radioButton1.CheckedChanged += new System.EventHandler(this.affiche);
this.radioButton2.CheckedChanged += new System.EventHandler(this.affiche);
this.radioButton3.CheckedChanged += new System.EventHandler(this.affiche);
this.checkBox1.CheckedChanged += new System.EventHandler(this.affiche);
this.checkBox2.CheckedChanged += new System.EventHandler(this.affiche);
this.checkBox3.CheckedChanged += new System.EventHandler(this.affiche);
Die Methode „poster“ wird wie folgt vervollständigt:
private void affiche(object sender, System.EventArgs e) {
// displays radio button or checkbox status
// is it a checkbox?
if (sender is CheckBox) {
CheckBox chk = (CheckBox)sender;
listBoxvaleurs.Items.Add(chk.Name + "=" + chk.Checked);
}
// is it a radiobutton?
if (sender is RadioButton) {
RadioButton rdb = (RadioButton)sender;
listBoxvaleurs.Items.Add(rdb.Name + "=" + rdb.Checked);
}
}
Die Syntax
if (sender is CheckBox) {
wird verwendet, um zu überprüfen, ob der Absendertyp CheckBox ist. Dies ermöglicht es uns dann, den Typ auf den genauen Absendertyp umzuwandeln. Die Methode poster schreibt in die listBoxValeurs den Namen der Komponente, die das Ereignis auslöst, sowie deren Eigenschaftswert Checked. Zur Laufzeit [7] löst ein Klick auf ein Optionsfeld zwei CheckChanged-Ereignisse aus: eines auf dem alten, markierten Feld, das auf „unmarked“ wechselt, und das andere auf dem neuen Feld, das auf „marked“ wechselt.
7.2.6. Inverter ScrollBar
Es gibt verschiedene Arten von Steuerelementen: den horizontalen Lauf (HscrollBar), der vertikale Lauf (VscrollBar), der Inkrementierer (NumericUpDown). |
Führen wir die folgende Anwendung aus:
![]() |
Nr. | Typ | Name | Rolle |
1 | hScrollBar | hScrollBar1 | a horizontale Leiste |
2 | hScrollBar | hScrollBar2 | ein horizontaler Variator, der den Schwankungen von Variator 1 folgt |
3 | Beschriftung | labelValeurHS1 | zeigt den Wert des horizontalen Antriebs an |
4 | NumericUpDown | numericUpDown2 | zum Einstellen des Wertes von Regler 2 |
Über eine ScrollBar mit Umschaltfunktion kann der Benutzer einen Wert aus einem Bereich ganzzahliger Werte auswählen, der durch das „Band“ des Antriebs symbolisiert wird, über das sich ein Cursor bewegt. Der Antriebswert ist in dessen „Value“-Feld verfügbar.
- Bei einem horizontalen Regler stellt das linke Ende den Minimalwert des Bereichs dar, das rechte Ende den Maximalwert und der Cursor den aktuell ausgewählten Wert. Bei einem vertikalen Regler wird der Minimalwert durch das obere Ende und der Maximalwert durch das untere Ende dargestellt. Diese Werte werden durch die Eigenschaften „Minimum“ und „Maximum“ repräsentiert und sind standardmäßig auf 0 bzw. 100 gesetzt.
- Ein Klick auf die Enden des Schiebereglers bewirkt eine Änderung des Wertes um einen Schritt (positiv oder negativ), je nachdem, auf welches Ende geklickt wird (SmallChange), dessen Standardwert 1 ist.
- Ein Klick auf eine der beiden Seiten des Cursors ändert den Wert um einen Schritt (positiv oder negativ), je nachdem, auf welches Ende geklickt wird (LargeChange), dessen Standardwert 10 ist.
- Wenn auf das obere Ende eines vertikalen Dimmers geklickt wird, verringert sich dessen Wert. Dies mag für den durchschnittlichen Benutzer überraschend sein, der normalerweise erwartet, dass der Wert „steigt“. Dieses Problem wird gelöst, indem den Eigenschaften „SmallChange“ und „LargeChange“ ein negativer Wert zugewiesen wird
- Diese fünf Eigenschaften (Value, Minimum, Maximum, SmallChange, LargeChange) sind zum Lesen und Schreiben zugänglich.
- Das Hauptereignis des Steuerelements ist dasjenige, das eine Wertänderung signalisiert: das Scroll.
Eine Komponente vom Typ „NumericUpDown“ ähnelt dem Steuerelement: Sie verfügt ebenfalls über die folgenden Eigenschaften: Minimum, Maximum und Value, deren Standardwerte 0, 100 und 0 sind. Hier wird der Wert jedoch in einem Eingabefeld angezeigt, das integraler Bestandteil des Steuerelements ist. Der Benutzer kann diesen Wert selbst ändern, sofern die Eigenschaft „ReadOnly“ nicht auf „true“ gesetzt ist. Der Inkrementwert wird durch die Eigenschaft Incremental festgelegt, der Standardwert ist 1. Das Hauptereignis der Komponente NumericUpDown ist dasjenige, das eine Wertänderung signalisiert: das Ereignis ValueChanged
Der Anwendungscode lautet wie folgt:
using System.Windows.Forms;
namespace Chap5 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
// set the characteristics of drive 1
hScrollBar1.Value = 7;
hScrollBar1.Minimum = 1;
hScrollBar1.Maximum = 130;
hScrollBar1.LargeChange = 11;
hScrollBar1.SmallChange = 1;
// drive 2 is given the same characteristics as drive 1
hScrollBar2.Value = hScrollBar1.Value;
hScrollBar2.Minimum = hScrollBar1.Minimum;
hScrollBar2.Maximum = hScrollBar1.Maximum;
hScrollBar2.LargeChange = hScrollBar1.LargeChange;
hScrollBar2.SmallChange = hScrollBar1.SmallChange;
// ditto for the incrementer
numericUpDown2.Value = hScrollBar1.Value;
numericUpDown2.Minimum = hScrollBar1.Minimum;
numericUpDown2.Maximum = hScrollBar1.Maximum;
numericUpDown2.Increment = hScrollBar1.SmallChange;
// the Label is given the value of drive 1
labelValeurHS1.Text = hScrollBar1.Value.ToString();
}
private void hScrollBar1_Scroll(object sender, ScrollEventArgs e) {
// value change on drive 1
// its value is passed on to drive 2 and to the label
hScrollBar2.Value = hScrollBar1.Value;
labelValeurHS1.Text = hScrollBar1.Value.ToString();
}
private void numericUpDown2_ValueChanged(object sender, System.EventArgs e) {
// incrementer has changed value
// set the value of controller 2
hScrollBar2.Value = (int)numericUpDown2.Value;
}
}
}
7.3. Ereignisse Maus
Beim Zeichnen in einem Container ist es wichtig, die Position der Maus zu kennen, damit beispielsweise beim Klicken ein Punkt angezeigt werden kann. Mausbewegungen lösen Ereignisse in dem Container aus, in dem sie sich bewegt.
![]() |
- [1]: Ereignisse, die auftreten, wenn die Maus über ein Formular oder ein Steuerelement bewegt wird
- [2]: Ereignisse, die beim Drag & Drop (Drag'nDrop) auftreten
Die Maus hat das Steuerelementfeld betreten | |
Die Maus hat gerade den Bereich des Steuerelements verlassen | |
Die Maus bewegt sich innerhalb des Steuerelements | |
Linke Maustaste gedrückt | |
Linke Maustaste loslassen | |
Der Benutzer legt ein Objekt auf dem Steuerelement ab | |
Der Benutzer betritt den Steuerelementbereich, indem er ein Objekt hineinzieht | |
Der Benutzer verlässt den Steuerelementbereich, indem er ein Objekt zieht | |
Der Benutzer fährt mit einem Objekt über den Steuerungsbereich |
Hier ist eine Anwendung, die Ihnen hilft zu verstehen, wann verschiedene Mausereignisse auftreten:
![]() |
Nr. | Typ | Name | Rolle |
1 | Bezeichnung | lblPositionSouris | um die Mausposition in Formular 1, Liste 2 oder Schaltfläche 3 anzuzeigen |
2 | ListBox | listBoxEvts | um andere Mausereignisse als MouseMove anzuzeigen |
3 | Button | buttonEffacer | zum Löschen des Inhalts von 2 |
Um die Mausbewegungen auf den drei Steuerelementen zu verfolgen, schreiben wir einen einzigen Handler, den Poster:
![]() |
Der Prozedurcode für „poster“ lautet wie folgt:
private void affiche(object sender, MouseEventArgs e) {
// mvt mouse - displays its (X,Y) coordinates
labelPositionSouris.Text = "(" + e.X + "," + e.Y + ")";
}
Jedes Mal, wenn die Maus in den Bereich eines Steuerelements gelangt, ändert sich ihr Koordinatensystem. Ihr Ursprung (0,0) ist die obere linke Ecke des Steuerelements, auf dem sie sich befindet. Wenn Sie also zur Laufzeit die Maus vom Formular auf die Schaltfläche bewegen, können Sie die Änderung der Koordinaten deutlich sehen. Um diese Änderungen im Mausbereich besser zu erkennen, können Sie die Cursor-Steuerelemente [1] verwenden:
![]() |
Diese Eigenschaft dient dazu, die Form des Mauszeigers festzulegen, wenn er den Bereich des Steuerelements betritt. In unserem Beispiel haben wir den Mauszeiger für das Formular selbst auf „Standard“ [2], für Liste 2 auf „Hand“ [3] und für Schaltfläche 3 auf „Kreuz“ [4] gesetzt.
Um zudem das Betreten und Verlassen von Liste 2 durch die Maus zu erkennen, verarbeiten wir die Ereignisse „MouseEnter“ und „MouseLeave“ aus derselben Liste:
private void listBoxEvts_MouseEnter(object sender, System.EventArgs e) {
// the event
listBoxEvts.Items.Insert(0, string.Format("MouseEnter à {0:hh:mm:ss}",DateTime.Now));
}
private void listBoxEvts_MouseLeave(object sender, EventArgs e) {
// the event
listBoxEvts.Items.Insert(0, string.Format("MouseLeave à {0:hh:mm:ss}", DateTime.Now));
}
Um Klicks auf das Formular zu verarbeiten, verarbeiten wir die folgenden Ereignisse: MouseDown und MouseUp:
private void listBoxEvts_MouseDown(object sender, MouseEventArgs e) {
// the event
listBoxEvts.Items.Insert(0, string.Format("MouseDown à {0:hh:mm:ss}", DateTime.Now));
}
private void listBoxEvts_MouseUp(object sender, MouseEventArgs e) {
// the event
listBoxEvts.Items.Insert(0, string.Format("MouseUp à {0:hh:mm:ss}", DateTime.Now));
}
- Zeilen 3 und 8: Die Meldungen werden an erster Stelle in der ListBox platziert, sodass die neuesten Ereignisse zuerst aufgeführt werden.
![]() |
Zum Schluss der Code für den Klick-Handler der Schaltfläche „Löschen“:
private void buttonEffacer_Click(object sender, EventArgs e) {
listBoxEvts.Items.Clear();
}
7.4. Erstelle ein Fenster mit Menü
Schauen wir uns nun an, wie man ein Fenster mit einem Menü erstellt. Wir erstellen das folgende Fenster:
![]() |
Um ein Menü zu erstellen, wählen Sie „MenuStrip“ in der Leiste „Menüs & Symbolleisten“ aus:
![]() |
- [1]: Auswahl der Komponente [MenuStrip]
- [2]: Auf dem Formular erscheint ein Menü mit leeren Feldern, die mit „Type Here“ beschriftet sind. Sie müssen lediglich die verschiedenen Menüoptionen angeben.
- [3]: Die Beschriftung „Option A“ wurde eingegeben. Wir fahren mit der Beschriftung [4] fort.
- [5]: Die Beschriftungen für Option A wurden eingegeben. Fahren Sie mit der Beschriftung [6] fort
![]() |
- [6]: Die ersten Optionen B
- [7]: Unter B1 wird ein Trennzeichen verwendet. Dieses ist in einem Kombinationsfeld mit dem Text „Hier eingeben“ verfügbar
- [8]: Um ein Untermenü zu erstellen, verwenden Sie die Pfeiltaste [8] und geben Sie das Untermenü in [9] ein
Nun müssen nur noch die verschiedenen Komponenten des Formulars benannt werden:
![]() |
Nr. | Typ | Name(n) | Rolle |
1 | Bezeichnung | labelStatut | um den Text des angeklickten Menüpunkts anzuzeigen |
2 | toolStripMenuItem | toolStripMenuItemOptionsA toolStripMenuItemA1 toolStripMenuItemA2 MenüelementA3 | Menüoptionen unter der Hauptoption „Optionen A“ |
3 | Menüpunkt der Symbolleiste | Menüelement-OptionenB MenüelementB1 MenüelementB2 toolStripMenuItemB3 | Menüoptionen unter der Hauptoption „Optionen B“ |
4 | Werkzeugleisten-Menüpunkt | MenüelementB31 toolStripMenuItemB32 | Menüoptionen unter der Hauptoption „B3“ |
Menüoptionen sind Steuerelemente wie andere visuelle Komponenten und verfügen über Eigenschaften und Ereignisse. Die Eigenschaften des Menüelements „A1“ lauten beispielsweise wie folgt:
![]() |
In unserem Beispiel werden zwei Eigenschaften verwendet:
Name des Menüsteuerelements | |
Bezeichnung der Menüoption |
Wählen Sie in der Menüstruktur die Option A1 aus und klicken Sie mit der rechten Maustaste, um die Eigenschaften des Steuerelements aufzurufen:
![]() |
In den Ereignissen [1] ordnen wir das Poster [2] dem Ereignis „Click“ zu. Das bedeutet, dass wir möchten, dass der Klick auf die Option A1 durch eine Methode namens „poster“ verarbeitet wird. Visual Studio generiert das Poster automatisch im Codefenster:
private void affiche(object sender, EventArgs e) {
}
In dieser Methode zeigen wir einfach im Label „labelStatut“ die Eigenschaft „Text“ des angeklickten Menüpunkts an:
private void affiche(object sender, EventArgs e) {
// displays the name of the selected submenu in the TextBox
labelStatut.Text = ((ToolStripMenuItem)sender).Text;
}
Der Typ des Ereignis-Senders ist „object“. Da die Menüoptionen vom Typ „ToolStripMenuItem“ sind, müssen wir das Objekt in „ToolStripMenuItem“ umwandeln.
Für alle Menüoptionen setzen wir den Klick-Handler auf die Methode poster [3,4].
Starten wir die Anwendung und wählen wir einen Menüpunkt aus:
![]() | ![]() |
7.5. Nicht-visuelle Komponenten
Wir wenden uns nun einer Reihe von nicht-visuellen Komponenten zu: Sie werden während der Entwicklung verwendet, sind aber zur Laufzeit nicht sichtbar.
7.5.1. Dialogfelder „ “, „OpenFileDialog“ und „SaveFileDialog“
Wir werden die folgende Anwendung erstellen:
![]() |
Die Steuerelemente sind wie folgt:
Nr. | Typ | Name | Rolle |
1 | TextBox | TextBoxLines | vom Benutzer eingegebener oder aus einer Datei geladener Text MultiLine=True, ScrollBars=Both, AccepReturn=True, AcceptTab=True |
2 | Schaltfläche | buttonSauvegarder | speichert den Text von [1] in einer Textdatei |
3 | Schaltfläche | SchaltflächeCharger | Lädt den Inhalt einer Textdatei in [1] |
4 | Schaltfläche | buttonEffacer | löscht den Inhalt von [1] |
5 | SaveFileDialog | saveFileDialog1 | Komponente zur Auswahl des Namens und des Speicherorts der Sicherungsdatei für [1]. Diese Komponente wird aus der Symbolleiste [7] entnommen und einfach auf das Formular platziert. Sie wird dann gespeichert, nimmt aber keinen Platz auf dem Formular ein. Es handelt sich um eine nicht-visuelle Komponente. |
6 | OpenFileDialog | openFileDialog1 | Komponente zur Auswahl der Datei, die in [1] geladen werden soll. |
Der mit dem Löschen verbundene Code ist einfach:
private void buttonEffacer_Click(object sender, EventArgs e) {
// we put the empty string in the TexBox
textBoxLignes.Text = "";
}
Wir werden die folgenden Eigenschaften und Methoden des SaveFileDialog verwenden:
Feld | Typ | Rolle |
Eigenschaft | die Dateitypen, die in der Dropdown-Liste „Dateityp“ im | |
Eigenschaft | Die Nummer des Dateityps, der standardmäßig in der obigen Liste vorgeschlagen wird. Beginnt bei 0. | |
Eigenschaft | Der ursprünglich zum Speichern der Datei angezeigte Ordner | |
Eigenschaft | Der vom Benutzer angegebene Name der Sicherungsdatei | |
Methode | Methode, die das Dialogfeld zum Speichern anzeigt. Gibt ein Ergebnis vom Typ DialogResult zurück. |
Die Methode „ShowDialog“ zeigt ein Dialogfeld an, das dem unten abgebildeten ähnelt:
![]() |
Dropdown-Liste, die aus dem Filter erstellt wurde. Der Standarddateityp wird durch FilterIndex festgelegt | |
aktuelle Datei, festgelegt durch InitialDirectory, sofern diese Eigenschaft gesetzt wurde | |
vom Benutzer ausgewählter oder direkt eingegebener Dateiname. Wird in der Eigenschaft „FileName“ verfügbar sein | |
Schaltflächen „Speichern“/„Abbrechen“. Bei Verwendung des Registers gibt ShowDialog das Ergebnis DialogResult.OK zurück |
Die Schutzprozedur kann wie folgt geschrieben werden:
private void buttonSauvegarder_Click(object sender, System.EventArgs e) {
// save the input box in a text file
// set the savefileDialog1 dialog box
saveFileDialog1.InitialDirectory = Application.ExecutablePath;
saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*";
saveFileDialog1.FilterIndex = 0;
// display the dialog box and retrieve the result
if (saveFileDialog1.ShowDialog() == DialogResult.OK) {
// retrieve the file name
string nomFichier = saveFileDialog1.FileName;
StreamWriter fichier = null;
try {
// open the file for writing
fichier = new StreamWriter(nomFichier);
// we write the text inside
fichier.Write(textBoxLignes.Text);
} catch (Exception ex) {
// problem
MessageBox.Show("Problème à l'écriture du fichier (" +
ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
} finally {
// close the file
if (fichier != null) {
fichier.Dispose();
}
}
}
}
- Zeile 4: Setze die Startdatei (InitialDirectory) auf die Datei (Application.ExecutablePath), die die ausführbare Datei der Anwendung enthält.
- Zeile 5: Legen Sie die anzuzeigenden Dateitypen fest. Beachten Sie die Filtersyntax: filter1|filter2|..|filtren mit filteri= Text|Dateimuster. Hier kann der Benutzer zwischen den folgenden Dateitypen wählen: *.txt und *.*.
- Zeile 6: Legen Sie den Dateityp fest, der dem Benutzer zuerst angezeigt werden soll. Hier bezeichnet der Index 0 *.txt-Dateien.
- Zeile 8: Das Dialogfeld wird angezeigt und sein Ergebnis abgerufen. Während das Dialogfeld angezeigt wird, hat der Benutzer keinen Zugriff mehr auf das Hauptformular (modales Dialogfeld). Der Benutzer legt den Namen der zu speichernden Datei fest und verlässt das Dialogfeld entweder durch Klicken auf „Speichern“, über „Abbrechen“ oder durch Schließen des Fensters. Das Ergebnis von ShowDialog ist nur dann DialogResult.OK, wenn der Benutzer „Speichern“ verwendet hat, um das Dialogfeld zu verlassen.
- Sobald dies geschehen ist, befindet sich der Name der zu erstellenden Datei nun im FileName-Objekt saveFileDialog1. Dies bringt uns zurück zur klassischen Erstellung einer Textdatei. Der Inhalt des TextBox-Objekts: textBoxLignes.Text, wobei eventuell auftretende Ausnahmen behandelt werden.
Die Klasse OpenFileDialog ist der Klasse SaveFileDialog sehr ähnlich. Wir verwenden dieselben Methoden und Eigenschaften wie oben. Die Methode ShowDialog zeigt ein Dialogfeld an, das dem unten abgebildeten ähnelt:
![]() |
Dropdown-Liste, die aus dem Filter erstellt wird. Der Standarddateityp wird durch FilterIndex festgelegt | |
aktuelle Datei, festgelegt durch „InitialDirectory“, sofern diese Eigenschaft gesetzt wurde | |
vom Benutzer ausgewählter oder direkt eingegebener Dateiname. Wird in der Eigenschaft „FileName“ verfügbar sein | |
Schaltflächen „Öffnen“/„Abbrechen“. Wenn „Öffnen“ verwendet wird, setzt ShowDialog das Ergebnis auf DialogResult.OK |
Die Prozedur zum Laden der Textdatei kann wie folgt geschrieben werden:
private void buttonCharger_Click(object sender, EventArgs e) {
// load a text file into the input box
// set the openfileDialog1 dialog box
openFileDialog1.InitialDirectory = Application.ExecutablePath;
openFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*";
openFileDialog1.FilterIndex = 0;
// display the dialog box and retrieve the result
if (openFileDialog1.ShowDialog() == DialogResult.OK) {
//
string nomFichier = openFileDialog1.FileName;
StreamReader fichier = null;
try {
// retrieve the file name
fichier = new StreamReader(nomFichier);
// open the file in read mode
textBoxLignes.Text = fichier.ReadToEnd();
} catch (Exception ex) {
// read the entire file and put it in the TextBox
MessageBox.Show("Problème à la lecture du fichier (" +
ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
} finally {
// problem
if (fichier != null) {
fichier.Dispose();
}
}// close the file
}//finally
}
- Zeile 4: Setze die Startdatei (InitialDirectory) auf die Datei (Application.ExecutablePath), die die ausführbare Datei der Anwendung enthält.
- Zeile 5: Legen Sie die anzuzeigenden Dateitypen fest. Beachten Sie die Filtersyntax: filter1|filter2|..|filtren mit filteri= Text|Dateimuster. Hier kann der Benutzer zwischen den folgenden Dateitypen wählen: *.txt und *.*.
- Zeile 6: Lege den Dateityp fest, der dem Benutzer zuerst angezeigt werden soll. Hier bezeichnet der Index 0 *.txt-Dateien.
- Zeile 8: Das Dialogfeld wird angezeigt und sein Ergebnis abgerufen. Während das Dialogfeld angezeigt wird, hat der Benutzer keinen Zugriff mehr auf das Hauptformular (modales Dialogfeld). Der Benutzer legt den Namen der zu speichernden Datei fest und verlässt das Dialogfeld entweder durch Klicken auf „Öffnen“, über „Abbrechen“ oder durch Schließen des Fensters. Das Ergebnis von ShowDialog ist nur dann DialogResult.OK, wenn der Benutzer „OK“ verwendet hat, um das Dialogfeld zu verlassen.
- Sobald dies geschehen ist, befindet sich der Name der zu erstellenden Datei nun im FileName-Objekt openFileDialog1. Dies bringt uns zurück zum klassischen Lesen einer Textdatei. Beachten Sie in Zeile 16 die Methode zum Lesen einer gesamten Datei.
7.5.2. Dialogfelder „FontColor“ und „ColorDialog“
Wir setzen das vorherige Beispiel fort und fügen zwei neue Schaltflächen sowie zwei neue nicht-visuelle Steuerelemente hinzu:
![]() |
67
Nr. | Typ | Name | Rolle |
1 | Schaltfläche | buttonCouleur | zum Festlegen der Schriftfarbe des Textfelds |
2 | Schaltfläche | buttonPolice | um die Schriftart für TextBox festzulegen |
3 | ColorDialog | colorDialog1 | die Komponente zur Farbauswahl – aus der Toolbox [5]. |
4 | FontDialog | colorDialog1 | die Komponente zur Schriftartauswahl – aus der Toolbox [5]. |
Die Klassen FontDialog und ColorDialog verfügen über eine Methode ShowDialog, ähnlich wie die ShowDialog-Klassen OpenFileDialog und SaveFileDialog.
![]() |
Die Methode ShowDialog der Klasse ColorDialog dient zur Auswahl einer Farbe [1]. Die Klasse FontDialog dient zur Auswahl einer Schriftart [2]:
- [1]: Wenn der Benutzer das Dialogfeld mit „OK“ verlässt, ist das Ergebnis der Methode ShowDialog DialogResult.OK und die ausgewählte Farbe befindet sich im Color-Objekt der verwendeten Klasse ColorDialog.
- [2]: Wenn der Benutzer das Dialogfeld mit „OK“ verlässt, ist das Ergebnis der Methode ShowDialog DialogResult.OK und die ausgewählte Schriftart befindet sich im Font-Objekt von FontDialog.
Wir verfügen nun über die Elemente, die wir zur Verarbeitung von Klicks auf die Schaltflächen „Farbe“ und „Schriftart“ benötigen:
private void buttonCouleur_Click(object sender, EventArgs e) {//if
if (colorDialog1.ShowDialog() == DialogResult.OK) {
// choice of text color
textBoxLignes.ForeColor = colorDialog1.Color;
}// change the Forecolor property of TextBox
}
private void buttonPolice_Click(object sender, EventArgs e) {
//if
if (fontDialog1.ShowDialog() == DialogResult.OK) {
// font selection
textBoxLignes.Font = fontDialog1.Font;
}
- Zeile [4]: Die Eigenschaft [ForeColor] einer TextBox-Komponente legt die Farbe der Zeichen in der TextBox fest. Hier handelt es sich um die Farbe, die der Benutzer im Dialogfeld [ColorDialog] ausgewählt hat.
- Zeile [12]: Die Eigenschaft [Font] einer TextBox-Komponente legt die Schriftart [Font] der Zeichen in der TextBox fest. Hier ist diese Schriftart diejenige, die der Benutzer im Dialogfeld [FontDialog] ausgewählt hat.
7.5.3. Timer
Wir schlagen hier vor, die folgende Anwendung zu schreiben:
![]() |
Nr. | Typ | Name | Rolle |
1 | Bezeichnung | labelChrono | zeigt eine Stoppuhr an |
2 | Schaltfläche | buttonArretMarche | Stopp-/Start-Taste |
3 | Timer | Timer1 | Komponente, die hier jede Sekunde ein Ereignis auslöst |
In [4] sehen wir, dass die Stoppuhr läuft, in [5] ist die Stoppuhr angehalten.
Um den Inhalt des Labels „LabelChrono“ jede Sekunde zu ändern, benötigen wir eine Komponente, die jede Sekunde ein Ereignis generiert, das wir abfangen können, um die Anzeige der Stoppuhr zu aktualisieren. Diese Komponente ist der Timer [1], der in der Toolbox „Komponenten“ [2] verfügbar ist:
![]() |
Die Eigenschaften der hier verwendeten Timer-Komponente lauten wie folgt:
Anzahl der Millisekunden, nach denen ein Tick-Ereignis ausgelöst wird. | |
Das Ereignis, das am Ende des Intervalls in Millisekunden ausgelöst wird | |
macht den Timer aktiv (true) oder inaktiv (false) |
In unserem Beispiel heißt der Timer „timer1“ und „timer1.Interval“ ist auf 1000 ms (1 s) eingestellt. Das Ereignis „Tick“ tritt daher jede Sekunde auf. Ein Klick auf die Schaltfläche „Stop/Start“ wird von der Prozedur „buttonArretMarche_Click“ verarbeitet:
using System;
using System.Windows.Forms;
namespace Chap5 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
// change the Font property of TextBox
private DateTime début = DateTime.Now;
...
private void buttonArretMarche_Click(object sender, EventArgs e) {
// variable instance
if (buttonArretMarche.Text == "Marche") {
// off or on?
début = DateTime.Now;
// we note the start time
labelChrono.Text = "00:00:00";
// we display it
timer1.Enabled = true;
// start timer
buttonArretMarche.Text = "Arrêt";
// change the button label
return;
}// end
if (buttonArretMarche.Text == "Arrêt") {
// timer off
timer1.Enabled = false;
// change the button label
buttonArretMarche.Text = "Marche";
// end
return;
}
}
}
}
- Zeile 13: Die Prozedur, die den Klick auf die Ein-/Aus-Schaltfläche verarbeitet.
- Zeile 15: Die Beschriftung der Stopp-/Start-Schaltfläche lautet entweder „Stopp“ oder „Start“. Wir müssen daher diese Beschriftung prüfen, um zu wissen, was zu tun ist.
- Zeile 17: Im Fall von „Marche“ speichern wir die Startzeit in einer Variablen „start“, die eine globale Variable (Zeile 11) des Formularobjekts ist
- Zeile 19: Initialisiert den Inhalt des Labels „LabelChrono“
- Zeile 21: Timer gestartet (Enabled=true)
- Zeile 23: Die Beschriftung der Schaltfläche wird in „Stop“ geändert.
- Zeile 27: im Fall von „Arrêt“ (Stopp)
- Zeile 29: Timer angehalten (Enabled=false)
- Zeile 31: Beschriftung der Schaltfläche in „Ein“ ändern.
Wir müssen uns noch mit dem Ereignis „Tick“ des Objekts „timer1“ befassen, einem Ereignis, das jede Sekunde auftritt:
private void timer1_Tick(object sender, EventArgs e) {
// a second has passed
DateTime maintenant = DateTime.Now;
TimeSpan durée = maintenant - début;
// update the stopwatch
labelChrono.Text = durée.Hours.ToString("d2") + ":" + durée.Minutes.ToString("d2") + ":" + durée.Seconds.ToString("d2");
}
- Zeile 3: Notiere die Uhrzeit
- Zeile 4: Berechnet die seit dem Start der Stoppuhr verstrichene Zeit. Das Ergebnis ist ein Objekt vom Typ TimeSpan, das eine Zeitdauer darstellt.
- Zeile 6: Diese muss in der Stoppuhr im Format hh:mm:ss angezeigt werden. Dazu verwenden wir die TimeSpan-Objekte Hours, Minutes und Seconds, die jeweils die Stunden, Minuten und Sekunden der Dauer repräsentieren, die wir im Format ToString("d2") anzeigen, um 2 Stellen darzustellen.
7.6. Anwendungsbeispiel – „ “ Version 6
Wir nehmen die Beispielanwendung IMPOTS. Die neueste Version wurde in Abschnitt 6.4 behandelt. Es handelte sich um die folgende dreischichtige Anwendung:
![]() |
- Die Schichten [metier] und [dao] waren in einer DLL gekapselt
- die [ui]-Schicht war eine [console]-Schicht
- Die Instanziierung der Schichten und die Integration in die Anwendung wurden von Spring übernommen.
In dieser neuen Version wird die [ui]-Schicht durch die folgende grafische Benutzeroberfläche bereitgestellt:
![]() |
7.6.1. Die Visual Studio-Lösung
Die Visual Studio-Lösung besteht aus den folgenden Komponenten:
![]() |
- [1]: Das Projekt besteht aus den folgenden Elementen:
- [Program.cs]: die Klasse, die die Anwendung startet
- [Form1.cs]: Klasse des ersten Formulars
- [Form2]: die Klasse des zweiten Formulars
- [lib] (siehe [2]): Alle für das Projekt erforderlichen DLLs wurden hinzugefügt:
- [ImpotsV5-dao.dll]: die DLL der [dao]-Schicht, die in Abschnitt 6.4.3 generiert wurde;
- [ImpotsV5-metier.dll]: die DLL der [dao]-Schicht, die in Abschnitt 6.4.4 generiert wurde;
- [Spring.Core.dll], [Common.Logging.dll], [antlr.runtime.dll]: die bereits in der vorherigen Version verwendeten DLLs von Spring (siehe Abschnitt 6.4.6).
- [Referenzen] – wie in [3] beschrieben: Projektreferenzen. Für jede DLL in der Datei [lib] wurde eine Referenz hinzugefügt
- [App.config]: die Projektkonfigurationsdatei. Sie ist identisch mit der in Abschnitt 6.4.6 beschriebenen Version;
- [DataImport.txt]: die Steuerklassen-Datei, die so konfiguriert ist, dass sie automatisch in den Projekt-Ausführungsordner [4] kopiert wird
Das Formular [Form1] ist das Formular zur Eingabe der Steuerberechnungsparameter [A], das bereits oben vorgestellt wurde. Das Formular [Form2] [B] dient zur Anzeige einer Fehlermeldung:
![]() |
7.6.2. Die [Klasse Program.cs]
Die Klasse [Program.cs] startet die Anwendung. Ihr Code lautet wie folgt:
using System;
using System.Windows.Forms;
using Spring.Context;
using Spring.Context.Support;
using Metier;
using System.Text;
namespace Chap5 {
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
// code generated by Vs
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// --------------- Developer code
// instantiations [metier] and [dao] layers
IApplicationContext ctx = null;
Exception ex = null;
IImpotMetier metier = null;
try {
// spring context
ctx = ContextRegistry.GetContext();
// a reference is requested on the [metier] layer
metier = (IImpotMetier)ctx.GetObject("metier");
} catch (Exception e1) {
// memory exception
ex = e1;
}
// form to display
Form form = null;
// was there an exception?
if (ex != null) {
// yes - create the error message to be displayed
StringBuilder msgErreur = new StringBuilder(String.Format("Chaîne des exceptions : {0}{1}", "".PadLeft(40, '-'), Environment.NewLine));
Exception e = ex;
while (e != null) {
msgErreur.Append(String.Format("{0}: {1}{2}", e.GetType().FullName, e.Message, Environment.NewLine));
msgErreur.Append(String.Format("{0}{1}", "".PadLeft(40, '-'), Environment.NewLine));
e = e.InnerException;
}
// creation of an error window to which the error message to be displayed is passed
Form2 form2 = new Form2();
form2.MsgErreur = msgErreur.ToString();
// this will be the window to display
form = form2;
} else {
// all went well
// creation of a graphical interface [Form1] to which we pass the reference on the [metier] layer
Form1 form1 = new Form1();
form1.Metier = metier;
// this will be the window to display
form = form1;
}
// window display
Application.Run(form);
}
}
}
Der von Visual Studio generierte Code ist ab Zeile 19 vollständig. Die Anwendung verwendet die [ App.config] wie folgt:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<object name="dao" type="Dao.FileImpot, ImpotsV5-dao">
<constructor-arg index="0" value="DataImpot.txt"/>
</object>
<object name="metier" type="Metier.ImpotMetier, ImpotsV5-metier">
<constructor-arg index="0" ref="dao"/>
</object>
</objects>
</spring>
</configuration>
- Zeilen 24–32: Verwendung der vorherigen [App.config]-Datei zur Instanziierung der [Metier]- und [DAO]-Schichten
- Zeile 26: Verwendung der Datei [App.config]
- Zeile 28: Abrufen einer Referenz aus der [metier]-Schicht
- Zeile 31: Ausnahme gespeichert
- Zeile 34: Das Referenzformular bestimmt das anzuzeigende Formular (Form1 oder Form2)
- Zeilen 36–50: Wenn eine Ausnahme ausgelöst wurde, bereiten wir die Anzeige eines Formulars vom Typ [Form2] vor
- Zeilen 38–44: Erstellen der anzuzeigenden Fehlermeldung. Sie setzt sich aus der Verkettung der Fehlermeldungen der verschiedenen Ausnahmen in der Ausnahmekette zusammen.
- Zeile 46: Es wird ein Formular vom Typ [Form2] erstellt.
- Zeile 47: Wie wir später sehen werden, ist dieses Formular eine öffentliche Eigenschaft MsgErreur, die die anzuzeigende Fehlermeldung darstellt:
public string MsgErreur { private get; set; }
Diese Eigenschaft wird eingetragen.
- Zeile 49: Das Referenzformular, das das anzuzeigende Fenster bezeichnet, wird initialisiert. Beachten Sie den hier zum Tragen kommenden Polymorphismus. form2 ist nicht vom Typ [Form], sondern vom Typ [Form2], einem von [Form] abgeleiteten Typ.
- Zeilen 50–57: keine Ausnahme. Wir bereiten die Anzeige eines Formulars vom Typ [Form1] vor.
- Zeile 53: Ein Formular vom Typ [Form1] wird erstellt.
- Zeile 54: Wie wir später sehen werden, ist dieses Formular eine öffentliche Eigenschaft Trade, die eine Referenz auf die [metier]-Ebene darstellt:
public IImpotMetier Metier { private get; set; }
Diese Eigenschaft wird eingetragen.
- Zeile 56: Das Referenzformular, das das anzuzeigende Fenster bezeichnet, wird initialisiert. Auch hier kommt Polymorphismus zum Tragen. form1 ist nicht vom Typ [Form], sondern vom Typ [Form1], einem von [Form] abgeleiteten Typ.
- Zeile 59: Das von form referenzierte Fenster wird angezeigt.
7.6.3. Das Formular [Form1]
Im [Design]-Modus sieht das Formular [Form1] wie folgt aus:
![]() |
Die Steuerelemente sind wie folgt
Nr. | Typ | Name | Rolle |
0 | GroupBox | groupBox1 | Text=Sind Sie verheiratet? |
1 | RadioButton | radioButtonOui | aktiviert, wenn verheiratet |
2 | RadioButton | radioButtonNon | aktiviert, wenn nicht verheiratet Markiert=True |
3 | NumericUpDown | numericUpDownEnfants | Anzahl der Kinder Minimum=0, Maximum=20, Schrittweite=1 |
4 | Textfeld | textSalaire | Jahresgehalt des Steuerpflichtigen in Euro |
5 | Bezeichnung | labelImpot | Steuerbetrag BorderStyle=Fixed3D |
6 | Schaltfläche | SchaltflächeBerechnen | startet die Steuerberechnung |
7 | Schaltfläche | Schaltfläche Löschen | versetzt das Formular in den Zustand, in dem es sich beim Laden befand |
8 | Schaltfläche | buttonExit | zum Beenden der Anwendung |
Regeln für die Formularbedienung
- Die Schaltfläche „Berechnen“ bleibt deaktiviert, solange das Lohnfeld leer ist
- Wenn sich bei der Berechnung herausstellt, dass das Gehalt falsch ist, wird der Fehler gemeldet [9]
Der Klassencode lautet wie folgt:
using System.Windows.Forms;
using Metier;
using System;
namespace Chap5 {
public partial class Form1 : Form {
// business] layer
public IImpotMetier Metier { private get; set; }
public Form1() {
InitializeComponent();
}
private void buttonCalculer_Click(object sender, System.EventArgs e) {
// is the salary correct?
int salaire;
bool ok=int.TryParse(textSalaire.Text.Trim(), out salaire);
if (! ok || salaire < 0) {
// error msg
MessageBox.Show("Salaire incorrect", "Erreur de saisie", MessageBoxButtons.OK, MessageBoxIcon.Error);
// back to the wrong field
textSalaire.Focus();
// select text for input field
textSalaire.SelectAll();
// back to input interface
return;
}
// salary is correct - tax can be calculated
labelImpot.Text = Metier.CalculerImpot(radioButtonOui.Checked, (int)numericUpDownEnfants.Value, salaire).ToString();
}
private void buttonQuitter_Click(object sender, System.EventArgs e) {
Environment.Exit(0);
}
private void buttonEffacer_Click(object sender, System.EventArgs e) {
// raz form
labelImpot.Text = "";
numericUpDownEnfants.Value = 0;
textSalaire.Text = "";
radioButtonNon.Checked = true;
}
private void textSalaire_TextChanged(object sender, EventArgs e) {
// calculate] button status
buttonCalculer.Enabled=textSalaire.Text.Trim()!="";
}
}
}
Wir kommentieren nur die wichtigen Teile:
- Zeile [8]: Die öffentliche Eigenschaft Trade ermöglicht es der Startklasse [Program.cs], eine Referenz auf die [metier]-Schicht in [Form1] einzufügen.
- Zeile [14]: Prozedur zur Steuerberechnung
- Zeilen 15–27: Überprüfung der Gültigkeit des Gehalts (eine ganze Zahl >= 0).
- Zeile 29: Steuerberechnung unter Verwendung der Methode [CalculerImpot] der [metier]-Schicht. Beachten Sie die Einfachheit dieses Vorgangs, die durch die Kapselung der [metier]-Schicht in einer DLL erreicht wird.
7.6.4. Das [Formular Form2]
Im [Entwurfsmodus] sieht das Formular [Form2] wie folgt aus:
![]() |
Die Steuerelemente sind wie folgt
Nr. | Typ | Name | Rolle |
1 | Textfeld | Fehler-Textfeld | Mehrzeilig=True, Bildlaufleisten=Beide |
Der Klassencode lautet wie folgt:
using System.Windows.Forms;
namespace Chap5 {
public partial class Form2 : Form {
// error msg
public string MsgErreur { private get; set; }
public Form2() {
InitializeComponent();
}
private void Form2_Load(object sender, System.EventArgs e) {
// error msg is displayed
textBoxErreur.Text = MsgErreur;
// deselect all text
textBoxErreur.Select(0, 0);
}
}
}
- Zeile 6: Die öffentliche Eigenschaft MsgErreur ermöglicht es der Startklasse [Program.cs], die anzuzeigende Fehlermeldung in [Form2] einzufügen. Diese Meldung wird beim Laden angezeigt, Zeilen 12–16.
- Zeile 14: Die Fehlermeldung wird in das Textfeld eingefügt
- Zeile 16: Die im vorherigen Schritt getroffene Auswahl wird aufgehoben. [TextBox].Select(début,longueur) wählt (markiert) eine Länge von Zeichen ab Zeichen-Nr. début aus. [TextBox].Select(0,0) entspricht der Aufhebung der Auswahl des gesamten Textes.
7.6.5. Fazit
Werfen wir einen Blick zurück auf die verwendete dreischichtige Architektur:
![]() |
Diese Architektur ermöglichte es uns, die bestehende [ui]-Schicht durch eine grafische Implementierung zu ersetzen, ohne die [metier]- und [dao]-Schichten zu verändern. Wir konnten uns auf die [ui]-Schicht konzentrieren, ohne uns Gedanken über mögliche Auswirkungen auf die anderen Schichten machen zu müssen. Dies ist der Hauptvorteil von 3-Schicht-Architekturen. Ein weiteres Beispiel sehen wir später, wenn die [dao]-Schicht, die derzeit Daten aus einer Textdatei verwendet, durch eine [dao]-Schicht ersetzt wird, die Daten aus einer Datenbank nutzt. Wie wir sehen werden, hat dies keine Auswirkungen auf die [ui]- und [metier]-Schichten.


































































