2010-12-22 2 views
3

[EDIT] umformuliert und Simplified ganzen Beitrag [/ EDIT]Wie ein Synchronisationskontext für die zweite Form erhalten gezeigt

In diesem blog, die folgenden (ich es ein wenig vereinfacht) gegeben ist als ein Beispiel eines SynchronizationContext Ziel der Verwendung eine Aufgabe auf dem UI-Thread ausgeführt wird:

Task.Factory.StartNew(() =>"Hello World").ContinueWith(
      task => textBox1.Text = task.Result, 
      TaskScheduler.FromCurrentSynchronizationContext()); 

ich diese Ergebnisse in einem neuen Projekt wiederholen, die Benutzeroberfläche sicher zu aktualisieren, aber aus irgendeinem Grunde in meinem aktuellen Projekt (auch wenn es war funktioniert) Ich kann nicht. Ich bekomme den Standard "Du darfst nicht die UI von der falschen Thread-Ausnahme aktualisieren".

Mein Code (in MainForm_Load (...)) ist wie diese, die w/a textBox1 hinzugefügt, um die Haupt-Form in einem frischen Projekt arbeitet, funktionieren aber nicht in meinem aktuellen Projekt:

var one = Task.Factory.StartNew(
     () => "Hello, my name is Inigo Montoya"); 
var two = one.ContinueWith(
     task => textBox1.Text = one.Result, 
     TaskScheduler.FromCurrentSynchronizationContext()); 

Jeder hat irgendwelche Gedanken darüber, was Gong sein könnte.

[EDIT]

ich den Fehler zurückverfolgt haben zurück in die Instanziierung eines Objekts, das ein Formular verwendet die Benutzer-Anmeldeinformationen aufzufordern. Der Fehler tritt nur auf, wenn das Formular angezeigt wurde. (Wenn ich einen fest codierten Wert zurückgebe, bevor die Show dieses Formulars passiert, funktioniert die ganze Sache gut).

Neue Frage: Wie bekomme ich den SynchronizationContext für das Formular, das ich konstruiere, wenn sein eigener Konstruktor ein anderes Formular anzeigt, bevor es angezeigt wurde? Hier ist, wie können Sie reproduzieren, was passiert:

1) Erstellen Sie zwei Formulare: Form1 mit einem TextBox und Form2 mit einem Button

2) Erstellen Sie eine Klasse OwnedBy1Uses2

Form1:

public partial class Form1 : Form 
{ 
    OwnedBy1Uses2 member; 
    public Form1() 
    { 
     InitializeComponent(); 
     member = new OwnedBy1Uses2(); 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     var ui = TaskScheduler.FromCurrentSynchronizationContext(); 
     Task<string> getData = Task.Factory.StartNew(
      () => "My name is Inigo Montoya..."); 
     Task displayData = getData.ContinueWith(
      t => textBox1.Text = t.Result, ui); 
    } 
} 

:

public partial class Form2 : Form 
{ 
    public Form2() 
    { 
     InitializeComponent(); 
     DialogResult = System.Windows.Forms.DialogResult.Cancel; 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     DialogResult = System.Windows.Forms.DialogResult.OK; 
     Hide(); 
    } 
} 

OwnedBy1Uses2:

class OwnedBy1Uses2 
{ 
    int x; 
    public OwnedBy1Uses2() 
    { 
     using (Form2 form = new Form2()) 
     { 
      if (form.ShowDialog() == System.Windows.Forms.DialogResult.OK) 
      { 
       x = 1; 
      } 
      else 
      { 
       x = 2; 
      } 
     } 
    } 
} 
+0

Ich dachte, der ganze Sinn einer Aufgabe war es asynch und per Definition, die in einem anderen Thread sein sollte. Sie können auf ein Steuerelement aus einem anderen Thread nicht zugreifen, wie Sie wissen. –

+0

Weitere Details: Ich lade Objekte aus einer Datenbank Asynch (sehr langwieriger Prozess). Wenn das erledigt ist, habe ich "displayItems" als einen sehr schnell laufenden Prozess definiert, der die Benutzeroberfläche aktualisiert. Der * andere * Punkt einer Task ist das Timing/Ordering von asynchronen Prozessen/Threads. Dies ist die Aufgabe von displayItems: "Wenn previousTask fertig ist, zeigen Sie die geladenen Elemente an" – Crisfole

+0

Wenn Sie den System.Threading.Task-Namespace verwenden, verwendet Ihre Aufgabe einen CLR-Thread-Pool-Thread. Dies ist also nicht mehr Ihr UInthread . Überprüfen Sie diese: http://www.eggheadcafe.com/tutorials/aspnet/21013a52-fe11-4af8-bf8b-50cfd1a51577/task-parallelism-in-c-4.aspx –

Antwort

4

einfach auf dem Haupt-Thread zu sein, ist nicht ausreichend. Sie müssen eine gültige SynchronizationContext.Current (setzen Sie einen Haltepunkt auf der FromCurrentSynchronizationContext Linie und überprüfen Sie den Wert von SynchronizationContext.Current; wenn es null ist, dann stimmt etwas nicht).

Die sauberste Lösung ist Ihre Aufgabe Code einschließlich FromCurrentSynchronizationContext innerhalb der UI Nachrichtenschleife auszuführen - das heißt, von so etwas wie Form.Load für WinForms oder Window.Loaded für WPF.

Edit:

Es gab in WinForms einen Bug, wo es in Form.Load setzen entweder nicht ausreichend war - man hatte tatsächlich Win32 Griff Schöpfung zu zwingen, durch die Handle Eigenschaft zu lesen. Ich hatte den Eindruck, dass dieser Fehler behoben wurde, aber ich könnte mich irren.

Edit 2 (von Kommentar kopiert):

ich Ihr Problem vermuten ist, dass Sie ShowDialog außerhalb von Application.Run sind aufgerufen wird. ShowDialog ist eine verschachtelte Nachrichtenschleife, aber in diesem Fall gibt es keine übergeordnete Nachrichtenschleife. Wenn Sie eine Uhr auf SynchronizationContext.Current setzen und durch die ShowDialog gehen, sehen Sie, dass es eine WindowsFormsSynchronizationContext ist, bevor das Dialogfeld angezeigt wird, aber zu einem Nicht-WinForms SynchronizationContext ändert, nachdem das Dialogfeld angezeigt wird. Durch das Verschieben der Elementerstellung (einschließlich ShowDialog) in das Ereignis Load wird das Problem behoben.

+0

Mein Code * wird * derzeit von einer UI-Nachrichtenschleife ausgeführt ... Er befindet sich in einem automatisch generierten Handler für ein TreeView SelectedChanged-Ereignis. Außerdem, wenn ich anhalte, bekomme ich einen Wert, es ist einfach nicht "null". Es ist direkt aus Form.Load auch. – Crisfole

+0

Wenn Sie eine Instanz von "WindowsFormsSynchronizationContext" sehen, dann senden Sie bitte Ihren genauen Code. –

+0

Mein genauer Code ist mehrere tausend Zeilen lang ... Ich werde daran arbeiten, eine Kopie zu bekommen, die in einer Sekunde separat ausgeführt werden kann. Ich habe den Fehler auf die Erstellung eines Objekts beschränkt, das ein anderes Formular erstellt, bevor das Hauptformular erstellt wird. Würde das Pfusch "SynchronizationContext.Current" einige wie? (Es ist ein Login-Formular, und muss vor diesem angezeigt werden) – Crisfole

Verwandte Themen