System.Windows.Threading.Dispatcher e WinForms?

Um System.Windows.Threading.Dispatcher funciona no thread de interface do usuário de um aplicativo WinForms ?

Se sim, porque? Ele está vindo de WindowsBase.dll, que parece ser um componente do WPF .

Se não, como posso invocar unidades de trabalho de volta para o thread de interface do usuário? Eu encontrei Control.BeginInvoke() , mas parece desajeitado para criar um controle somente para fazer referência ao segmento de origem.

Você pode usar o Dispatcher mesmo em um aplicativo WinForms.

Se você tiver certeza de estar em um thread de interface do usuário (por exemplo, em um manipulador button.Click), Dispatcher.CurrentDispatcher fornece o dispatcher de thread da interface do usuário que você pode usar posteriormente para despachar de segmentos de plano de fundo para o thread da interface do usuário como de costume.

Dispatcher é um componente do WPF, não um componente do WinForms.

Se você deseja despachar itens de trabalho no thread da interface do usuário, você deve usar Control.BeginInvoke como você já encontrou ou reagir a ResetEvents / WaitObjects entre threads.

Normalmente invocar itens de trabalho no thread de interface do usuário é uma coisa ruim, a menos que seja um trabalho de interface do usuário (ou seja, atualizar o conteúdo de um controle ou algo assim), caso em que o Control.BeginInvoke () seria suficiente.

Eu forneci um exemplo de usar System.Windows.Threading.Dispatcher no Windows Form na minha resposta à pergunta “Programação Paralela usando TPL em WinForms” desde a resposta anterior à sua pergunta :

Se você tiver certeza de estar no thread de interface do usuário (por exemplo, em um manipulador button.Click), Dispatcher.CurrentDispatcher fornece o dispatcher de thread da interface do usuário que você pode usar para despachar do thread de segundo plano para thread de interface do usuário como de costume.

é enganosa ou confusa ou não tem o contexto de uso concreto:

  • manipulador button.Click não garante estar no thread de interface do usuário;
  • se você não estiver no thread de interface do usuário, ainda é possível usar o disparcher do thread de interface do usuário do formulário WinForms

Pode-se obter dispatcher de thread de interface do usuário WinForm:

 Dispatcher dispatcherUI = Dispatcher.CurrentDispatcher; 

em qualquer manipulador de events de clique de botão ou em qualquer outro lugar (no construtor de formulário)

E, em seguida, usá-lo para executar na interface do usuário de outros segmentos, veja mais detalhes no exemplo abaixo na minha resposta :

 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 ); } ); } 

Use thread de trabalho de plano de fundo como é consciente de bomba de mensagem de interface do usuário, este artigo MSDN embora principalmente sobre o WPF afirma que o BWT está ciente da interface do usuário mesmo para formulários do windows.

Eu tive problema semelhante usando a class de dependência Oracle, que é executado em seu próprio segmento dentro do WinForms,

Quando o evento OnChange disparou do Oracle Dependency, eu queria mostrar as alterações no DataGridView simplesmente definindo DataSource para eventargs.Details (que é essencialmente um DataTable), e lança: System.InvalidOperationException foi manipulado pelo código do usuário Message = Operação de thread cruzado não é válido: controle ‘dataGridView1’ acessado de um segmento diferente do segmento em que foi criado.

O usuário do StackOverflow Brian Peiris (bpeiris@gmail.com), colega meu, me mostrou o caminho a seguir:

 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; } } 

Dê uma olhada no backgrounder e veja se ele atende às suas necessidades.

Às vezes, um componente Timer é útil e fácil de configurar no WinForms, basta definir seu intervalo e, em seguida, ativá-lo, em seguida, verifique se a primeira coisa que você faz no seu manipulador de events Tick é desabilitar a si mesmo.

Acho Timer executa o código em seu próprio segmento, então você ainda pode precisar fazer um BeginInvoke (chamado no object WinForm [this]) para executar sua ação.

 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); } 

você pode ver o exemplo acima em ação na ferramenta WebCapture ( http://gallery.clipflair.net/WebCapture , código-fonte em: http://ClipFlair.codeplex.com , consulte a pasta Tools / WebCapture) que captura capturas de canvas dos sites. BTW, se você quiser chamar o executável a partir da linha de comando, certifique-se de ir para Propriedades do projeto e, na guia Segurança, desativar a segurança do ClickOnce (caso contrário, ele não poderá acessar a linha de comando)