System.Windows.Threading.Dispatcher und WinForms?

functioniert ein System.Windows.Threading.Dispatcher auf dem UI-Thread einer WinForms Anwendung?

Wenn ja, warum? Es kommt von WindowsBase.dll, die eine WPF Komponente zu sein scheint.

Wenn nicht, wie kann ich Arbeitseinheiten wieder auf den UI-Thread aufrufen? Ich habe Control.BeginInvoke() , aber es scheint plump, ein Steuerelement zu erstellen, das nur auf den ursprünglichen Thread verweist.

Sie können den Dispatcher sogar in einer WinForms-App verwenden.

Wenn Sie sicher sind, dass Sie sich in einem Benutzeroberflächen-Thread befinden (z. B. in einem button.Click-Handler), erhalten Sie mit Dispatcher.CurrentDispatcher den UI-Thread-Dispatcher, mit dem Sie später wie gewohnt von Hintergrundthreads zum UI-Thread senden können.

Dispatcher ist eine WPF-Komponente und keine WinForms-Komponente.

Wenn Sie Arbeitselemente im UI-Thread versenden möchten, müssen Sie entweder Control.BeginInvoke verwenden, wie Sie es bereits gefunden haben, oder innerhalb von Threads auf ResetEvents / WaitObjects reagieren.

Normalerweise ist das Aufrufen von Arbeitselementen im UI-Thread eine schlechte Sache, es sei denn, es handelt sich um eine UI-Arbeitsaufgabe (z. B. Aktualisieren des Inhalts eines Steuerelements oder etwas). In diesem Fall wäre Control.BeginInvoke () ausreichend.

Ich habe ein Beispiel für die Verwendung von System.Windows.Threading.Dispatcher in Windows Form in meiner Antwort auf Frage “Parallele Programmierung mit TPL auf WinForms” seit der letzten Antwort auf Ihre Frage angegeben :

Wenn Sie sicher sind, dass Sie sich im UI-Thread befinden (z. B. in einem button.Click-Handler), gibt Ihnen Dispatcher.CurrentDispatcher den UI-Thread-Dispatcher, mit dem Sie wie gewohnt vom Hintergrundthread zum UI-Thread senden können.

ist entweder irreführend oder verwirrend oder fehlt der konkrete Nutzungskontext:

  • button.Click Handler stellt nicht sicher, dass er sich im UI-Thread befindet.
  • Wenn Sie sich nicht im UI-Thread befinden, können Sie weiterhin den Disparcher des UI-Threads des WinForms-Formulars verwenden

Man kann Dispatcher von WinForm UI-Thread bekommen:

 Dispatcher dispatcherUI = Dispatcher.CurrentDispatcher; 

entweder im Ereignishandler für den Klick-Klick oder irgendwo anders (im Formularkonstruktor)

Und dann verwenden Sie es, um auf der Benutzeroberfläche von anderen Threads auszuführen, finden Sie weitere Details zum Beispiel unten in meiner Antwort :

 private void button1_Click(object sender, EventArgs e) { Dispatcher dispUI = Dispatcher.CurrentDispatcher; for (int i = 2; i < 20; i++) { int j = i; var t = Task.Factory.StartNew (() => { var result = SumRootN(j); dispUI.BeginInvoke (new Action (() => richTextBox1.Text += "root " + j.ToString() + " " + result.ToString() + Environment.NewLine ) , null ); } ); } 

Verwenden Sie Hintergrund-Arbeitsthread, wie es UI-Nachrichtenpumpe bewusst ist. Dieser MSDN-Artikel, obwohl meistens über WPF, besagt, dass die BWT UI-aware auch für Windows-Formulare ist.

Ich hatte ähnliches Problem mit Oracle-Abhängigkeitsklasse, die auf einem eigenen Thread innerhalb von Winforms läuft,

Wenn das OnChange-Ereignis von Oracle Dependency ausgetriggers wurde, wollte ich die Änderungen in DataGridView anzeigen, indem ich DataSource einfach auf eventargs.Details setzte (was im Wesentlichen eine DataTable ist), und es wurde ausgetriggers : System.InvalidOperationException wurde vom Benutzercode Message = Cross-Thread-Operation nicht behandelt nicht gültig: Steuere ‘dataGridView1’, auf das von einem anderen Thread als dem Thread zugegriffen wird, auf dem es erstellt wurde.

StackOverflow-Benutzer Brian Peiris (bpeiris@gmail.com), Kollege von mir zeigte mir diesen Weg:

 void dep_OnChange(object sender, OracleNotificationEventArgs arg) { Console.WriteLine("Notification received"); int infoSum = int.Parse(arg.Details.Compute("Sum(Info)", "Info is not null").ToString()); InfoSum x = (InfoSum)infoSum; foreach (DataRow dr in arg.Details.Rows) { Console.WriteLine(string.Format("Operation(InfoSum)= {0}", Enum.GetName(typeof(InfoSum), x))); Console.WriteLine(string.Format("ontable={0} Rowid={1},info={2}", dr.Field("ResourceName"), dr.Field("rowid"), dr.Field("info"))); } // Following will throw cross-thread // dataGridView1.DataSource = arg.Details; // instead of line above use the following dataGridView1.BeginInvoke((Action)(()=>dataGridView1.DataSource = arg.Details)); IsNotified = true; } } 

Werfen Sie einen Blick auf Hintergrund und sehen Sie, ob es Ihren Bedürfnissen entspricht.

Manchmal ist eine Timer-Komponente in WinForms nützlich und einfach zu konfigurieren. Legen Sie einfach ihr Intervall fest und aktivieren Sie es dann. Stellen Sie dann sicher, dass das erste, was Sie in ihrem Tick-Event-Handler tun, sich selbst zu deaktivieren.

Ich denke, dass Timer den Code in seinem eigenen Thread ausführt, so dass Sie möglicherweise noch eine BeginInvoke (aufgerufen auf das WinForm-Objekt [this]) ausführen müssen, um Ihre Aktion auszuführen.

 private WebBrowserDocumentCompletedEventHandler handler; //need to make it a class field for the handler below (anonymous delegates seem to capture state at point of definition, so they can't capture their own reference) private string imageFilename; private bool exit; public void CaptureScreenshot(Uri address = null, string imageFilename = null, int msecDelay = 0, bool exit = false) { handler = (s, e) => { webBrowser.DocumentCompleted -= handler; //must do first this.imageFilename = imageFilename; this.exit = exit; timerScreenshot.Interval = (msecDelay > 0)? msecDelay : 1; timerScreenshot.Enabled = true; }; webBrowser.DocumentCompleted += handler; Go(address); //if address == null, will use URL from UI } private void timerScreenshot_Tick(object sender, EventArgs e) { timerScreenshot.Enabled = false; //must do first BeginInvoke((Action)(() => //Invoke at UI thread { //run in UI thread BringToFront(); Bitmap bitmap = webBrowser.GetScreenshot(); if (imageFilename == null) imageFilename = bitmap.ShowSaveFileDialog(); if (imageFilename != null) { Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(imageFilename))); //create any parent directories needed bitmap.Save(imageFilename); } bitmap.Dispose(); //release bitmap resources if (exit) Close(); //this should close the app, since this is the main form }), null); } 

Sie können das obige in Aktion am WebCapture-Tool ( http://gallery.clipflair.net/WebCapture , Quellcode unter: http://ClipFlair.codeplex.com , siehe Ordner Tools / WebCapture) sehen, das Screenshots von Websites erfasst. BTW, wenn Sie die ausführbare Datei über die Befehlszeile aufrufen möchten, gehen Sie zu den Eigenschaften des Projekts und deaktivieren Sie auf der Registerkarte Sicherheit die ClickOnce-Sicherheit (andernfalls kann sie nicht auf die Befehlszeile zugreifen)