TextBox.AppendText() von anderem Thread über Delegate



  • Hey genial "Mü" danke 🙂

    Ich hatte es zwar hinbekommen, der Code sieht aber schrecklich aus, es ist natürlich nicht so elegant wie deine Lösung, werde es so übernehmen...

    Danke für deine Beteiligung und Hilfe, das Abend-Bier geht auf dich 😉



  • Hehe, reich mir lieber eins (Bier) rüber 😉



  • *BeckRüberReich* 🙂

    So eine Frage hätte ich jetzt doch:
    Ich habe das nun mit der Progressbar getestet. Habe mir eine Schleife gebastelt um die Progressbar langsam zu füllen. Es funktioniert auch sehr gut. Nur so lange eben die Progressbar gefüllt wird, ist die GUI nicht zugänglich, ich kann sie beispielsweise nicht verschieben.

    Das ist in der GUI : Form Klasse

    public void AccessGUI(Control control, Action action)
            {
                if (control.InvokeRequired)
                    control.Invoke(action);
                else
                    action();
            }
    

    Das in der Klasse in dem der andere Thread läuft:

    public void SetPBValue2()
            {
                GUI.F_GUI.AccessGUI(GUI.F_GUI.toolStripProgressBar1.Control, () =>
                {
    
                    GUI.F_GUI.toolStripProgressBar1.PerformStep();
    
                });
            }
    
            public void StartProgressBar()
            {
                for (int i = 0; i < 100000000; i++)
                {
                    if (i % 1000000 == 0)
                    {
                        SetPBValue2();
                    }
                }
            }
    

    Über einen Button in der GUI rufe ich eben StartProgressBar() auf, und die ProgressBar wird gefüllt. Erst nach dem Füllen, ist sie wieder zugänglich.



  • StartProgressBar läuft im GUI-Thread. Natürlich ist die GUI blockiert bis die Schleife abgearbeitet wurde.

    Ganz allgemein solltest Du die Anzahl der Wechsel in den GUI-Thread möglichst gering halten. Eine Progressbar (mit Threadwechsel) 1000 mal in der Sekunde zu "stepen" ist unnötig und extrem unperformant.

    Mit Invoke kann man auch schnell Deadlocks bauen. Also vorsicht 😉



  • StartProgressBar läuft im GUI-Thread. Natürlich ist die GUI blockiert bis die Schleife abgearbeitet wurde.

    OK stimmt hätte ich doch selbst so schlussfolgern können 🙂

    Eine Progressbar 1000 mal in der Sekunde zu "stepen" ist unnötig und extrem unperformant.

    Das war nur testweise 🙂

    Mit Invoke kann man auch schnell Deadlocks bauen. Also vorsicht ;)
    

    Ja meine GUI-Zugriffe beschränken sich hauptsächlich auf das gelegentliche aktualisieren von textboxen oder labels. Es gibt 2 prozesse bei denen ich die Progressbar benutze, die dauern aber maximal 35 sekunden.

    Super dann wäre alles endgültig geklärt, mir gefällt die lösung sehr gut, sieht sehr sauber aus, ich spare einige zeilen im gegensatz zu meiner "hauptsacheEsFunktioniertMitDelegaten"-Lösung 🙂

    Da sind dann heute 2 Bier drin die auf dich gehen 😉 Schönes WE



  • Dir auch einen schönen Samstag-Abend 👍



  • Du schreibst ja selbst, dass es nur ein Beispiel war. Aber ich möchte gerne noch etwas anmerken.

    Joeyeay078087679697 schrieb:

    public void SetPBValue2()
            {
                GUI.F_GUI.AccessGUI(GUI.F_GUI.toolStripProgressBar1.Control, () =>
                {
    
                    GUI.F_GUI.toolStripProgressBar1.PerformStep();
    
                });
            }
    
    1. Du hast AccessGUI in deine Form gepackt. Bedenke, dass Du diese Methode (und die andere aus der ThreadUI-Klasse von mir) wunderbar auslagern und in JEDER Form wiederverwenden kannst. Zum Beispiel in eine Utility-DLL.

    2. Es sieht so aus, als würde deine Arbeiterklasse immer noch die Form kennen. Und die Form hast Du wahrscheinlich über ein Singleton global bekannt gemacht (GUI.F_GUI). Das ist beides nicht so toll. Keine Hilfs-, Arbeiter- oder ThreadWorkerklasse sollte über die GUI bescheid wissen. Es ist sehr viel sauberer wenn solche Klassen niedriger Ebene (im Schichtenmodell) Events werfen bei interessanten Ereignissen, und die GUI (d.h. die entsprechenden Forms) diese Events abonnieren. Innerhalb der Eventhandler verwendest Du dann einfach die sauber ausgelagerte AccessGUI-Methode.



  • Hallo Joey...

    du solltest dir auch mal die BackgroundWorker-Klasse anschauen, welche insbesondere für Aktualisierungen von ProgressBar etc. einfacher zu bedienen ist (die ProgressChanged-Ereignismethode läuft automatisch im GUI-Thread, so daß manuell dann kein Invoke mehr nötig ist).



    1. Deine Controls machst Du public: GUI.F_GUI.toolStripProgressBar1.Control. Das ist ganz übel. Du kannst diese Abhängigkeiten aus deinem Code entfernen, durch ein Design mittels Events.


  • Oh man ich blicke gar nicht mehr durch.
    Ist mein 1. C# Projekt, in Java war das immer einfacher zu lösen.

    Nochmal zusammengefasst:
    -Ich habe eine Form-Klasse "GUI"
    -Ich habe eine Klasse mit dem anderen Thread "MyThread".

    Ich habe insgesamt 5 "Prozesse", also Zugriffe auf verschiedene Elemente der Form:
    -UpdateProgrammInfo(5 strings die 5 verschiedene Labels aktualisieren sollen)
    -UpdateStatusBox(1 string der auf eine TextBox geschrieben werden soll)
    -UpdateConnectionStatus(1 ToolStripStatusLabel dessen Text und Farbe geändert werden soll)
    -SetProgressBarValues(ProgressBar Values Minimum, Maximum und Step setzen)
    -UpdateProgressbar(ProgressBar per PerformStep() erhöhen)

    Wo erstelle ich jetzt was womit bzw. wo definiere ich was und wie erfolgen die Zugriffe? Sorry ich blicke nicht mehr durch 😞



  • Also ich hoffe das ist jetzt sauber, hatte einen kleinen Workaround über Guggle gefunden. Ein einfaches Beispiel:

    In meiner "GUI : Form" Klasse definiere ich ein Objekt der Klasse GUIAccess, wo ich die Delegate und Events deklariere, die Methode die Veränderungen auf der GUI durchführt, und Abonniere das Event dazu:

    GUIAccess Gu = new GUIAccess;
    
    private void UpdateLabel(string Text)
    {
    this.label1.Text = Text;
    }
    
    Gu.UpdateLabelDelegateEvent += new Gu.UpdateLabelDelegate(UpdateLabel);
    

    In meiner Klasse "GUIAccess" deklariere ich das Delegate, das Event, und die Methode die von dem anderen Thread aufgerufen wird, wenn das Event ausgelöst werden soll:

    public delegate void UpdateLabelDelegate(string Text);
    
    public event UpdateLabelDelegate UpdateLabelDelegateEvent;
    
    public void EventAusloesen()
    {
    UpdateLabelDelegateEvent("Neuer Wert");
    }
    

Anmelden zum Antworten