Laden...

Backgroundworker und Mehrsprachigkeit

Erstellt von thomas.at vor 15 Jahren Letzter Beitrag vor 14 Jahren 2.528 Views
T
thomas.at Themenstarter:in
111 Beiträge seit 2005
vor 15 Jahren
Backgroundworker und Mehrsprachigkeit

Hallo Leute

Ich habe eine Server/Client-Anwendung mit einem Pluginsystem auf der Clientseite. Diese werden über eine seperate Domain im Clienthauptprogramm geladen. In einem Modul ist eine längerdauernde Funktion, die Daten aus eine Datenbank in Excel umkopiert. Um die GUI nicht zu blockieren, wurde diese Funktion in einen Backgroundworker ausgelagert. Im Plugin wird ein Fortschrittssfenster geöffnet und der Backgroundworker gestartet. Dieser aktualisiert dann einen Fortschrittsbalken auf dem Formular. Dies funktioniert alles bestens. Nun ist mein Programm aber mehrsprachig aufgebaut (über die mechanismen von .NET, also Resourcen ...). Hier mal die Code-Relevanten Teile:

Plugin:



    private void RunBackground()
    {
      frmFortschritt = new Fortschritt(this.Bounds);
      frmFortschritt.Max = (akt + ms + 1) * frmWahl.Gewaehlt.Count;
      frmFortschritt.Closed += new EventHandler(frmFortschritt_Closed);
      frmFortschritt.Cancel = Export;
      frmFortschritt.Show();

      bgwExcel.DoWork += new DoWorkEventHandler(bgwExcel_DoWork);
      bgwExcel.ProgressChanged += new ProgressChangedEventHandler(bgwExcel_ProgressExport);
      bgwExcel.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgwExcel_ExportCompleted);
      bgwExcel.WorkerReportsProgress = true;
      bgwExcel.WorkerSupportsCancellation = true;
      bgwExcel.RunWorkerAsync(Export);
    }
 
    private void bgwExcel_ProgressExport(object sender, ProgressChangedEventArgs e)
    {
      Show_Fortschritt();
    }

    private void bgwExcel_ExportCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
      if ((bool)e.Result)
      {
        frmFortschritt.Set_Text(Properties.Resources.DatenWurdenExportiert);
        //Show_Text(Properties.Resources.DatenWurdenExportiert);
      }
    }
		
    delegate void SetTextCallback(string text);

    private void Show_Text(string Text)
    {
      if (frmFortschritt != null)
      {
        if (frmFortschritt.InvokeRequired)
        {
          SetTextCallback d = new SetTextCallback(Show_Text);
          frmFortschritt.Invoke(d, new object[] { Text });
        }
        else
          frmFortschritt.Set_Text(Text);
      }
    }

    private void Show_Fortschritt()
    {
      if (frmFortschritt != null)
      {
        // if (frmFortschritt.InvokeRequired)
        // {
        //   frmFortschritt.Invoke(new MethodInvoker(Show_Fortschritt));
        //   return;
        // }
        frmFortschritt.Aktuell = frmFortschritt.Aktuell + 1;
        frmFortschritt.Set_Text();
      }
    }

Fortschrittssdialog:


    public int Aktuell 
    { 
      get { return (pgbFortschritt.Value); } 
      set { pgbFortschritt.Value = value; }       // <-- Hier Fehler
    }

    public void Set_Text(string Text)
    {
      label1.Text = Text;                                 //<-- Hier Fehler
      btnCancel.Text = "OK";
    }


Wenn ich das Programm jetzt starte, dann bekomme ich beim Aktualisieren des Fortschrittbalkens (im ProgressChanged) und beim Completed die Fehlermeldung : "Ungültiger threadübergreifender Vorgang". Wenn ich dann den auskommentierten Teil einbaue, dann funktioniert das Programm wieder, allerdings sind dann die Texte in Deutsch (also Defaultsprache). Kann mir jemand helfen, wie ich das Problem löse, so dass die Texte in der gewünschten Sprache ohne CrossThreadError angezeigt werden?

vielen Dank im Vorraus

Thomas

4.506 Beiträge seit 2004
vor 15 Jahren

Hallo thomas.at,

ich glaube Du hast das Baum-Wald Phänomen:

private void Show_Fortschritt()
    {
      if (frmFortschritt != null)
      {
        // if (frmFortschritt.InvokeRequired)
        // {
        //   frmFortschritt.Invoke(new MethodInvoker(Show_Fortschritt));
        //   return;
        // }
        else
        {
          frmFortschritt.Aktuell = frmFortschritt.Aktuell + 1;
          frmFortschritt.Set_Text();
        }
      }
    }

Einfach Else dazupacken, falls ich nicht noch etwas übersehen haben sollte.

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo thomas.at,

und im Allgemeinen: [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke)

herbivore

4.506 Beiträge seit 2004
vor 15 Jahren

Hallo nochmal,

das war Blödsinn von mir, Du hast ja ein Return drin, was das Else auch nicht viel besser macht 😉

Eventuell liegt das Problem bei

frmFortschritt.Set_Text();

Ich finde nur komisch, dass Du nicht die Eigenschaften und Methoden innerhalb Deiner Form Threadsicher gestaltest, so dass der Workerthread nicht darauf achten muss, ob ein "Invoke required" ist.

Also ich würde an Deiner Stelle alle Eigenschaften und Methoden, die von Threads aufgerufen werden können (im Detail z.B. "frmFortschritt.Aktuell", "frmFortschritt.Set_Text();" ...) so erweitern, dass sie dann selbst nachfragen, ob ein "Invoke required" ist.

Dann wäre die Threadsicherheit gekapselt und betrifft ALLE Threads, die sich Deiner Form begnügen.

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

T
thomas.at Themenstarter:in
111 Beiträge seit 2005
vor 15 Jahren

Hallo

das Problem wurde von mir vielleicht nicht ganz klar dargestellt. Bis zur Umstellung auf Mehrsprachigkeit (Localizable = false) hat ja alles mittels Backgroundworker funktioniert (der kümmert sich ja um das Invoke selbst). Nachdem ich aber Localizable auf true gesetzt habe musste ich das Invoke selbst noch abfragen (daher auch dies im Workerthread). Wenn ich das mache funktioniert ja alles wieder (keine Fehlermeldung) nur die Texte sind dann eben nicht in der gewünschten Sprache sondern in der Default. Für mich sieht es so aus, alsob der Workerthread eben in einem Defaultthread läuft und die Sprache noch in einem zusätzlichen Thread gestartet wird, aus dem dann der Backgroundworker arbeitet.

Thomas

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo thomas.at,

Thread.CurrentCulture
Thread.CurrentUICulture

herbivore

T
thomas.at Themenstarter:in
111 Beiträge seit 2005
vor 15 Jahren

Hallo herbivore

Danke für den Tip. Ich musste nur im Backgroundworker in der Dowork-Methode über Thread.CurrentUICulture die aktuelle Sprache setzen und schon geht alles wieder (auch ohne InvokeRequired)

Wieder einmal hat das Forum super geholfen!

S
46 Beiträge seit 2006
vor 14 Jahren

@ thomas.at:

Wie reichst du die Sprachänderung von der GUI an deinen Backgroundworker weiter?

Ich habe gerade ein ähnliches Problem mit mehreren Threads (und div. Timer, die Aktionen auslösen). Wenn ich in der GUI die Sprache ändere, so betrifft das nur den einen Thread. Mir fehlt eine Möglichkeit, für alle laufenden Threads die Sprache zu ändern.

F
10.010 Beiträge seit 2004
vor 14 Jahren

Es geht aber nicht anders.

Du musst in jedem Thread die Culture setzen.

S
46 Beiträge seit 2006
vor 14 Jahren

Ja, das ist mir mittlerweile klar, aber wie realisiere ich es, dass ich alle Threads benachrichtige? Ich habe es bisher nur geschafft, mit Thread.CurrentThread... die Culture zu setzen, weiß aber nicht, wie ich die anderen Threads "benachrichtige".

F
10.010 Beiträge seit 2004
vor 14 Jahren

Garnicht.

Du musst dies beim Erstellen eines Thread machen.
Wenn du z.b. den SmartThreadPool von Codeproject nimmst, hättest du das schon eingebaut.

3.971 Beiträge seit 2006
vor 14 Jahren

Ja, das ist mir mittlerweile klar, aber wie realisiere ich es, dass ich alle Threads benachrichtige?

Threads benachrichtigen geht nicht, dass ist der falsche Weg. Deine Threads können maximal prüfen, ob sich der Status geändert hat und darauf entsprechende Anweisungen ausführen.

Es ist aber unsinnig, während der Ausführung einer bestimmten Aufgabe, diverse relevante Daten zu ändern. Das ändern der Kultur für alle Threads könnte ungeahnte Konsequenzen hervorrufen. Es ist sinniger, dass nur neugestartete Threads bzw. welche aus dem Threadpool (direkt als erste Anweisung) die entsprechende neue Kultur sich holen. Laufende Threads müssten weiterlaufen so wie sie sind oder abgebrochen und neugestartet werden.

Eine andere Überlegung wäre, dass die Threads kultur-neutral arbeiten und erst innerhab des Gui-Threads (Control.Invoke) die entsprechende Ressource mit der aktuellen Kultur geladen wird.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

S
46 Beiträge seit 2006
vor 14 Jahren

Da hast du natürlich auch wieder Recht!

Mein Fehler ist vermutlich, dass ich meine Ressourcen zu früh hole, und zwar in den Hintergrundthreads. Vermutlich sollte ich darauf achten, sämtliche Ressourcen erst im Hauptthread (nach Invoke) abzurufen und von dort zu setzen.

Ich werde mal prüfen, ob das den gewünschten Effekt hervorruft.

Thx so far 😃

F
10.010 Beiträge seit 2004
vor 14 Jahren

Warum?

Viel einfacher ist doch, beim threadstart die Culture richtig zu setzen.

S
46 Beiträge seit 2006
vor 14 Jahren

Da es sich teilweise um Events von Timern oder Hardware-Ereignisse handelt, ist dies nicht möglich. Die Sprache meiner Oberfläche kann sich zudem jederzeit ändern.

F
10.010 Beiträge seit 2004
vor 14 Jahren

Ja und?

Wo ist da jetzt das Problem?