C# Progressbar und backgroundWorker mit dll verknüpfen

-Rayz-

Lieutenant
Registriert
Okt. 2010
Beiträge
897
Hallo,

in einer Form habe ich ein Event welches meine dll aufruft wenn es getriggert wird. In dieser dll laufen dann einige Berechnungen und während dieser Zeit wollte ich eine Progressbar anzeigen lassen. Nur leider weiß ich überhaupt nicht wie ich weitermachen soll.

Fehlermeldung: {"Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement progressBar1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde."}

Hier dachte ich, dass ich durch BeginInvoke den Form-Thread nicht verlasse...?

Code:
namespace DLLTest
{
    public partial class Form1 : Form
    {
       

        Initialisierung init = new Initialisierung();
        OnDataEvent dataTest = new OnDataEvent();
        public delegate void IDDelegate(DataEvent e);

        

        public Form1()
        {

            InitializeComponent();
            backgroundWorker1.WorkerReportsProgress = true;
            
            backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
           
            backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);

        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
                       
            for (int i = 0; i <= 100; i++)
            {               
                backgroundWorker1.ReportProgress(i);
            }       
            e.Cancel = true;

        }

        void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // The progress percentage is a property of e
            progressBar1.Value = e.ProgressPercentage;
        }

        private void Form1_Load(object sender, EventArgs e)
        {


        private void button1_Click(object sender, EventArgs e)
        {
                        
            dataTest.DDataEvent += new EventHandler<DataEvent>(this.ReceivedData2);
           init.start(dataTest);         

        }
        

        private void ReceivedData2(object sender, DataEvent e)
        {
            backgroundWorker1.RunWorkerAsync();   
            this.BeginInvoke(new DDelegate(this.DataRec2), e);
            

        }


        public void DataRec2(DataEvent e)
        {
            if (e.PictureFile != "")
            {
                pictureBox1.ImageLocation = e.PictureFile;
                pictureBox1.Load();

            }
            

        }
        private void label1_Click(object sender, EventArgs e)
        {
           
        }

        private void button2_Click(object sender, EventArgs e)
        {
            pictureBox1.ImageLocation = "";
            dataTest.DDataEvent -= new EventHandler<DataEvent>(this.ReceivedData2);
           
            init.ResetD();                     

        }       
    }
}

Auch ist der Aufruf "backgroundWorker1.RunWorkerAsync(); " an der falschen Stelle. Denn an der Stelle wurden die Berechnungen schon längst erledigt..
nur wo in der Form soll ich den Worker aufrufen?
Oder muss ich den Worker schon in die dll integrieren?
 
Zuletzt bearbeitet:
Also ich denke du musst folgendes tun:

Code:
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // The progress percentage is a property of e
            //progressBar1.Value = e.ProgressPercentage;
change_PGBar(e.ProgressPercentage);

        }

delegate void pghelper(int ProgressPercentage);
void change_PGBar(int ProgressPercentage) //ich geh jetzt mal von int aus
{
if (invokerequired)     
{
      pghelper del = new pghelper(change_PGBar);
      this.invoke(del,ProgressPercentage); //ich glaube so oder so ähnlich ist die Syntax hier
}
else
{
progressBar1.Value = ProgressPercentage;
}
}


Ändere die Funktion mal entsprechend ab. Füge den Delegate hinzu. Danach solltest zumindest den Zugriffsfehler aus dem anderen Thread heraus weghaben.
 
Zuletzt bearbeitet:
Doch durch beginninvoke verlässt du den GUI-Thread.
Btw. du könntest dich evtl. in dem zuge mit dem neuem async-Keyword beschäftigen.
 
@Magic1416

danke. Das funktioniert.
Aber wie ich mir dachte, kommt erst die Berechnung und sobald diese abgeschlossen ist, fängt die Progressbar an.
Ich muss also die Progressbar innerhalb meiner Berechnung also in der dll schon füllen oder?
Werde es mal versuchen..

@ Marguth
hab ich in meiner Suche auch schon gefunden. Nur ob ich damit mein Problem gerade lösen kann...vom logischen her klingt es ja eigentlich recht sinnvoll. Während ein Thread die Berechnung macht wird in einem anderen Thread die Progressbar gestartet und zwar solange, bis der Thread zur Berechnung fertig ist.
Da ich nicht weiß, wie ich das überhaupt realisieren kann, versuche ich es erst mal anders... irgendwie..:confused_alt:


Wobei die Progressbar starte ich ja mit backgroundWorker1.RunWorkerAsync(); und das kann ich nicht in die dll schreiben...
 
Zuletzt bearbeitet:
Die Progressbar hat innerhalb deiner dll nix verloren. Ich löse sowas immer mit einem Event in der Methode, die asynchron läuft. Das schaut dann immer ungefähr so aus: (Achtung, Code frei aus dem Kopf ohne Dev Umgebung geschrieben.

Code:
class Form1
{
      void doWork()
      {
              deineDLL dll = new deineDLL(deineDaten Daten);
              dll.PerformEvent += new PerformEventHandler(doPerform);
              
              dll.BeginAsyncWork();

      }

     delegate void performHandler (int Value); 
     void doPerform(int Value)
     {
           if (invoke required)  //für Contextwechsel
          {
                  performHandler del = new performHandler(doPerform);
                  this.invoke(del,Value);
           
           }
           else
           {
                 progressBar1.Peform(Value);
            }
     }

}


class deineDLL
{
         Event PerformEvent //genaue Syntax ist mir entfallen 

         public void BeginAsyncWork()
         {
                Thread th =new Thread(doWork); //Bin mir aussm Kopf nicht sicher ob man die Methode hier übergibt.
                th.isBackground = true;
               th.start();
         }


         void doWork()
         {
                   foreach (Daten deineDaten in DatenArray)
                   {

                         tueEtwas (deineDaten);

                         if (PerformEvent != null)
                            PerformEvent(deineDaten.Value);        //Event aufrufen. Sprung in die Methode doPerform im Form1.

                    }

         }

}



Das Beispiel wird wahrscheinlich nicht 100% richtig sein. Aber vielleicht hilft es dir, eine Lösung für dein Problem zu finden.
 
Damit ich weiß, ob ich auf dem richtigen Pfad bin.
Also ich bin gerade in der dll und habe dort zwei Klassen.
Klasse Initialisierung und die Klasse RFID.

Sobald über den Port Daten ankommen wird in der Klasse folgende MEthode aufgerufen:

Code:
 private void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {

                string indata = "";
                
                SerialPort sp = (SerialPort)sender;
                indata = sp.ReadExisting();

                if (indata.Length != 0)
                {
                    if ((indata.Length == 878) && ((indata[0] == '7878') || (indata[0] == '7878')))
                    {
                       
                    }
                        receiveEvent2.Received(PictureFile);
                       
                    }
                }
            }
            catch (Exception ex)
            {
                string Message = ex.Message;
            }
        }

Nun wollte ich am Anfang der Methode einen asnychronen Thread starten. In diesem Thread wird ein Counter hochgezählt und zwar solange, bis ich am Ende der Methode den asynchronen Thread beende.
Der Counter ist dann sozusagen mein UpdateHandler für die Progressbar.
Diesen Handler muss ich halt dann durchgehend in meine Form-Klasse schicken um dort die ProgressBar anzuzeigen..


Wird/kann das so funktionieren?

PS: sry da haben wir uns überschnitten. Lese mal eben deinen Beitrag :)
Ergänzung ()

aber ich kann doch meine ganzen Methoden nicht in eine foreach packen das würde ja nicht wirklich was bringen..
evtl. versteh ich es aber auch gerade nicht.
Alle Beispiele die ich mir im Internet angesehen habe, haben eine foreach oder eine for schleife um die Bar hochzuzählen aber wie soll ich das denn mit meinen Methoden zur Berechnung verbinden?

Da dachte ich halt ich starte einen asnychronen Prozess der neben der Berechnung läuft und einfach irgendetwas hochzählt..

An der Stelle springe ich übrigens wieder in die Form zurück und übergebe die Daten:
" receiveEvent2.Received(PictureFile); "
 
Zuletzt bearbeitet:
Die Frage ist, ob eine Progressbar dann überhaupt das richtige ist. Schließlich musst Du ja wissen, was 100 % sind - sprich wieviele Objekte verarbeitet werden müssen.
Was du noch machen kannst, ist die Progressbar z.b. in 10 Schritte zu unterteilen. In deinem Code fügst Du dann nach jeder Stelle, die etwas dauert, den Perform Eventaufruf ein. Das schaut dann ungefähr so aus.

Code:
public void BeginAsync()
{
           tueEtwas1();
           doPerform();

           tueEtwas2();
           doPerform();

           ....

          tueEtwas10();
          doPerform();

          BeendeThread();


}

Damit hast Du wenigstens keinen "Pseudo ProgressBar"
 
Ich wollte ja nur eine Progressbar, damit man überhaupt sieht, dass das System etwas macht und man nicht denkt, es funktioniert nichts.

Mein Vorschlag mit dem asynchronem Thread würde nicht funktionieren?

-- Berechnung Start--
asyn Methode starten
..berechnung...
berechnung...
....berechnung fertig..
async Methode stoppen
Wert übergeben

In der async Methode mit einer While Schleife eine Zahl hochzählen und weiterreichen an die Bar.
 
Eventuell wäre Timer.Tick noch eine Lösung, wenn Dir das mit den Einzelschritten nicht gefällt. Würde dies dann auch vollständig in der dll Implementieren
Ergänzung ()

Vielleicht ist es ja in deiner DLL ja auch möglich die Anzahl der Received Bytes auszulesen. Dann könntest z.b. mit Timer.Tick, im Sekundentakt dir die Anzahl der Bytes holen, und lieber dies ins Form schreiben. Dann steht halt sowas da wie: Currently received: 25683 Bytes
Die Zahl aktualisiert sich sekündlich und der User sieht was.
 
Ich lese ja nur einmal die Bytes aus und dann folgt die Rechnung.
Es handelt sich um einen RFID Gerät. Sobald ein entsprechender Chip aufgelegt wird, startet das Event und die Daten werden alle ausgelesen. Anschließend nutze ich die Daten für meine Berechnungen.
 
Der beste Weg das zu lösen wurde ja eigentlich schon genannt auch wenn Magic sich wieder davon entfernt hat ;). Aber ich gehe davon aus, das in diesem Fall eher entscheidend ist, dass es Funktioniert. Damit wäre die einfachste Version den BackgroundWorker in die Methode mit zu übergeben und dort dann den "ProgressChanged" aufzurufen.

Wenn ich das richtig verstanden habe, wird das erste mal bestimmt wie viele Teile es gibt und jedes weitere mal davon ausgehend die Progressbar befüllt. Wenn beides in der gleichen Methode abläuft kannst du den UserState dazu verwenden die beiden Fälle zu unterscheiden.

Beste Grüße und Viel Erfolg ;)
 
Finde das Thema gerade furchtbar kompliziert. Backgroundworker Threads async await Progressbar etc....
Aber ProgressChanged wird doch innerhalb einer Schleife "gefüllt". Ich habe aber keine Schleife...
Oder meinst du, ich soll es wie in Post 7 machen?
 
Das ganze Async Thema ist nicht einfach zu verstehen. Es gibt mehrere Möglichkeiten die zum Ziel führen. Das Schlüsselwort Async kannte ich selbst noch nicht.
Fakt ist doch aber, dass Du eine Funktion hast, die eine unbestimmte Zeit läuft und nur einmal durchlaufen wird. Für eine ordentliche Progressbar musst Du aber wissen, was 100% sind. Da du das nicht weißt, kannst Du es so machen wie ich es in Post 7 beschrieben habe. Nachteil ist, dass wenn in deinem Fall die CPU Zeit ausschließlich bei der Zeile:

receiveEvent2.RFIDDataReceived(PictureFile);

verbraucht wird, dann kannst Du die Lösung auch nicht nutzen, da dein Balken bis 90 % rauscht und dann nichts passiert. Daher der zweite Vorschlag, eben auf die gelesenen Bytes zu gehen. Während diese o.g. Funktion läuft, füllt sich doch irgend ein Puffer z.b. irgend ein Byte Array. Und genau dessen Füllstand kannst Du z.b. mittels Timer.Tick sekündlich abfragen. z.b:

DatenArray.Count oder Length

der Inhalt des Puffers ist doch in dem Moment völlig uninteressant. Die Länge (entspricht Größe der gelesenen Bytes) die da rauskommt, zeigst Du dem User an, solange wie der Lesevorgang eben dauert.
 
Es tut mir wirklich leid aber ich kann dir da nicht folgen. Was für ein Puffer soll hier denn existieren? Es ist ja nicht so, dass der Wert immer größer wird.
Als kleines Beispiel:

Folgende Daten kriege ich rein: "P<CPSName<<Nachname
Diesen Wert zerlege ich und in der Form erscheint später Typ: P Code: CPS Name:Name Nachname:Nachname.

Zu den gelesenen bytes kommen keine weiteren hinzu.
Und wenn ich selber einen Puffer erstellen soll, ist dies doch das selbe wie von dir im Post7 beschrieben...
 
Hmmm, ich bin davon ausgegangen, dass da etwas größeres kommt. Die Variable PictureFile klingt nämlich danach. Wie lange dauert denn der Lesevorgang eigentlich ?

Btw: Mit RFID und Co. sowie Com Schnittstellen habe ich noch nichts gemacht. Daher versuche ich das ganze generisch zu erklären. Vielleicht komm ich deswegen vom Thema ab K.A.
 
Zuletzt bearbeitet:
Kommt darauf an welche Berechnung durchgeführt werden muss. 3-5 Sekunden ca.
 
-Rayz- schrieb:
Finde das Thema gerade furchtbar kompliziert. Backgroundworker Threads async await Progressbar etc....
Im Gesamten kann das Ganze wirklich sehr unübersichtlich werden, aber genau da ist der BackgroundWorker sinvoll. Du kannst mit diesem ohne groß Nachzudenken einen Thread öffnen und mit diesem Arbeiten. Du musst dich nicht groß drum kümmern. Es macht auf Grundlage der ersichtlichen Programmiererfahrung keinen Sinn sich mit async oder dem "manuellen arbeiten mit Threads" zu beschäftigen.

-Rayz- schrieb:
Aber ProgressChanged wird doch innerhalb einer Schleife "gefüllt". Ich habe aber keine Schleife...

Ich bin etwas verwirrt, da du ja eigentlich mit einem BackgroundWorker gestartet hast wieso das jetzt ein Problem sein soll. Wenn dort noch Nachholbedarf besteht, am besten bei MSDN nachlesen.
 
Zuletzt bearbeitet: (Grammatik damit :P)
den Worker starte ich ja mit backgroundWorker1.RunWorkerAsync();
diesen kann ich in der dll aber nicht aufrufen...
 
Bitte lese dir die Beischreibung bei MSDN durch. Es fehlt hier am grundlegenden Verständnis.

Einmal kurz ein "run-through":
Mit dem Backgroundworker kannst du in deinem Programm einen extra Thread erstellen. In diesem Thread kannst du eine Methode aus deiner DLL aufrufen (Anders herum macht das keinen Sinn, denn der BackgroundWorker soll ja an die GUI "Informationen übergeben").* Der Methode übergibst du nun deinen Backgroundworker und dort rufst du wiederum die Methode ReportProgress auf.

Habe versucht das einigermaßen verständlich darzustellen, es ist mir zumindest ein klein wenig gelungen ;). Wie gesagt lese dir bitte durch was hier verlinkt wird, wenn jemand einen Link postet dann deshalb, weil dort relevantes steht, das du selbst verarbeiten musst.

* Damit das funktioniert müssen die Threads synchron sein, dies nimmt dir der BW ab in dem er die Methode ReportProgress bereit stellt. Diese löst den Event ProgressChanged aus, was "automatisch" synchronisiert ist mit dem "Abfangenden" Thread.
 
Zuletzt bearbeitet: (ein paar Wörter waren unglücklich gewählt :))
-Rayz- schrieb:
Ich wollte ja nur eine Progressbar, damit man überhaupt sieht, dass das System etwas macht und man nicht denkt, es funktioniert nichts.
Das geht ganz einfach:
Code:
myProgressBar.Style = ProgressBarStyle.Marquee;
 
Zurück
Oben