Forum: PC-Programmierung C#: OnPaint und Console.ReadLine


von Michael S. (rbs_phoenix)


Lesenswert?

Hallo zusammen. Ich studiere Eletronik und habe u.A. das Fach 
Windowsprogrammierung, welches auf C# beruht und inklusive 
Übungsaufgaben ist.

Im Laufe der Übungen hat sich das Folgende (vereinfacht) Ergeben:

Datei main.cs:
1
class Hauptklasse{
2
    public static void Main(){
3
        Unterklasse u = new Unterklasse();
4
        u.Lauf();
5
    }
6
}

Datei unter.cs:
1
class Unterklasse{
2
    public void Lauf(){
3
        string str = "";
4
        Console.WriteLine("Eingabe:");
5
        while(str != "beenden"){
6
            str = Console.ReadLine();
7
            Console.Clear();
8
            Console.WriteLine("Eingabe war: " + str);
9
            Console.WriteLine("Neue Eingabe:");
10
        }
11
    }
12
}

Die Aufgabe in der neuen Übung ist nun, die Ausgabe parallel in einer 
Windows Forms zu machen. Doch ich bekomme es nicht hin, dass es 
ausgegeben wird. Selbst das neue Fenster, was von der Form erstellt 
wird, spackt rum. Es wird nichts angezeigt und wenn ich ein anderes 
Fenster dort rüberziehe, kommen diese Schlieren, die typisch für 
abgestürzte Programme sind. Ich weiß nicht wie ich das machen soll. In 
der Aufgabe stand etwas wie: "Ersetzen sie die Methode Lauf durch die 
überschriebene OnPaint-Methode der Klasse Form.
Mein Versuch war:

Datei main.cs:
1
class Hauptklasse{
2
    public static void Main(){
3
        Unterklasse u = new Unterklasse();
4
        Application.Run(u);
5
    }
6
}

Datei unter.cs:
1
class Unterklasse : Form {
2
    protected override void OnPaint(PaintEventArgs pea){
3
        base.OnPaint(pea);
4
        Graphics grfx = pea.Graphics;
5
6
        string str = "";
7
        Console.WriteLine("Eingabe:");
8
        while(str != "beenden"){
9
            str = Console.ReadLine();
10
            Console.Clear();
11
            Console.WriteLine("Eingabe war: " + str);
12
            Console.WriteLine("Neue Eingabe:");
13
            grfx.DrawString("Eingabe war: " + str + "\nNeue Eingabe:", Font, Brushes.Black, 0, 0);
14
        }
15
    }
16
}

Das ging aber nicht, da er wohl erst die Änderung anzeigt, wenn die 
OnPaint-Methode Verlassen wird. Ebenso aus dem gleichen Grund ging 
folgendes auch nicht:

Datei unter.cs:
1
class Unterklasse : Form {
2
    protected override void OnPaint(PaintEventArgs pea){
3
        base.OnPaint(pea);
4
        Graphics grfx = pea.Graphics;
5
6
        string str = "";
7
        Console.WriteLine("Eingabe:");
8
        
9
        str = Console.ReadLine();
10
        Console.Clear();
11
        Console.WriteLine("Eingabe war: " + str);
12
        Console.WriteLine("Neue Eingabe:");
13
        grfx.DrawString("Eingabe war: " + str + "\nNeue Eingabe:", Font, Brushes.Black, 0, 0);
14
15
        Invalidate();
16
    }
17
}

Sofort wenn er in die Methode OnPaint rein geht, wird das Fenster 
geleert.

Dann dachte ich mir, ich kann es anders lösen:

Datei main.cs:
1
class Hauptklasse{
2
    public static void Main(){
3
        Unterklasse u = new Unterklasse();
4
        u.Show();
5
        u.Lauf();
6
        u.Hide();
7
    }
8
}

Datei unter.cs:
1
class Unterklasse : Form {
2
    string str;
3
    public Unterklasse(){
4
        str = "";
5
    }
6
7
    protected override void OnPaint(PaintEventArgs pea){
8
        base.OnPaint(pea);
9
        Graphics grfx = pea.Graphics;
10
        grfx.DrawString(str, Font, Brushes.Black, 0, 0);
11
    }
12
13
    public void Lauf(){
14
        string str = "";
15
        Console.WriteLine("Eingabe:");
16
        while(str != "beenden"){
17
            str = Console.ReadLine();
18
            Console.Clear();
19
            Console.WriteLine("Eingabe war: " + str);
20
            Console.WriteLine("Neue Eingabe:");
21
            str = "Eingabe war: " + str + "\nNeue Eingabe:");
22
            Invalidate();
23
        }
24
    }
25
}

Dort dachte ich, dass durch das Invalidate die OnPaint-Methode 
aufgerufen wird, doch dies war nicht der Fall.


Kann mir jemand vielleicht einen Tipp geben, wie ich das hinbekommen 
kann??
Ich bin langsam am Verzweifeln.

Danke schonmal

Michael

von Sebasrian L. (Gast)


Lesenswert?

1. In Lauf wird str neu definiert, onpaint kriegt gar nix
2. Du rufst nach invalidate sofort hide auf
3. Du kannst nicht ganz so einfach aus ner konsolen applikation eine 
winformsanwendung machen, umgekehrt schneller(aus dem projekt template)
4. Lass mal den konsolenteil aus der form raus und übergeb den string 
als property
5. Euer proff verlangt wirres zeug ohne grundlagen

Gl hf

von bluppdidupp (Gast)


Lesenswert?

Man sollte in OnPaint() eigentlich nichts anderes tun als Zeichnen.
Und erst recht keine blockierenden Funktionen wie ReadLine() verwenden, 
die dafür sorgen das Windows meinen könnte dass das Fenster tot wäre 
("Reagiert nicht mehr") weil OnPaint() verdächtig lange braucht.

Invalidate() in OnPaint() ist auch nicht sinnvoll, du erzeugst damit 
praktisch eine Endlosschleife.

Bei der zweiten Varianten fehlt außerdem Application.Run()
erst ab .Run() wird die Message-Loop gestartet. Die Message-Loop 
verarbeitet Nachrichten die Windows an Fenster der Anwendung sendet und 
löst oftmals Ereignisse wie OnPaint() überhaupt erst aus.

Weiterhin merkwürdig ist Konsole und Windows.Forms zu mischen.
Insgesamt riecht das praktisch nach übelster Pfuscherei (also schon die 
Aufgabenstellung, wenn die so gemeint ist) ;D

-----

Bei Windows.Forms (wenn man davon absieht dass man dort einfach ne 
Textbox nehmen könnte und kein Console.ReadLine() nimmt), würde ich das 
persönlich grob eher so machen:
1) KeyPress-Ereignis abfangen und getipptes Zeichen an einen String 
anhängen und Invalidate() aufrufen*
2) In OnPaint() einfach nur den String zeichnen

*Newline-Behandlung:
- Zusätzlich in einer bool-Variable merken falls ein Newline eingegeben 
wurde
- Beim nächsten KeyPress: Ist die bool-Variable gesetzt, dann erstmal 
den String leeren bevor das getippte Zeichen angehangen wird

von Michael S. (rbs_phoenix)


Lesenswert?

Sebasrian L. schrieb:
> 1. In Lauf wird str neu definiert, onpaint kriegt gar nix
> 2. Du rufst nach invalidate sofort hide auf
> 3. Du kannst nicht ganz so einfach aus ner konsolen applikation eine
> winformsanwendung machen, umgekehrt schneller(aus dem projekt template)
> 4. Lass mal den konsolenteil aus der form raus und übergeb den string
> als property
> 5. Euer proff verlangt wirres zeug ohne grundlagen

Zu 1.: In Lauf wird str (die in der gesamten Klasse Unterklasse bekannt 
ist) überschrieben. Wenn OnPaint aufgerufen würde, würde er doch also 
auch die Variable str verwenden können!?

Zu 2.: Invalidate rufe ich am Ende der While-Schleife auf. Gedacht hatte 
ich mir, dass ich die Eingabe mache, gib den Text in der Konsole auf, 
schreibe den anzuzeigenen Text in die str Variable und veranlasse mit 
Invalidate das Neuschreiben der Form, was somit die OnPaint-Methode 
aufruft, die dann den Text in str ausgibt.

Zu 5.: Denk ich mir auch manchmal. Es soll ein Programm langsam von 
Konsolenbetrieb in komplett Windows Forms betrieb geändert werden. Und 
diese Übung ist quasi ein Zwischenschritt, wo nur etwas angezeigt werden 
kann.

@ bluppdidupp: Deshalb kann ich auch nich so einfach mit Textfeldern die 
Eingabe machen. Damit wäre das sicherlich weniger das Problem. Die 
Bedienfelder lösen ja auch die Ereignisse aus. OnClick, OnKeyDown usw 
usf. Es soll quasi nichts geändert werden, nur dass das, was in der 
Konsole steht, auch in der Windows Forms steht.


bluppdidupp schrieb:
> Invalidate() in OnPaint() ist auch nicht sinnvoll, du erzeugst damit
> praktisch eine Endlosschleife.

Das war auch mein Ziel, nur dachte ich, dass der Text die ganze Zeit da 
steht. Dann auf str = "beenden" geprüft und ein Close(); gemacht und ich 
hätte dadurch quasi die While-Schleife ersetzt.


bluppdidupp schrieb:
> Bei der zweiten Varianten fehlt außerdem Application.Run()
> erst ab .Run() wird die Message-Loop gestartet. Die Message-Loop
> verarbeitet Nachrichten die Windows an Fenster der Anwendung sendet und
> löst oftmals Ereignisse wie OnPaint() überhaupt erst aus.

Das hab ich mir auch schon überlegt, doch wenn ich Application.Run habe, 
wo kommt dann mein Console.ReadLine() hin? In die OnPaint-Methode ja 
nicht, da dann nicht gezeichnet wird. Unter das Application.Run auch 
nicht, da man dort ja erst hinkommt, wenn man die Form per X oder 
Close(); beendet hat.

bluppdidupp schrieb:
> Bei Windows.Forms (wenn man davon absieht dass man dort einfach ne
> Textbox nehmen könnte und kein Console.ReadLine() nimmt), würde ich das
> persönlich grob eher so machen:
> 1) KeyPress-Ereignis abfangen und getipptes Zeichen an einen String
> anhängen und Invalidate() aufrufen*
> 2) In OnPaint() einfach nur den String zeichnen
>
> *Newline-Behandlung:
> - Zusätzlich in einer bool-Variable merken falls ein Newline eingegeben
> wurde
> - Beim nächsten KeyPress: Ist die bool-Variable gesetzt, dann erstmal
> den String leeren bevor das getippte Zeichen angehangen wird

Wie gesagt, mit Textfeld oder OnKeyPress-Methode ginge das ganze 
warscheinlich deutlich einfacher.
Ich hab die Aufgabenstellung einfach mal kopiert:

"Aufgabe 5.1 Fenster mit OnPaint-Methode
5.1 Im nächsten Schritt soll die Ausgabe des Programms nun parallel in 
der Konsole und in einer selbst erstellten Form stattfinden.
Das ist die zugrundeliegende Idee: In der reinen Konsolenversion war der 
„Motor“ in der while-Schleife der Methode Lauf() innerhalb der Klasse 
Unterklasse eingebaut. Diesen Motor verlagern wir nun in die 
überschriebene OnPaint-Methode einer Form. Gleichzeitig bemühen wir uns, 
von dem bisher geschriebenen Code so viel wie möglich beizubehalten, da 
dieser Teil ja bereits getestet ist. Leiten Sie daher die Klasse 
Unterklasse von der Klasse Form ab. Vergessen Sie nicht, die Verweise 
System.Windows.Forms und System.Drawing und die entsprechenden 
using-Direktiven in unter.cs unserem Programm hinzuzufügen.
...
Um Ausgaben im Formular zu realisieren, wird ein PaintHandler benötigt, 
welcher dafür sorgt, Text- und Grafik-Ausgaben im Formular darzustellen. 
Hierfür soll die Methode
protected override void OnPaint(PaintEventArgs pea)
verwendet werden. Sie ersetzt die Methode Lauf() unserer bisherigen 
Klasse Unterklasse. Achten Sie darauf, dass die Lauffähigkeit des 
Programms durch Ihre Änderungen nicht beeinträchtigt wird, der Bediener 
also immer noch über die Konsole Befehle an das Programm übermitteln 
kann. Der einzige Unterschied ist nun, dass zusätzlich zu den Ausgaben 
in der Konsole diese auch im Formular dargestellt werden sollen. Es ist 
klar, dass das Programm in dieser Zwischenstufe zwischen 
Konsolenanwendung und reiner grafischer Benutzereingabe etwas „ruckelig“ 
funktioniert, aber das werden wir im weiteren Verlauf abstellen. Um die 
Form dazu zu bringen, dass sie sich beendet, muss die Methode Close () 
aufgerufen werden."

Vielleicht hab ich auch etwas nur nich gerafft.

von Michael S. (rbs_phoenix)


Lesenswert?

Ok. Ich hab es nun doch hinbekommen. Nach vielen Versuchen und 
umstellen.

Letztendlich ist es wie hier, nur dass das DrawString vor dem ReadLine 
kommt:

Michael Skropski schrieb:
> Datei main.cs:class Hauptklasse{
>     public static void Main(){
>         Unterklasse u = new Unterklasse();
>         Application.Run(u);
>     }
> }
>
> Datei unter.cs:class Unterklasse : Form {
>     protected override void OnPaint(PaintEventArgs pea){
>         base.OnPaint(pea);
>         Graphics grfx = pea.Graphics;
>
>         string str = "";
>         Console.WriteLine("Eingabe:");
>         while(str != "beenden"){
>             str = Console.ReadLine();
>             Console.Clear();
>             Console.WriteLine("Eingabe war: " + str);
>             Console.WriteLine("Neue Eingabe:");
>             grfx.DrawString("Eingabe war: " + str + "\nNeue Eingabe:", Font, 
Brushes.Black, 0, 0);
>         }
>     }
> }

Dennoch vielen Dank.

von Oliver R. (superberti)


Lesenswert?

Hi Michael,

mannomann, Du kannst ja nichts dafür, aber die Aufgabe ist meiner 
Meinung nach wirklich absoluter Schwachsinn!
OnPaint dient einzig und allein zum Zeichnen von eigenen grafischen 
Erweiterungen. Normalerweise braucht niemand diese Funktion zu 
überschreiben. Der Zeitpunkt, wann dieser Event ausgelöst wird, bestimmt 
ganz alleine Windows und sonst niemand (außer erzwungen mit 
Invalidate()). Jegliche Applikationslogik hat hier überhaupt gar nichts 
zu suchen.
Auch wenn es nur ein Zwischenschritt war: Eine Übung dient ja dazu, dass 
irgendetwas Sinnvolles hängenbleibt. Diesen Kram sollte man allerdings 
GANZ GANZ SCHNELL wieder vergessen!

Gruß, Oliver

von Michael S. (rbs_phoenix)


Lesenswert?

Ich dachte mir auch schon, dass das nicht im Sinne des Erfinders sein 
kann. Ich mein, was passiert denn, wenn durch irgendwas (z.B. das 
Fenster über den Bildschirmrand rausschieben und wieder zurück) noch ein 
OnPaint ausgelöst wird, er aber noch in der while-Schleife steckt. In 
der Vorlesung sind jetzt Events dran gekommen und es geht jetzt weiter 
mit Buttons, Textfeldern usw. Ich glaube nicht, das da noch soo viel 
kommt bezüglich der Forms bzw. OnPaint.

Ich hoffe das Wahlpflichtfach "Microcontrollerprogrammierung in C" wird 
besser. Immerhin gibts da keine Forms oder ähnliches und C hatten wir 
schon im ersten Semester.

Naja, ich bin gespannt, was er sich für die nächste Übung ausgedacht 
hat.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.