Acessando o thread da interface do usuário (principal) com segurança no WPF

Eu tenho um aplicativo que atualiza meu datagrid cada vez que um arquivo de log que estou assistindo é atualizado (acrescentado com novo texto) da seguinte maneira:

private void DGAddRow(string name, FunctionType ft) { ASCIIEncoding ascii = new ASCIIEncoding(); CommDGDataSource ds = new CommDGDataSource(); int position = 0; string[] data_split = ft.Data.Split(' '); foreach (AttributeType at in ft.Types) { if (at.IsAddress) { ds.Source = HexString2Ascii(data_split[position]); ds.Destination = HexString2Ascii(data_split[position+1]); break; } else { position += at.Size; } } ds.Protocol = name; ds.Number = rowCount; ds.Data = ft.Data; ds.Time = ft.Time; dataGridRows.Add(ds); rowCount++; } ... private void FileSystemWatcher() { FileSystemWatcher watcher = new FileSystemWatcher(Environment.CurrentDirectory); watcher.Filter = syslogPath; watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; watcher.Changed += new FileSystemEventHandler(watcher_Changed); watcher.EnableRaisingEvents = true; } private void watcher_Changed(object sender, FileSystemEventArgs e) { if (File.Exists(syslogPath)) { string line = GetLine(syslogPath,currentLine); foreach (CommRuleParser crp in crpList) { FunctionType ft = new FunctionType(); if (crp.ParseLine(line, out ft)) { DGAddRow(crp.Protocol, ft); } } currentLine++; } else MessageBox.Show(UIConstant.COMM_SYSLOG_NON_EXIST_WARNING); } 

Quando o evento é gerado para o FileWatcher, porque ele cria um thread separado, quando tento executar o dataGridRows.Add (ds); para adicionar a nova linha, o programa simplesmente falha sem nenhum aviso durante o modo de debugging.

No WinForms, isso foi facilmente resolvido utilizando a function Invoke, mas não tenho certeza de como fazer isso no WPF.

   

Você pode usar

Dispatcher.Invoke(Delegate, object[])

no dispatcher do Application (ou de qualquer UIElement ).

Você pode usá-lo por exemplo como este:

 Application.Current.Dispatcher.Invoke(new Action(() => { /* Your code here */ })); 

ou

 someControl.Dispatcher.Invoke(new Action(() => { /* Your code here */ })); 

A melhor maneira de fazer isso seria obter um SynchronizationContext do thread da interface do usuário e usá-lo. Essa class abstrai o marshalling de chamadas para outros threads e torna o teste mais fácil (em contraste com o uso do Dispatcher do WPF diretamente). Por exemplo:

 class MyViewModel { private readonly SynchronizationContext _syncContext; public MyViewModel() { // we assume this ctor is called from the UI thread! _syncContext = SynchronizationContext.Current; } // ... private void watcher_Changed(object sender, FileSystemEventArgs e) { _syncContext.Post(o => DGAddRow(crp.Protocol, ft), null); } } 

Use [Dispatcher.Invoke (DispatcherPriority, Delegate)] para alterar a interface do usuário de outro segmento ou do plano de fundo.

1º passo Use os namespaces a seguir

 using System.Windows; using System.Threading; using System.Windows.Threading; 

Etapa 2 Coloque a seguinte linha onde você precisa atualizar a interface do usuário

 Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate { //Update UI here })); 

Sintaxe

 [BrowsableAttribute(false)] public object Invoke( DispatcherPriority priority, Delegate method ) 

parameters

priority

Tipo: System.Windows.Threading.DispatcherPriority

A prioridade, relativa às outras operações pendentes na fila de events do Dispatcher, é chamada pelo método especificado.

method

Tipo: System.Delegate

Um delegado para um método que não recebe argumentos, que é colocado na fila de events do Dispatcher.

Valor de retorno

Tipo: System.Object

O valor de retorno do delegado sendo chamado ou nulo se o delegado não tiver valor de retorno.

Versão informação

Disponível desde o .NET Framework 3.0