[c#]Creare un thread di lettura file

domenica 06 aprile 2008 - 21.45

program Profilo | Junior Member

Ciao,
vorrei creare un thread che duri tutto il ciclo di vita di un form.
Questo deve visualizzare quasi in "real time" il suo contenuto in una textbox...E' realizzabile?

Grazie.

rossimarko Profilo | Guru

Ciao, come mai hai questa esigenza? Devi monitorare il contenuto del file?
-----------------------------------------
Rossi Marco
http://blogs.dotnethell.it/rossimarko

program Profilo | Junior Member

Si in pratica sto cercando di realizzare un applicazione client/server.
I vari client inviano delle strighe al server che le scrive sul file...
Volevo quindi fare in modo che nella listbox o textbox del server venisse visualizzato il contenuto del file aggiornato in tempo reale.

Mi potete aiutare?
Grazie.


Per ora ho creato solo la funzione che legge i dati dal file ma non so come andare avanti...
public void CheckFile() { //cancello la listbox for (int i = lstbox.Items.Count - 1; i > 0; i--) { lstbox.Items.Remove(lstbox.Text); } //Leggo il file StreamReader sr = File.OpenText("dati.txt"); string riga = ""; do { lstbox.Items.Add(riga); riga = sr.ReadLine(); } while (riga != null); sr.Close(); }

rossimarko Profilo | Guru

Dipende dalla frequenza con cui il file viene modificato.

Se l'aggiornamento non è molto frequente puoi sfruttare la classe FileSystemWatcher (http://msdn2.microsoft.com/it-it/library/system.io.filesystemwatcher.aspx) che ti notifica un evento quando il contenuto del file viene modificato.

In alternativa puoi sfruttare l'oggetto BackgroundWorker (http://msdn2.microsoft.com/it-it/library/8xs8549b.aspx) inserendo nella funzione DoWork il codice di lettura del file. Per rendere automatica la lettura ogni XX tempo basterà inserire un Timer nella form che si occuperà di lanciare il BackgroundWorker.
-----------------------------------------
Rossi Marco
http://blogs.dotnethell.it/rossimarko

program Profilo | Junior Member

Ho provato con la seconda opzione e funziona!

Ora però volevo provare con l'altro metodo.

Ho fatto il tutto però nel metodo che richiama l'evento Changed della classe FileSystemWatcher ho questo errore:
Occorre un riferimento a un oggetto per la proprietà, il metodo o il campo non statico 'RemoteServer.Server.lstbox'

In pratica se nel metodo ci metto solo una messagebox che mi dice che il file è stato modificato tutto funziona.
Se invece ci metto il codice di lettura del file con la listbox ho l'errore di prima.
Come mai?

Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra

rossimarko Profilo | Guru

L'errore è dovuto al fatto che il tuo metodo OnChanged è stato definito static e quindi non puoi accedere agli oggetti della form.
Se lo hai definito static perchè ti dava errore quando nel metodo run agganciavi l'evento considera che anche in questo caso il problema è lo stesso. Il metodo Run è statico e non può accedere a dei metodi non statici.
Quindi ti consiglio di inserire il codice nel FileSystemWatcher in un metodo non statico della form, così puoi agganciare l'evento OnChange senza la keyword static.

Un'altra cosa da prendere in considerazione è che all'interno dell'evento OnChanged non potrai accedere direttamente agli oggetti della form perchè l'evento è gestito in un thread separato. Per ovviare a questo devi sfruttare il metodo Invoke e la proprietà InvokeRequired
http://msdn2.microsoft.com/it-it/library/system.windows.forms.control.invokerequired.aspx


Ti invio comunque una form di test che ho fatto per provare il tuo esempio:

public partial class Form1 : Form { public delegate void AddToListDelagate(string str, bool clear); FileSystemWatcher watcher = null; public Form1() { InitializeComponent(); // Create a new FileSystemWatcher and set its properties. watcher = new FileSystemWatcher(); watcher.Path = @"D:\"; watcher.Filter = "prova.txt"; watcher.Changed += new FileSystemEventHandler(OnChanged); watcher.EnableRaisingEvents = true; } private void OnChanged(object source, FileSystemEventArgs e) { //Legge il contenuto del file ReadFileContent(); } private void ReadFileContent() { //Lettura file StreamReader sr = File.OpenText(@"d:\prova.txt"); string riga = null; //Pulizia della lista if (this.InvokeRequired) this.Invoke(new AddToListDelagate(AddToList), String.Empty, true); else AddToList(riga, true); do { //Legge una riga riga = sr.ReadLine(); if (this.InvokeRequired) //Utilizza metodo invoke per accedere agli oggetti della form this.Invoke(new AddToListDelagate(AddToList), riga, false); else //Accesso diretto agli oggetti della form AddToList(riga, false); } while (riga != null); sr.Close(); } /// <summary> /// Aggiunge una stringa alla lista /// </summary> private void AddToList(string str, bool clear) { if (clear) { lstbox.Items.Clear(); return; } if (String.IsNullOrEmpty(str)) return; else lstbox.Items.Add(str); } }
-----------------------------------------
Rossi Marco
http://blogs.dotnethell.it/rossimarko

program Profilo | Junior Member

Innanzitutto grazie molte.

Volevo chiederti poi delle delucidazioni.
I Delegate per caprici sarebbero come i Template in c? Quando devo usarli precisamente in c#?

Gli Invoke invece perchè vanno utilizzati?Non capisco bene...
Ad esempio prendo un pezzo del tuo codice:

if (this.InvokeRequired) this.Invoke(new AddToListDelagate(AddToList), String.Empty, true);


Non capisco bene il suo significato...

Grazie ancora.

rossimarko Profilo | Guru

L'invoke in questo caso va usato perchè altrimenti si genera un'errore. Prova a commentare le due righe che mi hai segnalato e vedrai che richiamando direttamente il metodo si genera un errore.

Nel link che ti ho inviato c'è una nota che spiega il perchè:
"I controlli in Windows Form sono associati a un thread specifico e non sono thread-safe. Pertanto, se si chiama il metodo di un controllo da un thread diverso, è necessario utilizzare uno dei metodi Invoke del controllo per effettuare il marshalling della chiamata al thread adeguato. Questa proprietà può essere utilizzata per determinare se è necessario chiamare un metodo Invoke, che può essere utile se non si conosce il thread proprietario di un controllo"
http://msdn2.microsoft.com/it-it/library/system.windows.forms.control.invokerequired.aspx

I delegate invece definiscono la firma di un metodo:
http://msdn2.microsoft.com/it-it/library/system.delegate.aspx


-----------------------------------------
Rossi Marco
http://blogs.dotnethell.it/rossimarko

program Profilo | Junior Member

Bene teoricamente ho capito pero' mi sono impuntato su questo punto

...
Pertanto, se si chiama il metodo di un controllo da un thread diverso, è necessario utilizzare uno dei metodi Invoke del controllo per effettuare il marshalling della chiamata al thread adeguato.
...

Nel codice postato quale sarebbe il thread diverso che richiama il metodo del controllo listbox?
Non riesco a "vedere" questa cosa.

Grazie.

rossimarko Profilo | Guru

L'evento OnChanged gira su un thread diverso. Te ne accorgi perchè togliendo l'if sull'InvokeRequired ti da il seguente errore:

Cross-thread operation not valid: Control 'lstbox' accessed from a thread other than the thread it was created on.

Questo vuol dire che l'evento sta girando su un thread diverso da quello della form. In questo caso specifico quindi l'unica soluzione è l'utilizzo del metodo Invoke.

PS: Non troverai nel codice la chiamata con un thread diverso perchè è la classe FileSystemWatcher che gestisce la chiamata.
-----------------------------------------
Rossi Marco
http://blogs.dotnethell.it/rossimarko

program Profilo | Junior Member

Ok ora ho capito.
Ti Ringrazio.Gentilissimo.

Ciao!
Partecipa anche tu! Registrati!
Hai bisogno di aiuto ?
Perchè non ti registri subito?

Dopo esserti registrato potrai chiedere
aiuto sul nostro Forum oppure aiutare gli altri

Consulta le Stanze disponibili.

Registrati ora !
Copyright © dotNetHell.it 2002-2024
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5