none
Form1_Shown wiederholter aufruf RRS feed

  • Frage

  • Hallo zusammen,
    bin Neuling im C# Bereich und kämpfte mich bisher mit Konsolenanwendungen durch.

    Jetzt habe ich eine Visual Studio eine Form gebaut. Der Code für sich  funktioniert auch wunderbar.

    Am besten zeige ich den Code:
    Die Main:

            public static void Main()
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.DoEvents();
                Application.Run(new Form1());

    In der Form1_shown rufe ich meinen Code zur Bearbeitung auf.

    private void Form1_Shown(object sender, EventArgs e)
            {
                ClassW w = new ClassW("Outlook", LabelInfo);
                w.ThreadCall();
    
            }

    Die Instanzierung der Klasse, der Konstruktor und der Aufruf des der Instanz (ThreadCall) wird sauber ausgeführt. Das Label LabelInfo brauche ich zur Aktualisierung. Nach Beendigung des Thread(w.Thredcall()) wird aber erneut Form1_Shown aufgerufen.  Sollte hier nicht zurück in die Main gesprungen werden bzw. ich möchte die Anwendung sauber schließen.

    Ich verstehe jetzt aber nicht warum?

    Freitag, 15. März 2019 11:54

Antworten

  • Hi,

    das whilte( th.IsAlive ) solltest Du dir schon mal sparen und den Code dazu komplett entsorgen. Dann brauchs Du auch kein Threading. Wenn Du in periodischen Abständen etwas aktualisieren willst, richte einen Timer ein, stell dort das gewünschte Intervall ein und nimm die entsprechenden Vorgänge dann im jeweiligen Event vor.

    Um die Zeit zu messen, merkst Du dir den aktuellen Zeitstempel vor Beginn und dann nochmal nach Beendigung der jeweiligen Arbeiten. Um das Label zu aktualisieren, nimmst Du, wie oben geschrieben, bspw. einen Timer.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET (2001-2018)
    https://www.asp-solutions.de/ - IT Beratung, Softwareentwicklung, Remotesupport

    Samstag, 16. März 2019 14:37
    Moderator
  • Hi,
    nachfolgend eine kleine Konsolen-Demo aus Deinen Code-Stücken:

    using System;
    using System.Diagnostics;
    using System.IO.Compression;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace ConsoleApp1
    {
      class Program04
      {
        static void Main(string[] args)
        {
          try
          {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.DoEvents();
            Application.Run(new Form1());
          }
          catch (Exception ex) { Console.WriteLine(ex.ToString()); }
          Console.WriteLine("Continue enter key");
          Console.ReadKey();
        }
    
        internal class ClassW
        {
          Form1 f = null;
          public ClassW(Form1 frm) => f = frm;
    
          public async void ThreadGo()
          {
            Stopwatch zeitmessen = new Stopwatch();
            System.Timers.Timer t = new System.Timers.Timer(1000);
            t.Elapsed += (sender, e) =>
                 {
                   TimeSpan ts = zeitmessen.Elapsed;
                   Console.WriteLine("Sekunden vergangen: " + ts.Seconds.ToString());
                   f.UpdateLabel(ts.Seconds.ToString());
                 };
            Console.WriteLine("ThreadGO() aufgerufen");
            zeitmessen.Start();
            t.Start();
            // asynchronen Prozess starten
            Console.WriteLine("vor th start");
            await Task.Run(Unzip);
            Console.WriteLine("nach th start");
            zeitmessen.Stop();
            t.Stop();
          }
    
          private void Unzip()
          {
            try
            {
              Console.WriteLine("vor Unzip");
              ZipFile.ExtractToDirectory("C:\\temp\\test.zip", "C:\\temp\\temp2\\");
              Thread.Sleep(3000); // lange Arbeit simulieren
              Console.WriteLine("nach Unzip");
            }
            catch (Exception ex)
            {
              Console.WriteLine(ex.Message);
            }
          }
        }
        internal class Form1 : Form
        {
          internal Label Label1 = new Label();
          public Form1() => this.Load += Form1_Load;
    
          private void Form1_Load(object sender, EventArgs e)
          {
            this.Controls.Add(Label1);
            // Thread starten
            ClassW c = new ClassW(this);
            c.ThreadGo();
          }
    
          internal void UpdateLabel(string msg)
          {
            if (Label1.InvokeRequired) this.Invoke(new Action<string>(this.UpdateLabel), msg);
            else Label1.Text = msg;
          }
        }
      }
    }


    --
    Viele Grüsse / Best Regards
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Sonntag, 17. März 2019 19:40

Alle Antworten

  • Hi,
    Das Fenster Form1 bleibt solange offen bis es geschlossen wird. Wenn ThreadCall eine Thread startet und erst mit Beendigung des Threads das Fenster geschlossen werden soll, dann musst Du das Ereignis abfangen und ein this.Close(); im Form1 ausführen. Wenn man davon ausgeht, dass Deine Klasse ClassW zum Threadende ein Ereignis auslöst, dann hänge daran einen Ereignishandler mit dem this.Close().

    --
    Viele Grüsse / Best Regards
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Freitag, 15. März 2019 14:46
  • Hallo,
    Danke für die Antwort. Was mich halt verwundert, nachdem mein Thread erfolgreich abgearbeitet ist, wird erneut die Methode form1_Shown(...) aufgerufen. Laut C# Doku wird aber Shown nur einmalig aufgerufen. Könnte das evtl. mit Application.DoEvents() zusammenhängen? Ist es sinnvoll in der Shown Methode überhaupt Code auszuführen oder gibt es etwas geeigneteres?

    Bin in C# nicht so firm und muss halt noch vieles lernen, verstehen, anwenden und ganz viel lesen und querlesen :)

    Freitag, 15. März 2019 16:02
  • Hi,
    ich sehe in Deinem geposteten Code kein Application.DoEvents(). Das ist überhaupt nicht erforderlich. Anstelle dazu gibt es aktuell async/await. Was machst Du wirklich?

    --
    Viele Grüsse / Best Regards
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Freitag, 15. März 2019 22:30
  • Verwende das DoEvents() in meiner Main() (s.oben). So wie Microsoft es beschreibt, liest es sich, als sei DoEvents ein bad hack...

    Also die Klasse ClassW ist dazu da, die Sekunden zu zählen, die die Ausführung eines Programmteils benötigt. Dazu schreibt es jede Sekunden bzw. irgendwann die Minuten in ein Label. Einerseits habe ich jetzt die Ausführung des Programm, andererseits das zählen der Zeit und aktualisieren des Labels.

    Thread t = new Thread(ExecuteProgram);
    t.Start()
    while(t.isAlive())
    {
     ...
    }

    Daher dachte ich, ein Thread ist genau das, was ich brauche... muß sagen, das funktioniert sehr zuverlässig und liefert auch das richtige bzw. erwartetes Ergebnis ;)

    Gerade getestet, wenn in der Main() das Application.DoEvents() entferne, wird das Form überhaupt nicht angezeigt und direkt der Code ausgeführt! Irgendwas ist nicht ok... :(

    Samstag, 16. März 2019 10:14
  • Hi,

    erstell doch mal ein vollständiges aber nur mit dem absolut notwendigsten versehenes Projekt und stell das (bspw. in deinem OneDrive Account) zum Download zur Verfügung.

    Dann kann man sich das auch mal anschauen.

    Denn klar, irgendwo wirst Du da schon irgendwas falsch machen. Aber mit den Bruchstücken an Code ist das nur schwer bis gar nicht zu sagen.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET (2001-2018)
    https://www.asp-solutions.de/ - IT Beratung, Softwareentwicklung, Remotesupport

    Samstag, 16. März 2019 10:26
    Moderator
  • Hi,
    Deine while-Schleife arbeitet mit voller CPU-Last. Da bleibt dann keine Zeit übrig, um z.B. die Oberfläche zu aktualisieren bzw. anzuzeigen. Du solltest async/await nutzen. Da wird die CPU im Thread mit await solange ins Warten geschickt, bis der Thread (Task) beendet wird. In dieser Wartezeit kann sich die CPU um die Befehle zur Aktualisierung der Anzeige kümmern, was bei einer while-Schleife wegen der 100% CPU-Auslastung nicht möglich ist.

    --
    Viele Grüsse / Best Regards
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Samstag, 16. März 2019 11:41
  • Hallo zusammen,
    habe mein Vorhaben mal zusammengestampft und etwas abgeändert.
    Hier mal der Code der ClassW:

        class ClassW
        {
            Form1 f = new Form1();
            public void ThreadGo()
            {
                Console.WriteLine("ThreadGO() aufgerufen");
                Thread th = new Thread(Unzip);
                Console.WriteLine("vor th start");
                th.Start();
                Console.WriteLine("nach th start");
    
                Stopwatch zeitmessen = new Stopwatch();
                zeitmessen.Start();
    
                while (th.IsAlive)
                {
                    TimeSpan ts = zeitmessen.Elapsed;
                    Console.WriteLine("Sekunden vergangen: " + ts.Seconds.ToString());
                    f.UpdateLabel(ts.Seconds.ToString());
                }
                zeitmessen.Stop();
            }
    
            private void Unzip()
            {
                Console.WriteLine("vor Unzip");
                ZipFile.ExtractToDirectory("C:\\temp\\test.zip", "C:\\temp\\temp2\\");
                Console.WriteLine("nach unzip");
            }
    
        }

    Möchte in diesem Fall nur die Sekunden messen bis das test.zip Archiv entpackt ist. Hm, könnte ich das Zeit messen in eine eigene Funktion auslagern und mit einem eigenen Thread versehen?
    Momentan verstehe ich den async/await Konstrukt nicht wirklich :(
    Das wird doch schwieriger als gedacht, bzw. mit meinen Mitteln in C# wahrscheinlich nicht lösbar...

    @Peter Fleischer, stimmt, wenn ich auf th.isalive in der While Schleife warte, ist der Prozessor 100% auslgeastet.

    Samstag, 16. März 2019 14:26
  • Hi,

    das whilte( th.IsAlive ) solltest Du dir schon mal sparen und den Code dazu komplett entsorgen. Dann brauchs Du auch kein Threading. Wenn Du in periodischen Abständen etwas aktualisieren willst, richte einen Timer ein, stell dort das gewünschte Intervall ein und nimm die entsprechenden Vorgänge dann im jeweiligen Event vor.

    Um die Zeit zu messen, merkst Du dir den aktuellen Zeitstempel vor Beginn und dann nochmal nach Beendigung der jeweiligen Arbeiten. Um das Label zu aktualisieren, nimmst Du, wie oben geschrieben, bspw. einen Timer.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET (2001-2018)
    https://www.asp-solutions.de/ - IT Beratung, Softwareentwicklung, Remotesupport

    Samstag, 16. März 2019 14:37
    Moderator
  • Vielen Dank für den tip mit Timer. Konnte mittels System.Timers und dem Event elapsedEventHandler das ganze ohne Thread aufbauen.

    Eine Kleinigkeit ist noch im argen...Nach dem Starten des Programms soll sich die Form öffnen, die Abarbeitung des Codes erfolgen mit akutalisierungen in einer Textbox und nach Abarbeitung sich wieder schließen.

    Es liegen also keine aktiven Komponenten auf der Form wie beispeilsweise ein Button oder irgendetwas aktiv gesteuertes.

    Rufe ich meine Klasse in Form1_Shown auf, wird das Form aufgebaut, aber ohne Elemente. Nach Abarbeitung des Codes ist das Form dann voll aufgebaut.
    Rufe ich die Klasse in der Main auf, also:

                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
                ClassW w = new ClassW();
                w.ThreadGo();

    Wird erst nach beenden der Form der code ausgeführt.

    Ich weiß jetzt nicht wo ich meinen Klassenaufruf hinlegen muss, damit zuerst die Form mit allen Steuerelemente aufgebaut wird und anschließend der Aufruf meiner Klasse erfolgen kann. :(

    Kann mir nochmals jemand einen letzten tip geben :)

    Sonntag, 17. März 2019 09:12
  • Hi,
    nachfolgend eine kleine Konsolen-Demo aus Deinen Code-Stücken:

    using System;
    using System.Diagnostics;
    using System.IO.Compression;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace ConsoleApp1
    {
      class Program04
      {
        static void Main(string[] args)
        {
          try
          {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.DoEvents();
            Application.Run(new Form1());
          }
          catch (Exception ex) { Console.WriteLine(ex.ToString()); }
          Console.WriteLine("Continue enter key");
          Console.ReadKey();
        }
    
        internal class ClassW
        {
          Form1 f = null;
          public ClassW(Form1 frm) => f = frm;
    
          public async void ThreadGo()
          {
            Stopwatch zeitmessen = new Stopwatch();
            System.Timers.Timer t = new System.Timers.Timer(1000);
            t.Elapsed += (sender, e) =>
                 {
                   TimeSpan ts = zeitmessen.Elapsed;
                   Console.WriteLine("Sekunden vergangen: " + ts.Seconds.ToString());
                   f.UpdateLabel(ts.Seconds.ToString());
                 };
            Console.WriteLine("ThreadGO() aufgerufen");
            zeitmessen.Start();
            t.Start();
            // asynchronen Prozess starten
            Console.WriteLine("vor th start");
            await Task.Run(Unzip);
            Console.WriteLine("nach th start");
            zeitmessen.Stop();
            t.Stop();
          }
    
          private void Unzip()
          {
            try
            {
              Console.WriteLine("vor Unzip");
              ZipFile.ExtractToDirectory("C:\\temp\\test.zip", "C:\\temp\\temp2\\");
              Thread.Sleep(3000); // lange Arbeit simulieren
              Console.WriteLine("nach Unzip");
            }
            catch (Exception ex)
            {
              Console.WriteLine(ex.Message);
            }
          }
        }
        internal class Form1 : Form
        {
          internal Label Label1 = new Label();
          public Form1() => this.Load += Form1_Load;
    
          private void Form1_Load(object sender, EventArgs e)
          {
            this.Controls.Add(Label1);
            // Thread starten
            ClassW c = new ClassW(this);
            c.ThreadGo();
          }
    
          internal void UpdateLabel(string msg)
          {
            if (Label1.InvokeRequired) this.Invoke(new Action<string>(this.UpdateLabel), msg);
            else Label1.Text = msg;
          }
        }
      }
    }


    --
    Viele Grüsse / Best Regards
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Sonntag, 17. März 2019 19:40