Accesso all’interfaccia utente (principale) Thread sicuro in WPF

Ho un’applicazione che aggiorna il mio datagrid ogni volta che un file di registro che sto guardando viene aggiornato (Aggiunto con nuovo testo) nel modo seguente:

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 l’evento viene generato per FileWatcher, poiché crea un thread separato, quando provo a eseguire dataGridRows.Add (ds); per aggiungere la nuova riga, il programma si blocca appena senza alcun avviso dato durante la modalità di debug.

In Winforms, questo è stato facilmente risolto utilizzando la funzione Invoke, ma non sono sicuro su come farlo in WPF.

Puoi usare

Dispatcher.Invoke(Delegate, object[])

sul dispatcher Application (o di qualsiasi UIElement ).

Puoi usarlo per esempio in questo modo:

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

o

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

Il modo migliore per farlo sarebbe ottenere un SynchronizationContext dal thread dell’interfaccia utente e utilizzarlo. Questa class astrae le chiamate di marshalling ad altri thread e semplifica i test (a differenza dell’utilizzo del Dispatcher di WPF direttamente). Per esempio:

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

Usa [Dispatcher.Invoke (DispatcherPriority, Delegate)] per cambiare l’interfaccia utente da un altro thread o dallo sfondo.

Passaggio 1 . Utilizzare i seguenti spazi dei nomi

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

Passaggio 2 . Inserisci la riga seguente in cui è necessario aggiornare l’interfaccia utente

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

Sintassi

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

parametri

priority

Digitare: System.Windows.Threading.DispatcherPriority

La priorità, relativa alle altre operazioni in sospeso nella coda degli eventi di Dispatcher, viene richiamata il metodo specificato.

method

Tipo: System.Delegate

Un delegato a un metodo che non accetta argomenti, che viene inserito nella coda eventi di Dispatcher.

Valore di ritorno

Digitare: System.Object

Il valore di ritorno dal delegato viene invocato o null se il delegato non ha alcun valore di ritorno.

Informazioni sulla versione

Disponibile da .NET Framework 3.0