Thread und Control.Update()

r0cka

Grünschnabel
Hi,

ich habe ein kleines Verständnisproblem bzgl. dem updaten einer Form, vielleicht könnt ihr mir helfen.
Und zwar möchte ich aus einer Methode heraus, welche relativ Zeitintensiv ist eine Form aufrufen, die ein Statustext anzeigt. Die aufgerufene Form blockiert logischer Weise, da sie im gleichen Thread läuft wie die Methode. Bis jetzt umgehe ich dieses Problem in dem ich ein Form.update() aufurfe:

Code:
public void meineLangeMethode(string filepath)
{
LadeExcelDatei(filepath)

FrmBitteWarten frmWait = new frmWait();
frmWait.show();

for (int i = 0; i < exceldateiEnde; i++)
{
frmWait.Write(i.ToString());
frmWait.Update();
//proccesing excel data
}
frmWait.Close()
}


Die Methode frmWait.Write(string Text) in FrmWait schreibt lediglich ein Labeltext.
Ich denke mal das ist nicht die sauberste Art und Weise, aber ich bekomme es mit delegates und invokes einfach nicht gebacken. Wenn jemand vielleicht dazu einen Ansatz hat wäre das super.

Vielen Dank und Grüße
Philipp
 
Hallo,

das Invoke in ein Control kannst du so machen:
Code:
MethodInvoker invoke = delegate
{
      //hier die Controls aus dem Form ändern..
};
this.Invoke(invoke);

Die aufgerufene Form blockiert logischer Weise, da sie im gleichen Thread läuft wie die Methode.
Du könntest die Instanz der FrmBitteWarten-Klasse schon im gleichen Thread starten, in dem das HauptForm läuft, und dann mit obigem Code das Form.Show() in das FrmBitteWarten-Form invoken.
Also dann z.B. so:
Im gleichen Thread in dem das Hauptform läuft (als globale Variable (ich weiß nicht, ob man das in C# so nennen kann, aber eine, die in der ganzen Klasse gültig ist), um dann im anderen Thread darauf zugreifen zu können):
Code:
FrmBitteWarten frmWait = new FrmBitteWarten();
Und in dem anderen Thread aus dem du an dem Form etwas ändern willst:
Code:
MethodInvoker invoker = delegate
{
      frmWait.Show();
}
frmWait.Invoke(invoker);



Und noch was zu deinem Code:
FrmBitteWarten frmWait = new frmWait();
frmWait.show();
Dieser Code ist aber nicht aus deinem Source-Code kopiert oder? :rolleyes:

MfG
Fabsch
 
Hi Fabsch,

ersteinmal danke für deine Antwort.

Nein der code ist nicht kopiert, den hab ich nur zur Veranschaulichung erstellt.

Ich habe dein Beispiel umgesetzt, jedoch bekomme ich dann die Meldung: "invoke oder begininvoke kann erst ausgeführt werden, wenn das Fensterhandle erstellt wurde".
Code:
class ExcelTools
{
  FrmPleaseWait frmPleaseWait = new FrmPleaseWait();

public void ImportFromExcel(FileInfo fleLoan)
{
  MethodInvoker invoker = delegate { frmPleaseWait.Show(); };
  .
  .
  .
  frmPleaseWait.Invoke(invoker);
  .
  .
  .
}
}

Was mache ich falsch?

Grüße und danke für Hilfe
Philipp
 
Hallo,

ich denke, du musst das Fenster zuerst mit
Code:
frmPleaseWait.Show();
oder
Code:
frmPleaseWait.ShowDialog();
anzeigen, bevor du ein Invoke ausführst!

MfG
Fabsch
 
Hi,

Die Meldung kommt nicht mehr jedoch blockiert das geöffnete Statusfenster immer noch.
Mir ist auch nicht ganz klar, wie das so funktionieren soll. Ich habe die Funktion zum schreiben in den invoker gepackt. Ungefähr so:

Code:
MethodInvoker invoker = delegate { frmPleaseWait.WriteStatus("staring import"); };
frmPleaseWait.Show();


loop
invoker.invoke();
...do the import
end loop


Wie kann ich dem invoker eine Variable übergeben und warum blockiert das Fenster immernoch?

Grüße und Danke
Philipp
 
Dein Fenster blockiert, weil du die Verarbeitung nicht in einem eigenen Thread laufen lässt.

Wenn du während deiner Verarbeitung dem User anbieten willst, weitere Dinge zu tun, dann gibt es da ein paar Ansätze deine Aktionen asynchron auszuführen. Die einfachste wird wohl sein, das ganze über ein WaitCallback-Objekt unter zuhilfenahme des ThreadPool zu realisieren:

Code:
public class MyClass
{
   AsyncOperation asyncOperation;
   public void Start(object whatEver)
   {
      asyncOperation = System.ComponentModel.AsyncOperationManager.CreateOperation(whatEver);

            WaitCallback wc = new WaitCallback(startImportAsync);
            ThreadPool.QueueUserWorkItem(wc, whatEver);   
   }   

   private void startWork(object args)
   {
      try{
         /* Tu irgendwas */
         if (asyncOperation != null)
            asyncOperation.PostOperationCompleted(complete, args);
      }
      catch(Exception ex)
      {
         if (asyncOperation != null)
            asyncOperation.PostOperationCompleted(aborted, args);
      }

   }

   private void complete(object args)
   {  /* Methode die nach Beendigung aufgerufen wird */}
   
   private void aborted(object args)
   {  /* Methode die bei einem Fehler aufgerufen wird */}
}

Wenn du deiner Klasse dann noch Events spendierst die gefeuert werden wenn die Verarbeitung beendet ist, dann kannst du im Frontend dementsprechend drauf reagieren.


-> Achtung: Ungetestet Vom Prinzip her aber in Ordnung. Notfalls nochmal nachfragen oder :google: nach AsyncOperation und WaitCallback
 
Zurück