Laden...

Aus zweiten Thread auf meinen UI-Thread zugreifen

Erstellt von Thron vor 6 Jahren Letzter Beitrag vor 6 Jahren 2.447 Views
T
Thron Themenstarter:in
63 Beiträge seit 2017
vor 6 Jahren
Aus zweiten Thread auf meinen UI-Thread zugreifen

Hallo,

ich möchte aus einem anderen Thread auf mein Formular zugreifen um eine LisBox mit Daten zu befüllen. Jetzt kommt das altbekannte Problem, dass ich ja nicht von einem anderen Thread darauf zugreifen darf, wenn ich nicht dein Invoke / Delegate nutze. Ich habe mich damit mal versucht und irgendwie geht das nicht so recht.

Die Listbox wir zwar befüllt, aber das "Programm" verhält sich so, als würde ich gar keinen eigenen Thread dazu aufrufen. Es ist während dem Befüllen "eingefroren", dass wollte ich ja gerade mit dem extra Thread vermeiden.

Kann mir jemand helfen? Ich finde meinen Fehler nicht!?


    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

        }

        public void ArraylisteFüllen()
            {

                if (listBox1.InvokeRequired)
                    listBox1.Invoke((MethodInvoker)delegate ()
                    {

                        ArrayList zeilen = new ArrayList();
                        for (int i = 0; i < 5; i++)
                        {
                            zeilen.Add("Datensatz" + i);
                            listBox1.Items.Add("Datensatz" + i);
                            Thread.Sleep(2000);
                        }

                    });
            }


            private void btn_gif_Click(object sender, EventArgs e)
            {
                Thread frm1 = new Thread(new ThreadStart(ArraylisteFüllen));
                frm1.Start();
            }

        }

Gruß an alle und Danke schon im Voraus für Eure Mühe....

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo Thron,

vorab:
ArrayList gehört in die Mottenkiste und sollte wie alle untypisierten Collections aus System.Collections nicht mehr benutzt werden. Verwende stattdessen List<T> und alle anderen typisierten Collections aus System.Collections.Generic.

Bei deinem Problem hat dir [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke) nicht geholfen bzw. wo hängst du da genau?

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

2.298 Beiträge seit 2010
vor 6 Jahren

Hallo,

dein Thread erfüllt im aktuellen Zustand nur die Aufgabe, dem GUI-Thread zu sagen, dass er die ListBox befüllen soll.

Das befüllen der ListBox (und das ist auch korrekt so) erfolgt dann eben wieder im GUI-Thread. Der korrekte Ansatz wäre also: Vom Thread die Liste erstellen lassen und aschließend an den GUI-Thread zur Erzeugung der ListBox-Einträge zu übergeben.

Bei 5 Zeilen würde dir im übrigen nicht das GUI einfrieren, wenn du das Sleep rausnehmen würdest.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

T
Thron Themenstarter:in
63 Beiträge seit 2017
vor 6 Jahren

das mir ein Hauptformular einfriert...

habe es jetzt nach der "Anleitung" von dir probiert und wieder das gleiche ergebnis.

1
124 Beiträge seit 2012
vor 6 Jahren

Bei 5 Zeilen würde dir im übrigen nicht das GUI einfrieren, wenn du das Sleep rausnehmen würdest.

T
461 Beiträge seit 2013
vor 6 Jahren

@123thomas war schneller 😉

Das ist ja nicht wirklich eine Antwort, damit kann keiner was anfangen.

Wie sieht der aktuelle Code aus, was passiert beim Debug... teilt etwas die Ausgabe mit was interessant sein könnte?

Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen... 😄

2.298 Beiträge seit 2010
vor 6 Jahren

Nö, das glaube ich dir nicht. Ich habe deinen Code mal entsprechend angepasst (so dass die Funktionsweise noch immer der von dir gewünschten entspricht).


List<string> zeilen = new List<string>();

for(int i = 0; i < 5; i++)
{
   zeilen.Add("Datensatz" + i);

   if(this.listBox1.InvokeRequired)
   {
         listBox1.Invoke((MethodInvoker)delegate ()
         {
                listBox1.Items.Add(zeilen[i]);
         });
   }

   Thread.Sleep(2000);
}

Im ürigen sieht mir dein Code sehr nach einer Aufgabe für einen Timer aus.

Beispiel:


public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        _timer.Interval = 2000;
        _timer.Tick += new EventHandler(_timer_Tick);
    }

    List<string> _zeilen = new List<string>();

    void _timer_Tick(object sender, EventArgs e)
    {
        _zeilen.Add("Datensatz" + this._zeilen.Count);
        listBox1.Items.Add(_zeilen[this._zeilen.Count - 1]);

        if (this._zeilen.Count == 5)
            this._timer.Enabled = false;
    }

    Timer _timer = new Timer();

    private void button1_Click(object sender, EventArgs e)
    {
        this._timer.Enabled = true;
    }      
}

Und zu guter letzt noch die Umsetzung wie ich es dir beschrieben hatte:


List<string> zeilen = new List<string>();
for (int i = 0; i < 5; i++)
{
    zeilen.Add("Datensatz" + i);
    Thread.Sleep(2000);
}

if (this.listBox1.InvokeRequired)
{
    this.listBox1.Invoke((MethodInvoker)delegate()
    {
        this.listBox1.Items.AddRange(zeilen.ToArray());
    });
}

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

T
Thron Themenstarter:in
63 Beiträge seit 2017
vor 6 Jahren

okay...hat funktioniert, lag an dem falsch gesetzten Sleep...

Wenn ich jetzt eine rechenintensive Aufgabe habe und möchte immer mal wieder den Zwischenstand davon haben, dann lagere ich diese in einem eigenen Thread aus und lassen


if(this.listBox1.InvokeRequired)
   {
         listBox1.Invoke((MethodInvoker)delegate ()
         {
                // Hier immer wieder den Return zurückgeben!?
         });
   }

oder ist das falsch...

Danke für den Tipp mit den List <T> werde es in Zukunft umsetzten...

Gruß

2.298 Beiträge seit 2010
vor 6 Jahren

Wenn ich jetzt eine rechenintensive Aufgabe habe und möchte immer mal wieder den Zwischenstand davon haben, dann lagere ich diese in einem eigenen Thread aus und lassen

Bei Rechenintensiven Aufgaben wird ein Thread für die Aufgabe erstellt. - Ein return darin auszuführen, würden den Thread beenden. Besser wäre hier: Werfen eines Events auf das die GUI reagiert. Die Aufgabe gehört sowieso in eine eigene Klasse. Das GUI aboniert das Event und reagiert entsprechend.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

16.806 Beiträge seit 2008
vor 6 Jahren

Hier wird es sinn machen, das ganz schnell wirklich auszulagern statt mit irgendwelchen delegate Inline-Code zu basteln:

  • Operation in eine eigene Klasse auslagern
  • Parameterübergabe via Konstruktor
  • Starten der Aufgabe im Hintergrundthread zB, durch ein Run() oder wie man auch immer das nennen will
  • Klasse steht als Objektinstanz dann zur Verfügung
  • Aktualisierungen erfolgen dann über Events
  • UI lauscht auf die Events und erhält darüber die Informationen
T
Thron Themenstarter:in
63 Beiträge seit 2017
vor 6 Jahren

Okay....

Verstanden....Vielen Dank für die Infos...