2016-07-13 17 views
0

So habe ich diese App mit einem MainForm, die ein paar Tasten darauf hat. und die Schaltflächen würden zeitaufwendige Aufgaben erledigen, die die Arbeit mit der MainForm-Benutzeroberfläche einschließen. Da wir manchmal mehrere Instanzen dieser App gleichzeitig ausführen müssen, entschied ich mich, einen MainFormHost zu erstellen, wo es ein Formular mit einem Tab-Steuerelement ist, das unter jedem Tab eine Instanz von meinem MainForm erstellt und dort hostet. Und bisher war alles in Ordnung. Das Problem ist, wenn ich auf eine Schaltfläche auf MAinForm1 klicke, funktioniert es gut, aber sobald ich auf eine Schaltfläche auf MainForm2 klicke, wird der Prozess der MainForm1-Schaltfläche hinter dem MainForm2-Prozess in die Warteschlange gestellt.Hosten mehrerer Instanz der App in einem Tabcontrol/Warteschlange

MainForm GetMainFrom(TabPage tabPage) 
{ 
tabPage.Invoke(new Action(() => 
{ 
    mainForm = new MainForm(); 
    mainForm.TopLevel = false; 
    mainForm.FormBorderStyle = FormBorderStyle.None; 
    mainForm.Dock = DockStyle.Fill; 

    _mainForms.Add(mainForm); 
    tabPage.Controls.Add(mainForm); 
    mainForm.Show(); 

})); 
} 

Und dann rufen Sie die Methode:

var mainFormThread = new Thread(() => 
{ 
    mainForm = GetMainFrom(tabPage); 
}); 

mainFormThread.SetApartmentState(ApartmentState.STA); 
mainFormThread.Start(); 

Ich kann den Code innerhalb der Mainform ändern sich die Dinge aufzurufen, wenn sie mit der Benutzeroberfläche arbeiten wollen, weil es zu viel Arbeit sein wird, aber ich kann einfach jeden Knopf klicken macht aus einem Thread/oder Aufgabe ...

TIA

Antwort

0

kurz genannt werden, nur einen ‚Haupt-Thread‘ haben kann, die mit dem UI-Steuerelemente in Wechselwirkung treten können. Je. Zeitraum. Es gibt keine Möglichkeit, dies zu umgehen (im Standard-Code können Sie aus einem anderen Prozess auf Ihr Formular zeichnen, aber ich werde nicht darauf eingehen)

Also, was Sie tun müssen, ist ordnen Sie Ihren Code in einem Muster setzen.

  • Methoden, die Hintergrundarbeit zu tun - dies kann auf einem separaten Thread (s)
  • Methoden durchgeführt werden, die die UI-Update - muss auf dem UI/Hauptthread

Die Idee ist, getan werden einfache, Hintergrund-Arbeit dauert sehr lange, und das Aktualisieren der Benutzeroberfläche sollte nicht.

Da Sie 1 oder 2 lange laufende Aktionen auf dem Hauptschnittfeld ausgeführt haben, blockiert dies die Benutzeroberfläche und verursacht das Verhalten, das Sie erleben.

Durch blockiert die UI, was Sie eigentlich haben, ist die Windows-Nachricht Pumpe kann nicht pumpen. Jeder einzelne Befehl zum Ändern der Größe eines Fensters oder Aktualisieren eines Steuerelements in einem Formular ist eine Nachricht in der Pumpe. Das muss auf dem Hauptthread laufen. Wenn Sie mit diesem Thread "arbeiten", z. B. eine Verbindung zu einer Datenbank herstellen, eine Datei herunterladen usw., kann die Pumpe nicht fortgesetzt werden, sodass Sie die Meldung "Diese Anwendung reagiert nicht" erhalten.

So müssen Sie die Datei oder was auch immer in einem Thread herunterladen, und wenn es fertig ist, Übergang zum UI-Thread, und aktualisieren Sie die Benutzeroberfläche, z. sagen Sie in einem Textfeld beendet.

Der Weg zum Übergang zwischen den beiden Threads (Hintergrund zu Haupt) ist die Verwendung von begininvoke, und Sie wissen, ob dies erforderlich ist, indem Sie 'invoke required' verwenden.

Von MSDN

// This method demonstrates a pattern for making thread-safe 
 
// calls on a Windows Forms control. 
 
// 
 
// If the calling thread is different from the thread that 
 
// created the TextBox control, this method creates a 
 
// SetTextCallback and calls itself asynchronously using the 
 
// Invoke method. 
 
// 
 
// If the calling thread is the same as the thread that created 
 
// the TextBox control, the Text property is set directly. 
 

 
private void SetText(string text) 
 
{ 
 
\t // InvokeRequired required compares the thread ID of the 
 
\t // calling thread to the thread ID of the creating thread. 
 
\t // If these threads are different, it returns true. 
 
\t if (this.textBox1.InvokeRequired) 
 
\t { \t 
 
\t \t SetTextCallback d = new SetTextCallback(SetText); 
 
\t \t this.Invoke(d, new object[] { text }); 
 
\t } 
 
\t else 
 
\t { 
 
\t \t this.textBox1.Text = text; 
 
\t } 
 
}

Dies ist ein großer Artikel und erhalten Sie, wo Sie sein müssen: MSDN about thread safe code and invoke required

+0

Vielen Dank für Ihre ausführliche Erklärung.Allerdings wusste ich das meiste von dem, was du erklärt hast, aber meine Frage war, ob es einen Weg/eine Abkürzung für mich gibt, diese Art von Änderungen an meinem Code nicht vorzunehmen. Sie suchen beispielsweise nach dem Erstellen mehrerer UI-Threads oder nach dem Ausführen von zwei Instanzen meiner App. Sie funktionieren problemlos, ohne sich gegenseitig zu blockieren. Ich möchte nicht viele Fenster geöffnet haben und lieber die Instanzen unter einem Tab-Steuerelement verwaltet werden. – Alireza

+0

Abgesehen von mehreren Prozessen (im Grunde mehrere Kopien Ihrer Anwendung ausgeführt) dann nein, gibt es nicht. Sie haben ein grundlegendes Designproblem. Der einfachste Weg, dies zu korrigieren, wäre das Task-Framework. – adudley

Verwandte Themen