BackgroundWorker/Thread und Progressbar



  • Hallo,

    ich habe ein frmMainForm aus der ich einen Progressbar aufrufen möchte der in einem eigenen Thrad laufen soll, damit er das frmMainForm nicht blockiert.

    Habe dazu bissel was gefunden. Ich habe eine neues windowsforms frmProgressbar angelegt.

    #pragma once
    
    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;
    using namespace System::Threading;
    
    namespace projekt {
    
    	 using namespace System;
     using namespace System::ComponentModel;
     using namespace System::Collections;
     using namespace System::Windows::Forms;
     using namespace System::Data;
     using namespace System::Drawing;
    
     public ref class frmProgressBar : public System::Windows::Forms::Form
     {
     public:
      frmProgressBar(int)
       : numberToCompute(0)
      {
       InitializeComponent();
       //
       //TODO: .
       //
    
       numberToCompute = 0;
    
       MAXNUM = 100000;  //Max number to increase
       this->progressBar1->Maximum = MAXNUM ;
       InitializeThread();
      }
    
     protected:
      /// <summary>
      /// 
    
      /// </summary>
      ~frmProgressBar()
      {
       if (components)
       {
        delete components;
       }
      }
     private: System::Windows::Forms::ProgressBar^  progressBar1;
     private: System::ComponentModel::BackgroundWorker^  backgroundWorker1;
     protected:
    
     private:
      /// <summary>
      /// 
    
      /// </summary>
      System::ComponentModel::Container ^components;
    
    #pragma region Windows Form Designer generated code
      /// <summary>
      /// 
      /// 
    
      /// </summary>
      void InitializeComponent(void)
      {
       this->progressBar1 = (gcnew System::Windows::Forms::ProgressBar());
       this->backgroundWorker1 = (gcnew System::ComponentModel::BackgroundWorker());
       this->SuspendLayout();
       //
       // progressBar1
       //
       this->progressBar1->Location = System::Drawing::Point(44, 147);
       this->progressBar1->Name = L"progressBar1";
       this->progressBar1->Size = System::Drawing::Size(236, 23);
       this->progressBar1->TabIndex = 0;
    
       this->progressBar1->Step = 1;
       //
       // Create backgroundWorker1
       // 
       this->backgroundWorker1->DoWork += gcnew System::ComponentModel::DoWorkEventHandler(this, &frmProgressBar::Worker);
       //
       // frmProgressBar
       //
       this->AutoScaleDimensions = System::Drawing::SizeF(7, 12);
       this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
       this->ClientSize = System::Drawing::Size(292, 273);
       this->Controls->Add(this->progressBar1);
       this->Name = L"frmProgressBar";
       this->Text = L"frmProgressBar";
       this->ResumeLayout(false);
    
      }
    #pragma endregion
     public:
    
      void InitializeThread(void)
      {
    
          //Add Worker Thread Handler
          this->backgroundWorker1->DoWork += gcnew DoWorkEventHandler(this,   
    
                               &frmProgressBar::Worker);
    
       //Add ThreadCompleted Handler that occures when thread handler exited.   
       this->backgroundWorker1->RunWorkerCompleted += gcnew
    
          RunWorkerCompletedEventHandler(this, &frmProgressBar::ThreadCompleted);
    
       //Set Report Progress Activate 
    
       this->backgroundWorker1->WorkerReportsProgress = true;
    
       //Add ProgressChanged Event Handler
    
       this->backgroundWorker1->ProgressChanged += gcnew   
    
          ProgressChangedEventHandler(this, &frmProgressBar::ProgressChanged);
    
       //Run WorkerThread
    
       this->backgroundWorker1->RunWorkerAsync(numberToCompute);
      }
    
     private:
    
    System::Void Worker(System::Object^  sender, System::ComponentModel::DoWorkEventArgs^  e)
        {
         BackgroundWorker^ worker = dynamic_cast<BackgroundWorker^>(sender);
    
         e->Result = repair(0, worker, e);
    
        }
    
        void ThreadCompleted(Object^ /*sender*/ , RunWorkerCompletedEventArgs^ e)
        {
        // throw(gcnew System::NotImplementedException);
         if(e->Error != nullptr){
    
          MessageBox::Show(e->Error->Message);
    
         }else{
    
          //operation succedded, now we can continue
    
          this->progressBar1->Increment(100);
    
         }
    
        }
     public:
    
      void ProgressChanged(Object^ /*sender*/, ProgressChangedEventArgs^ e)
      {
    
          //Reset Progress Ctrl's Value
          this->progressBar1->Value = numberToCompute;
    
      }
    
      bool repair(int n, BackgroundWorker^ worker, DoWorkEventArgs^ e)
      {
    
    //Increase numberToCompute, While numberToCompute value is equal to MAXNUM
       while (1)
       {
        if (numberToCompute == MAXNUM)
        {
            return false;
        }
        else
        {     
            numberToCompute++;
    
            worker->ReportProgress(numberToCompute);
        }
    
       }
    
      }
      int numberToCompute;
    
      int MAXNUM;
    };
    }
    

    Von meiner frmMainForm rufe ich dann bei einem Button klick folgendes auf:

    frmProgressBar^ bar = gcnew frmProgressBar(5);
    bar->show();
    

    Der Progressebar wird auch gestartet, jedoch scheint er nicht in einen eigenen Thread zu laufen.

    Hat jemand eine Idee?

    Danke schonmale

    gruß
    Entilzah



  • Ich denke, Du hast ein Problem mit den Dimensionen. Dein Code ist so schnell fertig, dass der Progressbar immer gerade schon voll ist.

    Füge mal eine Simulierte Verzögerung ein (in der while(1) Schleife):

    numberToCompute++; 
    if (numberToCompute % 100 == 0)
    {
    	System::Threading::Thread::Sleep(10);
    }
    
    worker->ReportProgress(numberToCompute);
    

    Simon



  • sorry, leider funktioniert das auch nicht. Beide Fenster sind eingefrohren.



  • Hmm... komisch, denn bei mir funktioniert dein Code.
    Habe ihn kopiert und ausprobiert.

    Simon



  • ah es scheint an was anderem zu liegen.

    In der frmMainForm erstelle ich ein Object von der Klasse HtmlRequest welche unter anderem Webseiten auf ihre Erreichbarkeit prüft.
    Solange diese Prüfungen laufen, friert natürlich das mainForm ein.

    Kann ich das HtmlRequest Objekt nicht einfach in einen eigenen Thread laufen lassen ohne die Klasse HtmlRequest ändern zu müssen?

    gruß entilzah



  • Kann der BackgoundWorker nur bei Forms angewendet werden? Denn die htmlRequest ist kein Form

    Ist es möglich die Klasse htmlRequest von einer Thread Klasse erben zu lassen und somit jedesmal htmlRequest in einen eigenen Thread laufen zu lassen?

    Entilzah



  • ich habe es fast gelöst 🙂

    Also ich habe ne Funktion DoWork erstellt und rufe die nach einen Buttonklick wie folgt auf:

    newThread = gcnew Thread(gcnew ThreadStart(this, &frmMainForm::DoWork));
     newThread->Start();
    

    In der DoWork arbeite ich die Aufgaben ab. Ich möchte in der DoWork gerne die Funktion updateListView aufrufen damit der listView nach dem durcharbeiten der Aufgaben upgedatet wird. Problem ist wenn ich es in der DoWork Funktion mache, bekomme ich die Meldung:

    Zusätzliche Informationen: Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement listView1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.
    

    packe die das upateListView hier rein:

    newThread = gcnew Thread(gcnew ThreadStart(this, &frmMainForm::DoWork));
     newThread->Start();
    updateListview()
    

    habe ich das Problem, dass updateListview gleich ausgeführt wird und nicht wenn der Thread fertig ist.

    Gibt es eine Möglichkeit nach dem Ende eines Threads eine Methode aufrufen zu lassen?

    Oder gibt es eine Möglichkeit in der DoWork auf den listView des mainThread zuzugreifen?

    mfg
    entilzah



  • Erstelle eine Methode, die die das Updaten übernimmt. Innerhalb dieser Methode prüfe auf InvokeRequired und rufe dann ggf. Invoke oder BeginInvoke auf.

    InvoreRequired sagt Dir ob der Aufruf aus einem anderen Thread kam, Invoke bzw. BeginInvoke schiebt den Aufruf in den UI Thread rein.



  • Knuddlbaer schrieb:

    Erstelle eine Methode, die die das Updaten übernimmt. Innerhalb dieser Methode prüfe auf InvokeRequired und rufe dann ggf. Invoke oder BeginInvoke auf.

    InvoreRequired sagt Dir ob der Aufruf aus einem anderen Thread kam, Invoke bzw. BeginInvoke schiebt den Aufruf in den UI Thread rein.

    danke es läuft ! 🙂

    mfg
    entilzah


Anmelden zum Antworten