Skip to content

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:

Hintergrundfarbe
zum Festlegen der Hintergrundfarbe des Fensters
ForeColor
zum Festlegen der Farbe von Zeichnungen oder Text im Fenster
Menu
um dem Fenster ein Menü zuzuordnen
Text
um dem Fenster einen Titel zu geben
FormBorderStyle
um den Fenstertyp festzulegen
Font
zum Festlegen der Schriftart für die Anzeige im
Name
um den Fensternamen festzulegen

Hier legen wir die Eigenschaften Text und Name fest:

Text
Eingabefelder und Schaltflächen – 1
Name
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:

  1. 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)
  2. 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

text
die anzuzeigende Meldung
Beschriftung
Fenstertitel
Schaltflächen
Schaltflächen im Fenster
Symbol
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):

Konstante
Schaltflächen
   AbbrechenWiederholenIgnorieren
  OK
    OKAbbrechen
    Erneut versuchenAbbrechen
    JaNein
    JaNeinAbbrechen

Das Symbol kann seine Werte aus den folgenden Konstanten beziehen (mit dem Präfix „MessageBoxIcon“, wie in Zeile 10 oben gezeigt):

Sternchen
Fehler
idem Stopp
Ausrufezeichen
idem Warnung
Hand
Informationen
idem Sternchen
Keine
Frage
Stopp
idem Hand
Warnung

 

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:

Image

Um herauszufinden, welche Schaltfläche der Benutzer zum Schließen des MessageBox-Fensters gedrückt hat, schreiben wir:

DialogResult res=MessageBox.Show(..);
if (res==DialogResult.Yes){ // il a appuyé sur le bouton oui...}

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:

EventHandler evtHandler=new EventHandler(buttonAfficher_Click);

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:

evtHandler+=new EvtHandler(M);

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:

public event MouseEventHandler MouseMove;

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.

Laden
Das Formular wird geladen
Schließen
Das Formular wird geschlossen
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:

Multiline=true
, um mehrere Zeilen Text zuzulassen
ScrollBars=(None, Horizontal, Vertical, Both)
um festzulegen, ob das Steuerelement Bildlaufleisten haben soll (Horizontal, Vertical, Both) oder nicht (None)
AcceptReturn=(True, False)
Wenn „true“, springt die Eingabetaste zur Zeile
AcceptTab=(True, False)
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:

Einfach
Nicht-Dropdown-Liste mit Bearbeitungsfeld
DropDown
Dropdown-Liste mit Bearbeitungsfeld
DropDownList
Dropdown-Liste ohne Bearbeitungsfeld

Standardmäßig ist der Typ einer ComboBox „DropDown“.

Die Klasse „ComboBox“ eines einzelnen Herstellers:

new ComboBox()
erstellt eine leere Combobox

Die Elemente der ComboBox sind in der Eigenschaft Items verfügbar:

public ComboBox.ObjectCollection Items {get;}

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:

C.Items.Count
Anzahl der Combo-Elemente
C.Items[i]
Element i der Combobox
C.Add(Objekt o)
fügt Objekt o als letztes Element der Kombinationsliste hinzu
C.AddRange(object[] objets)
fügt ein Array von Objekten am Ende einer Combobox hinzu
C.Insert(int i, object o)
fügt das Objekt o an Position i der Combobox ein
C.RemoveAt(int i)
entfernt Element i aus der Combobox
C.Remove(Objekt o)
entfernt das Objekt o aus der Combobox
C.Clear()
löscht alle Elemente der Combobox
C.IndexOf(Objekt o)
gibt die Position i des Objekts o in der Combobox zurück
C.SelectedIndex
Index des ausgewählten Elements
C.SelectedItem
ausgewähltes Element
C.SelectedItem.Text
Text, der für das ausgewählte Element angezeigt wird
C.Text
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:

Es
nur ein Element kann ausgewählt werden
MultiExtended
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.
MultiSimple
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
MouseEnter
Die Maus hat das Steuerelementfeld betreten
MouseLeave
Die Maus hat gerade den Bereich des Steuerelements verlassen
MouseMove
Die Maus bewegt sich innerhalb des Steuerelements
MouseDown
Linke Maustaste gedrückt
MouseUp
Linke Maustaste loslassen
DragDrop
Der Benutzer legt ein Objekt auf dem Steuerelement ab
DragEnter
Der Benutzer betritt den Steuerelementbereich, indem er ein Objekt hineinzieht
DragLeave
Der Benutzer verlässt den Steuerelementbereich, indem er ein Objekt zieht
DragOver
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
Name des Menüsteuerelements
Text
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
Zeichenkette Filter
Eigenschaft
die Dateitypen, die in der Dropdown-Liste „Dateityp“ im
int FilterIndex
Eigenschaft
Die Nummer des Dateityps, der standardmäßig in der obigen Liste vorgeschlagen wird. Beginnt bei 0.
Zeichenkette InitialDirectory
Eigenschaft
Der ursprünglich zum Speichern der Datei angezeigte Ordner
Zeichenkette Dateiname
Eigenschaft
Der vom Benutzer angegebene Name der Sicherungsdatei
DialogResult.ShowDialog()
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:

1
Dropdown-Liste, die aus dem Filter erstellt wurde. Der Standarddateityp wird durch FilterIndex festgelegt
2
aktuelle Datei, festgelegt durch InitialDirectory, sofern diese Eigenschaft gesetzt wurde
3
vom Benutzer ausgewählter oder direkt eingegebener Dateiname. Wird in der Eigenschaft „FileName“ verfügbar sein
4
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:

1
Dropdown-Liste, die aus dem Filter erstellt wird. Der Standarddateityp wird durch FilterIndex festgelegt
2
aktuelle Datei, festgelegt durch „InitialDirectory“, sofern diese Eigenschaft gesetzt wurde
3
vom Benutzer ausgewählter oder direkt eingegebener Dateiname. Wird in der Eigenschaft „FileName“ verfügbar sein
4
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:

Intervall
Anzahl der Millisekunden, nach denen ein Tick-Ereignis ausgelöst wird.
Tick
Das Ereignis, das am Ende des Intervalls in Millisekunden ausgelöst wird
Aktiviert
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.